use luau_common::ByteSlice;
use crate::call::ThreadStack;
use crate::gc::{GcBarrier, GcRuntime};
use crate::metamethod::MetamethodRuntime;
use crate::number::{
LUAI_MAXINT2STR, LUAI_MAXNUM2STR, clamp_f, int_to_str, lerp_f, num_to_str, sign_f,
};
use crate::state::ThreadState;
use crate::string::StringRuntime;
use crate::table::TableRuntime;
use crate::thread::Thread;
use crate::value::{TValue, TValueCursor};
use core::mem::MaybeUninit;
use core::ptr;
use crate::Table;
use crate::buffer::Buffer;
use crate::gc::GcObject;
use crate::handle::RawHandle;
use crate::metamethod::TmEvent;
use crate::number::str_to_num;
use crate::types::{LUA_TNIL, LUA_TTABLE, LUA_TUSERDATA};
pub type LuauFastFunction = unsafe fn(&Thread, TValueCursor, TValue, i32, TValueCursor, i32) -> i32;
unsafe fn missing(
_thread: &Thread,
_result: TValueCursor,
_arg0: TValue,
_nresults: i32,
_args: TValueCursor,
_nparams: i32,
) -> i32 {
-1
}
unsafe fn assert(
_thread: &Thread,
_result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults == 0 && !arg0.is_false() {
0
} else {
-1
}
}
unsafe fn type_(
thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 {
unsafe {
result
.value_unchecked()
.set_string_value(thread.global().type_name(arg0.tt() as usize))
};
1
} else {
-1
}
}
unsafe fn len(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_string() {
unsafe { result.value_unchecked() }.set_number(unsafe {
arg0.string_value().as_ptr().as_ref().unwrap_unchecked().len as f64
});
return 1;
}
-1
}
unsafe fn type_of(
thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 {
unsafe { result.value_unchecked() }
.set_string_value(unsafe { thread.obj_type_name_str(arg0) });
1
} else {
-1
}
}
unsafe fn select(
thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams == 1 && nresults == 1 {
let (base, n, hash_selector) = unsafe {
let ci = thread.current_call_info();
let func = ci.function();
let proto = ci.proto().unwrap_unchecked();
let base = thread.stack_base();
let n = base.offset_from(func) as i32
- proto.as_ptr().as_ref().unwrap_unchecked().num_params as i32
- 1;
let hash_selector =
arg0.is_string() && arg0.string_value().as_bytes().first() == Some(&b'#');
(base, n, hash_selector)
};
if arg0.is_number() {
let i = arg0.number_value() as i32;
if ((i - 1) as u32) < (n as u32) {
unsafe { result.value_unchecked() }.set_obj(unsafe {
base.sub(n as usize).add((i - 1) as usize).value_unchecked()
});
return 1;
}
} else if hash_selector {
unsafe { result.value_unchecked() }.set_number(n as f64);
return 1;
}
}
-1
}
unsafe fn to_number(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams == 1 && nresults <= 1 {
if arg0.is_number() {
unsafe { result.value_unchecked() }.set_number(arg0.number_value());
} else if arg0.is_string() {
let string = arg0.string_value();
if let Some(value) = str_to_num(unsafe { string.as_bytes() }) {
unsafe { result.value_unchecked() }.set_number(value);
} else {
unsafe { result.value_unchecked() }.set_nil();
}
} else {
unsafe { result.value_unchecked() }.set_nil();
}
1
} else {
-1
}
}
unsafe fn to_string(
thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams < 1 || nresults > 1 {
return -1;
}
unsafe {
if arg0.is_nil() {
result
.value_unchecked()
.set_string_value(thread.global().type_name(LUA_TNIL as usize));
} else if arg0.is_boolean() {
let bytes: &[u8] = if arg0.boolean_value() != 0 {
b"true"
} else {
b"false"
};
let Ok(s) = thread.intern_string(bytes.as_bstr()) else {
return -1;
};
result.value_unchecked().set_string_value(s);
} else if arg0.is_number() {
if thread.needs_gc() {
return -1;
}
let mut buffer = [0u8; LUAI_MAXNUM2STR];
let len = num_to_str(&mut buffer, arg0.number_value());
let Ok(s) = thread.intern_string(buffer[..len].as_bstr()) else {
return -1;
};
result.value_unchecked().set_string_value(s);
} else if arg0.is_string() {
result
.value_unchecked()
.set_string_value(arg0.string_value());
} else if arg0.is_integer() {
if thread.needs_gc() {
return -1;
}
let mut buffer = [0u8; LUAI_MAXINT2STR];
let len = int_to_str(&mut buffer, arg0.integer_value());
let Ok(s) = thread.intern_string(buffer[..len].as_bstr()) else {
return -1;
};
result.value_unchecked().set_string_value(s);
} else {
return -1;
}
1
}
}
macro_rules! unary_num {
($doc:literal, $name:ident, $op:expr) => {
#[doc = $doc]
unsafe fn $name(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_number() {
unsafe { result.value_unchecked() }.set_number(($op)(arg0.number_value()));
1
} else {
-1
}
}
};
}
macro_rules! binary_num {
($doc:literal, $name:ident, $op:expr) => {
#[doc = $doc]
unsafe fn $name(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
{
unsafe { result.value_unchecked() }.set_number(($op)(
arg0.number_value(),
unsafe { args.value_unchecked() }.number_value(),
));
1
} else {
-1
}
}
};
}
unary_num!("`luauF_abs`", abs, f64::abs);
unary_num!("`luauF_acos`", acos, f64::acos);
unary_num!("`luauF_asin`", asin, f64::asin);
binary_num!("`luauF_atan2`", atan2, f64::atan2);
unary_num!("`luauF_atan`", atan, f64::atan);
unary_num!("`luauF_ceil`", ceil, f64::ceil);
unary_num!("`luauF_cosh`", cosh, f64::cosh);
unary_num!("`luauF_cos`", cos, f64::cos);
unary_num!("`luauF_exp`", exp, f64::exp);
unary_num!("`luauF_floor`", floor, f64::floor);
binary_num!("`luauF_fmod`", fmod, |a: f64, b: f64| a % b);
binary_num!("`luauF_pow`", pow, f64::powf);
unary_num!("`luauF_sinh`", sinh, f64::sinh);
unary_num!("`luauF_sin`", sin, f64::sin);
unary_num!("`luauF_sqrt`", sqrt, f64::sqrt);
unary_num!("`luauF_tanh`", tanh, f64::tanh);
unary_num!("`luauF_tan`", tan, f64::tan);
unsafe fn deg(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_number() {
let radians_per_degree = std::f64::consts::PI / 180.0;
unsafe { result.value_unchecked() }.set_number(arg0.number_value() / radians_per_degree);
1
} else {
-1
}
}
unsafe fn frexp(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 2 && arg0.is_number() {
let value = arg0.number_value();
if value == 0.0 {
unsafe { result.value_unchecked() }.set_number(0.0);
unsafe { result.add(1).value_unchecked() }.set_number(0.0);
return 2;
}
let abs = value.abs();
let exponent = abs.log2().floor() as i32 + 1;
let fraction = value / 2.0f64.powi(exponent);
unsafe { result.value_unchecked() }.set_number(fraction);
unsafe { result.add(1).value_unchecked() }.set_number(exponent as f64);
2
} else {
-1
}
}
unsafe fn ldexp(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
{
unsafe { result.value_unchecked() }.set_number(
arg0.number_value()
* 2.0f64.powi(unsafe { args.value_unchecked() }.number_value() as i32),
);
1
} else {
-1
}
}
unsafe fn log10(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_number() {
unsafe { result.value_unchecked() }.set_number(arg0.number_value().log10());
1
} else {
-1
}
}
unsafe fn log(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_number() {
let a1 = arg0.number_value();
if nparams == 1 {
unsafe { result.value_unchecked() }.set_number(a1.ln());
return 1;
} else if unsafe { args.value_unchecked() }.is_number() {
let base = unsafe { args.value_unchecked() }.number_value();
let value = if base == 2.0 {
a1.log2()
} else if base == 10.0 {
a1.log10()
} else {
a1.ln() / base.ln()
};
unsafe { result.value_unchecked() }.set_number(value);
return 1;
}
}
-1
}
unsafe fn max(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
{
let a1 = arg0.number_value();
let a2 = unsafe { args.value_unchecked() }.number_value();
let mut r = if a2 > a1 { a2 } else { a1 };
for index in 3..=nparams {
let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
if !arg.is_number() {
return -1;
}
let a = arg.number_value();
r = if a > r { a } else { r };
}
unsafe { result.value_unchecked() }.set_number(r);
1
} else {
-1
}
}
unsafe fn min(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
{
let a1 = arg0.number_value();
let a2 = unsafe { args.value_unchecked() }.number_value();
let mut r = if a2 < a1 { a2 } else { a1 };
for index in 3..=nparams {
let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
if !arg.is_number() {
return -1;
}
let a = arg.number_value();
r = if a < r { a } else { r };
}
unsafe { result.value_unchecked() }.set_number(r);
1
} else {
-1
}
}
unsafe fn modf(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 2 && arg0.is_number() {
let a1 = arg0.number_value();
let ip = a1.trunc();
let fp = a1 - ip;
unsafe { result.value_unchecked() }.set_number(ip);
unsafe { result.add(1).value_unchecked() }.set_number(fp);
2
} else {
-1
}
}
unsafe fn rad(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_number() {
let radians_per_degree = std::f64::consts::PI / 180.0;
unsafe { result.value_unchecked() }.set_number(arg0.number_value() * radians_per_degree);
1
} else {
-1
}
}
unsafe fn clamp(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 3
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
&& unsafe { args.add(1).value_unchecked().is_number() }
{
let value = arg0.number_value();
let min = unsafe { args.value_unchecked() }.number_value();
let max = unsafe { args.add(1).value_unchecked().number_value() };
if min <= max {
let value = if value < min { min } else { value };
unsafe { result.value_unchecked() }.set_number(if value > max { max } else { value });
return 1;
}
}
-1
}
unsafe fn sign(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_number() {
let value = arg0.number_value();
unsafe { result.value_unchecked() }.set_number(if value > 0.0 {
1.0
} else if value < 0.0 {
-1.0
} else {
0.0
});
return 1;
}
-1
}
unsafe fn round(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_number() {
unsafe { result.value_unchecked() }.set_number(arg0.number_value().round());
return 1;
}
-1
}
unsafe fn is_nan(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_number() {
unsafe { result.value_unchecked() }.set_boolean(i32::from(arg0.number_value().is_nan()));
return 1;
}
-1
}
unsafe fn is_inf(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_number() {
unsafe { result.value_unchecked() }
.set_boolean(i32::from(arg0.number_value().is_infinite()));
return 1;
}
-1
}
unsafe fn is_finite(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_number() {
unsafe { result.value_unchecked() }.set_boolean(i32::from(arg0.number_value().is_finite()));
return 1;
}
-1
}
unsafe fn ar_shift(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
{
let u = (arg0.number_value() as i64) as u32;
let s = unsafe { args.value_unchecked() }.number_value() as i32;
if (s as u32) < 32 {
unsafe { result.value_unchecked() }.set_number(((u as i32) >> s) as u32 as f64);
return 1;
}
}
-1
}
unsafe fn band(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
{
let mut r = ((arg0.number_value() as i64) as u32)
& ((unsafe { args.value_unchecked() }.number_value() as i64) as u32);
for index in 3..=nparams {
let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
if !arg.is_number() {
return -1;
}
r &= (arg.number_value() as i64) as u32;
}
unsafe { result.value_unchecked() }.set_number(r as f64);
1
} else {
-1
}
}
unsafe fn bnot(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_number() {
unsafe { result.value_unchecked() }
.set_number((!((arg0.number_value() as i64) as u32)) as f64);
1
} else {
-1
}
}
unsafe fn bor(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
{
let mut r = ((arg0.number_value() as i64) as u32)
| ((unsafe { args.value_unchecked() }.number_value() as i64) as u32);
for index in 3..=nparams {
let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
if !arg.is_number() {
return -1;
}
r |= (arg.number_value() as i64) as u32;
}
unsafe { result.value_unchecked() }.set_number(r as f64);
1
} else {
-1
}
}
unsafe fn bxor(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
{
let mut r = ((arg0.number_value() as i64) as u32)
^ ((unsafe { args.value_unchecked() }.number_value() as i64) as u32);
for index in 3..=nparams {
let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
if !arg.is_number() {
return -1;
}
r ^= (arg.number_value() as i64) as u32;
}
unsafe { result.value_unchecked() }.set_number(r as f64);
1
} else {
-1
}
}
unsafe fn btest(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
{
let mut r = ((arg0.number_value() as i64) as u32)
& ((unsafe { args.value_unchecked() }.number_value() as i64) as u32);
for index in 3..=nparams {
let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
if !arg.is_number() {
return -1;
}
r &= (arg.number_value() as i64) as u32;
}
unsafe { result.value_unchecked() }.set_boolean(i32::from(r != 0));
1
} else {
-1
}
}
unsafe fn count_lz(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_number() {
let n = (arg0.number_value() as i64) as u32;
let r = if n == 0 { 32 } else { n.leading_zeros() };
unsafe { result.value_unchecked() }.set_number(r as f64);
1
} else {
-1
}
}
unsafe fn count_rz(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_number() {
let n = (arg0.number_value() as i64) as u32;
let r = if n == 0 { 32 } else { n.trailing_zeros() };
unsafe { result.value_unchecked() }.set_number(r as f64);
1
} else {
-1
}
}
unsafe fn byte_swap(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_number() {
unsafe { result.value_unchecked() }
.set_number(((arg0.number_value() as i64) as u32).swap_bytes() as f64);
1
} else {
-1
}
}
unsafe fn extract(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
{
let n = (arg0.number_value() as i64) as u32;
let field = unsafe { args.value_unchecked() }.number_value() as i32;
if nparams == 2 {
if (field as u32) < 32 {
unsafe { result.value_unchecked() }.set_number(((n >> field) & 1) as f64);
return 1;
}
} else if unsafe { args.add(1).value_unchecked() }.is_number() {
let width = unsafe { args.add(1).value_unchecked().number_value() } as i32;
if field >= 0 && width > 0 && field + width <= 32 {
let mask = !(0xfffffffe_u32 << (width - 1));
unsafe { result.value_unchecked() }.set_number(((n >> field) & mask) as f64);
return 1;
}
}
}
-1
}
unsafe fn rotate_left(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
{
let value = (arg0.number_value() as i64) as u32;
let shift = unsafe { args.value_unchecked() }.number_value() as i32;
unsafe { result.value_unchecked() }
.set_number(value.rotate_left((shift & 31) as u32) as f64);
return 1;
}
-1
}
unsafe fn shift_left(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
{
let value = (arg0.number_value() as i64) as u32;
let shift = unsafe { args.value_unchecked() }.number_value() as i32;
if (shift as u32) < 32 {
unsafe { result.value_unchecked() }.set_number((value << shift) as f64);
return 1;
}
}
-1
}
unsafe fn replace(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
unsafe {
let field_arg = args.add(1).value_unchecked();
if nparams >= 3
&& nresults <= 1
&& arg0.is_number()
&& args.value_unchecked().is_number()
&& field_arg.is_number()
{
let number = (arg0.number_value() as i64) as u32;
let value = (args.value_unchecked().number_value() as i64) as u32;
let field = field_arg.number_value() as i32;
if nparams == 3 {
if (field as u32) < 32 {
let mask = 1u32;
let replaced = (number & !(mask << field)) | ((value & mask) << field);
result.value_unchecked().set_number(replaced as f64);
return 1;
}
} else {
let width_arg = args.add(2).value_unchecked();
if width_arg.is_number() {
let width = width_arg.number_value() as i32;
if field >= 0 && width > 0 && field + width <= 32 {
let mask = !(0xfffffffe_u32 << (width - 1));
let replaced = (number & !(mask << field)) | ((value & mask) << field);
result.value_unchecked().set_number(replaced as f64);
return 1;
}
}
}
}
}
-1
}
unsafe fn r_rotate(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
{
let value = (arg0.number_value() as i64) as u32;
let shift = unsafe { args.value_unchecked() }.number_value() as i32;
unsafe { result.value_unchecked() }
.set_number(value.rotate_right((shift & 31) as u32) as f64);
return 1;
}
-1
}
unsafe fn r_shift(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
{
let value = (arg0.number_value() as i64) as u32;
let shift = unsafe { args.value_unchecked() }.number_value() as i32;
if (shift as u32) < 32 {
unsafe { result.value_unchecked() }.set_number((value >> shift) as f64);
return 1;
}
}
-1
}
unsafe fn extract_k(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
{
let number = (arg0.number_value() as i64) as u32;
let packed = unsafe { args.value_unchecked() }.number_value() as i32;
let field = packed & 31;
let width_minus_one = packed >> 5;
let mask = !(0xfffffffe_u32 << width_minus_one);
unsafe { result.value_unchecked() }.set_number(((number >> field) & mask) as f64);
return 1;
}
-1
}
unsafe fn byte(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2 && arg0.is_string() && unsafe { args.value_unchecked() }.is_number() {
unsafe {
let string = arg0.string_value();
let start = args.value_unchecked().number_value() as i32;
let end = if nparams >= 3 {
if !args.add(1).value_unchecked().is_number() {
return -1;
}
args.add(1).value_unchecked().number_value() as i32
} else {
start
};
let len = string.as_ptr().as_ref().unwrap_unchecked().len as i32;
if start >= 1 && end >= start && end <= len {
let count = end - start + 1;
let expected = if nresults < 0 { 1 } else { nresults };
if count == expected {
let bytes = string.as_bytes();
for index in 0..count as usize {
result
.add(index)
.value_unchecked()
.set_number(bytes[start as usize + index - 1] as f64);
}
return count;
}
}
}
}
-1
}
unsafe fn char(
thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
let mut buffer = [0u8; 8];
if nparams < buffer.len() as i32 && nresults <= 1 {
unsafe {
if thread.needs_gc() {
return -1;
}
if nparams >= 1 {
if !arg0.is_number() {
return -1;
}
let ch = arg0.number_value() as i32;
if (ch as u8) as i32 != ch {
return -1;
}
buffer[0] = ch as u8;
}
for index in 2..=nparams {
let arg = args.add((index - 2) as usize);
if !arg.value_unchecked().is_number() {
return -1;
}
let ch = arg.value_unchecked().number_value() as i32;
if (ch as u8) as i32 != ch {
return -1;
}
buffer[index as usize - 1] = ch as u8;
}
buffer[nparams as usize] = 0;
let Ok(string) = thread.intern_string(buffer[..nparams as usize].as_bstr()) else {
return -1;
};
result.value_unchecked().set_string_value(string);
}
return 1;
}
-1
}
unsafe fn sub(
thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
unsafe {
if nparams >= 3
&& nresults <= 1
&& arg0.is_string()
&& args.value_unchecked().is_number()
&& args.add(1).value_unchecked().is_number()
{
if thread.needs_gc() {
return -1;
}
let string = arg0.string_value();
let start = args.value_unchecked().number_value() as i32;
let end = args.add(1).value_unchecked().number_value() as i32;
let len = string.as_ptr().as_ref().unwrap_unchecked().len as i32;
if start >= 1 && end >= start && ((end - 1) as u32) < (len as u32) {
let bytes = string.as_bytes();
let Ok(slice) =
thread.intern_string(bytes[(start - 1) as usize..end as usize].as_bstr())
else {
return -1;
};
result.value_unchecked().set_string_value(slice);
return 1;
}
}
-1
}
}
unsafe fn raw_equal(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2 && nresults <= 1 {
unsafe { result.value_unchecked() }
.set_boolean(i32::from(arg0.raw_equal(unsafe { args.value_unchecked() })));
1
} else {
-1
}
}
unsafe fn raw_get(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2 && nresults <= 1 && arg0.is_table() {
unsafe { result.value_unchecked() }
.set_obj(unsafe { arg0.table_value().get(args.value_unchecked()) });
1
} else {
-1
}
}
unsafe fn raw_set(
thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 3 && nresults <= 1 && arg0.is_table() {
unsafe {
let key = args;
let key_value = key.value_unchecked();
if key_value.is_nil()
|| (key_value.is_number() && key_value.number_value().is_nan())
|| (key_value.is_vector() && key_value.vector_value().iter().any(|v| v.is_nan()))
{
return -1;
}
let table = arg0.table_value();
if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
return -1;
}
result.value_unchecked().set_obj(arg0);
let Ok(slot) = thread.set(table, key_value) else {
return -1;
};
let value = args.add(1);
slot.set_obj(value.value_unchecked());
if value.value_unchecked().is_collectable() {
let child = value.value_unchecked().gc_value();
let table_object: GcObject = table.into();
if table_object.is_black() && child.is_white() {
thread.barrier_table(table, child);
}
}
1
}
} else {
-1
}
}
unsafe fn t_insert(
thread: &Thread,
_result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams == 2 && nresults <= 0 && arg0.is_table() {
unsafe {
let table = arg0.table_value();
if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
return -1;
}
let pos = table.getn() + 1;
let slot = if let Some(slot) = table.array_slot_for_key(pos) {
slot
} else {
let Ok(slot) = thread.set_num(table, pos) else {
return -1;
};
slot
};
let value = args.value_unchecked();
slot.set_obj(value);
if value.is_collectable() {
let child = value.gc_value();
let table_object: GcObject = table.into();
if table_object.is_black() && child.is_white() {
thread.barrier_table(table, child);
}
}
0
}
} else {
-1
}
}
unsafe fn t_unpack(
thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults < 0 && arg0.is_table() {
unsafe {
let table = arg0.table_value();
let mut n = -1;
if nparams == 1 {
n = table.getn();
} else if nparams == 3
&& args.value_unchecked().is_number()
&& args.add(1).value_unchecked().is_number()
&& args.value_unchecked().number_value() == 1.0
{
n = args.add(1).value_unchecked().number_value() as i32;
}
let stack_last = thread.stack_last();
let stack_room = stack_last.offset_from(result) as i32;
if n >= 0
&& n <= table.as_ptr().as_ref().unwrap_unchecked().size_array
&& stack_room >= n
&& n + nparams <= 8000
{
let array = table.array_cursor();
for index in 0..n as usize {
result
.add(index)
.value_unchecked()
.set_obj(array.add(index).value_unchecked());
}
thread.expand_stack_limit(result.add(n as usize));
return n;
}
}
}
-1
}
unsafe fn raw_len(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 {
if arg0.is_table() {
unsafe { result.value_unchecked() }
.set_number(unsafe { arg0.table_value().getn() as f64 });
return 1;
} else if arg0.is_string() {
unsafe { result.value_unchecked() }.set_number(unsafe {
arg0.string_value().as_ptr().as_ref().unwrap_unchecked().len as f64
});
return 1;
}
}
-1
}
unsafe fn get_metatable(
thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 {
let (mt, mtv) = unsafe {
match arg0.tt() {
x if x == LUA_TTABLE => arg0.table_value().metatable(),
x if x == LUA_TUSERDATA => arg0.userdata_value().metatable(),
tag => thread.global().metatable(tag as usize),
}
.map_or_else(
|| (None, crate::value::nil_object()),
|mt: Table| {
let name = thread.global().tm_name(TmEvent::MetaTable as usize);
(Some(mt), mt.get_str(name))
},
)
};
if !mtv.is_nil() {
unsafe { result.value_unchecked() }.set_obj(mtv);
} else if let Some(mt) = mt {
unsafe { result.value_unchecked() }.set_table_value(mt);
} else {
unsafe { result.value_unchecked() }.set_nil();
}
return 1;
}
-1
}
unsafe fn set_metatable(
thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
unsafe {
if nparams >= 2 && nresults <= 1 && arg0.is_table() && args.value_unchecked().is_table() {
let table = arg0.table_value();
if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0
|| table.metatable().is_some()
{
return -1;
}
let mt = args.value_unchecked().table_value();
table.set_metatable(Some(mt));
let object: GcObject = table.into();
let child: GcObject = mt.into();
if object.is_black() && child.is_white() {
thread.barrier_forward(object, child);
}
result.value_unchecked().set_table_value(table);
return 1;
}
}
-1
}
unsafe fn set_vector(result: TValueCursor, x: f32, y: f32, z: f32, w: f32) {
#[cfg(feature = "vector4")]
unsafe { result.value_unchecked() }.set_vector([x, y, z, w]);
#[cfg(not(feature = "vector4"))]
let _ = w;
#[cfg(not(feature = "vector4"))]
unsafe { result.value_unchecked() }.set_vector([x, y, z]);
}
unsafe fn vector(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
unsafe {
if nparams >= 2 && nresults <= 1 && arg0.is_number() && args.value_unchecked().is_number() {
let x = arg0.number_value() as f32;
let y = args.value_unchecked().number_value() as f32;
let mut z = 0.0f32;
if nparams >= 3 {
if !args.add(1).value_unchecked().is_number() {
return -1;
}
z = args.add(1).value_unchecked().number_value() as f32;
}
#[cfg(feature = "vector4")]
{
let mut w = 0.0f32;
if nparams >= 4 {
if !args.add(2).value_unchecked().is_number() {
return -1;
}
w = args.add(2).value_unchecked().number_value() as f32;
}
set_vector(result, x, y, z, w);
}
#[cfg(not(feature = "vector4"))]
{
set_vector(result, x, y, z, 0.0);
}
1
} else {
-1
}
}
}
unsafe fn vector_magnitude(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_vector() {
let vector = arg0.vector_value();
let mut sum = 0.0f32;
for value in vector {
sum += value * value;
}
unsafe { result.value_unchecked() }.set_number(sum.sqrt() as f64);
return 1;
}
-1
}
unsafe fn vector_normalize(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_vector() {
let vector = arg0.vector_value();
let mut sum = 0.0f32;
for value in vector {
sum += value * value;
}
let inv_sqrt = 1.0f32 / sum.sqrt();
#[cfg(feature = "vector4")]
unsafe {
set_vector(
result,
vector[0] * inv_sqrt,
vector[1] * inv_sqrt,
vector[2] * inv_sqrt,
vector[3] * inv_sqrt,
);
}
#[cfg(not(feature = "vector4"))]
unsafe {
set_vector(
result,
vector[0] * inv_sqrt,
vector[1] * inv_sqrt,
vector[2] * inv_sqrt,
0.0,
);
}
return 1;
}
-1
}
unsafe fn vector_cross(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_vector()
&& unsafe { args.value_unchecked() }.is_vector()
{
let a = arg0.vector_value();
let b = unsafe { args.value_unchecked() }.vector_value();
unsafe {
set_vector(
result,
a[1] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0],
0.0,
);
}
return 1;
}
-1
}
unsafe fn vector_dot(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_vector()
&& unsafe { args.value_unchecked() }.is_vector()
{
let a = arg0.vector_value();
let b = unsafe { args.value_unchecked() }.vector_value();
let mut dot = 0.0f32;
for (left, right) in a.iter().zip(b.iter()) {
dot += left * right;
}
unsafe { result.value_unchecked() }.set_number(dot as f64);
return 1;
}
-1
}
macro_rules! unary_vector {
($doc:literal, $name:ident, $op:expr) => {
#[doc = $doc]
unsafe fn $name(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_vector() {
let vector = arg0.vector_value();
#[cfg(feature = "vector4")]
unsafe {
set_vector(
result,
($op)(vector[0]),
($op)(vector[1]),
($op)(vector[2]),
($op)(vector[3]),
);
}
#[cfg(not(feature = "vector4"))]
unsafe {
set_vector(
result,
($op)(vector[0]),
($op)(vector[1]),
($op)(vector[2]),
0.0,
);
}
return 1;
}
-1
}
};
}
unary_vector!("`luauF_vectorfloor`", vector_floor, f32::floor);
unary_vector!("`luauF_vectorceil`", vector_ceil, f32::ceil);
unary_vector!("`luauF_vectorabs`", vector_abs, f32::abs);
unary_vector!("`luauF_vectorsign`", vector_sign, sign_f);
unsafe fn vector_clamp(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
unsafe {
let max_arg = args.add(1).value_unchecked();
if nparams >= 3
&& nresults <= 1
&& arg0.is_vector()
&& args.value_unchecked().is_vector()
&& max_arg.is_vector()
{
let value = arg0.vector_value();
let min = args.value_unchecked().vector_value();
let max = max_arg.vector_value();
if min[0] <= max[0] && min[1] <= max[1] && min[2] <= max[2] {
#[cfg(feature = "vector4")]
set_vector(
result,
clamp_f(value[0], min[0], max[0]),
clamp_f(value[1], min[1], max[1]),
clamp_f(value[2], min[2], max[2]),
clamp_f(value[3], min[3], max[3]),
);
#[cfg(not(feature = "vector4"))]
set_vector(
result,
clamp_f(value[0], min[0], max[0]),
clamp_f(value[1], min[1], max[1]),
clamp_f(value[2], min[2], max[2]),
0.0,
);
return 1;
}
}
}
-1
}
unsafe fn vector_min(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
unsafe {
if nparams >= 2 && nresults <= 1 && arg0.is_vector() && args.value_unchecked().is_vector() {
let mut value = arg0.vector_value();
let other = args.value_unchecked().vector_value();
for (slot, candidate) in value.iter_mut().zip(other.iter()) {
if *candidate < *slot {
*slot = *candidate;
}
}
for index in 3..=nparams {
let arg = args.add((index - 2) as usize).value_unchecked();
if !arg.is_vector() {
return -1;
}
let candidate = arg.vector_value();
for (slot, candidate) in value.iter_mut().zip(candidate.iter()) {
if *candidate < *slot {
*slot = *candidate;
}
}
}
#[cfg(feature = "vector4")]
set_vector(result, value[0], value[1], value[2], value[3]);
#[cfg(not(feature = "vector4"))]
set_vector(result, value[0], value[1], value[2], 0.0);
return 1;
}
}
-1
}
unsafe fn vector_max(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
unsafe {
if nparams >= 2 && nresults <= 1 && arg0.is_vector() && args.value_unchecked().is_vector() {
let mut value = arg0.vector_value();
let other = args.value_unchecked().vector_value();
for (slot, candidate) in value.iter_mut().zip(other.iter()) {
if *candidate > *slot {
*slot = *candidate;
}
}
for index in 3..=nparams {
let arg = args.add((index - 2) as usize).value_unchecked();
if !arg.is_vector() {
return -1;
}
let candidate = arg.vector_value();
for (slot, candidate) in value.iter_mut().zip(candidate.iter()) {
if *candidate > *slot {
*slot = *candidate;
}
}
}
#[cfg(feature = "vector4")]
set_vector(result, value[0], value[1], value[2], value[3]);
#[cfg(not(feature = "vector4"))]
set_vector(result, value[0], value[1], value[2], 0.0);
return 1;
}
}
-1
}
unsafe fn lerp(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 3
&& nresults <= 1
&& arg0.is_number()
&& unsafe { args.value_unchecked() }.is_number()
&& unsafe { args.add(1).value_unchecked() }.is_number()
{
let a = arg0.number_value();
let b = unsafe { args.value_unchecked() }.number_value();
let t = unsafe { args.add(1).value_unchecked() }.number_value();
let value = if t == 1.0 { b } else { a + (b - a) * t };
unsafe { result.value_unchecked() }.set_number(value);
return 1;
}
-1
}
unsafe fn vector_lerp(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
unsafe {
let t_arg = args.add(1).value_unchecked();
if nparams >= 3
&& nresults <= 1
&& arg0.is_vector()
&& args.value_unchecked().is_vector()
&& t_arg.is_number()
{
let a = arg0.vector_value();
let b = args.value_unchecked().vector_value();
let t = t_arg.number_value() as f32;
#[cfg(feature = "vector4")]
set_vector(
result,
lerp_f(a[0], b[0], t),
lerp_f(a[1], b[1], t),
lerp_f(a[2], b[2], t),
lerp_f(a[3], b[3], t),
);
#[cfg(not(feature = "vector4"))]
set_vector(
result,
lerp_f(a[0], b[0], t),
lerp_f(a[1], b[1], t),
lerp_f(a[2], b[2], t),
0.0,
);
return 1;
}
}
-1
}
macro_rules! unary_int {
($doc:literal, $name:ident, $op:expr) => {
#[doc = $doc]
unsafe fn $name(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
unsafe { result.value_unchecked() }.set_integer(($op)(arg0.integer_value()));
1
} else {
-1
}
}
};
}
macro_rules! binary_int {
($doc:literal, $name:ident, $op:expr) => {
#[doc = $doc]
unsafe fn $name(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
{
unsafe { result.value_unchecked() }.set_integer(($op)(
arg0.integer_value(),
unsafe { args.value_unchecked() }.integer_value(),
));
1
} else {
-1
}
}
};
}
unary_int!(
"`luauF_integer_neg`",
integer_neg,
|a: i64| (!(a as u64)).wrapping_add(1) as i64
);
binary_int!("`luauF_integer_add`", integer_add, |a: i64, b: i64| {
((a as u64).wrapping_add(b as u64)) as i64
});
binary_int!("`luauF_integer_sub`", integer_sub, |a: i64, b: i64| {
((a as u64).wrapping_sub(b as u64)) as i64
});
binary_int!("`luauF_integer_mul`", integer_mul, |a: i64, b: i64| {
((a as u64).wrapping_mul(b as u64)) as i64
});
unsafe fn integer_to_number(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
unsafe { result.value_unchecked() }.set_number(arg0.integer_value() as f64);
return 1;
}
-1
}
macro_rules! binary_integer_predicate {
($doc:literal, $name:ident, $left:expr, $right:expr, $pred:expr) => {
#[doc = $doc]
unsafe fn $name(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
{
let left = $left(arg0.integer_value());
let right = $right(unsafe { args.value_unchecked() }.integer_value());
unsafe { result.value_unchecked() }.set_boolean(i32::from(($pred)(left, right)));
return 1;
}
-1
}
};
}
binary_integer_predicate!(
"`luauF_integerlt`",
integer_lt,
|value: i64| value,
|value: i64| value,
|a, b| a < b
);
binary_integer_predicate!(
"`luauF_integerle`",
integer_le,
|value: i64| value,
|value: i64| value,
|a, b| a <= b
);
binary_integer_predicate!(
"`luauF_integergt`",
integer_gt,
|value: i64| value,
|value: i64| value,
|a, b| a > b
);
binary_integer_predicate!(
"`luauF_integerge`",
integer_ge,
|value: i64| value,
|value: i64| value,
|a, b| a >= b
);
binary_integer_predicate!(
"`luauF_integerult`",
integer_ult,
|value: i64| value as u64,
|value: i64| value as u64,
|a, b| a < b
);
binary_integer_predicate!(
"`luauF_integerule`",
integer_ule,
|value: i64| value as u64,
|value: i64| value as u64,
|a, b| a <= b
);
binary_integer_predicate!(
"`luauF_integerugt`",
integer_ugt,
|value: i64| value as u64,
|value: i64| value as u64,
|a, b| a > b
);
binary_integer_predicate!(
"`luauF_integeruge`",
integer_uge,
|value: i64| value as u64,
|value: i64| value as u64,
|a, b| a >= b
);
unsafe fn integer_create(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_number() {
let value = arg0.number_value();
if (i64::MIN as f64) <= value && value < 9223372036854775808.0 {
let integer = value as i64;
if integer as f64 == value {
unsafe { result.value_unchecked() }.set_integer(integer);
return 1;
}
}
unsafe { result.value_unchecked() }.set_nil();
return 1;
}
-1
}
unsafe fn integer_div(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
{
let a = arg0.integer_value();
let b = unsafe { args.value_unchecked() }.integer_value();
if b == 0 || (a == i64::MIN && b == -1) {
return -1;
}
unsafe { result.value_unchecked() }.set_integer(a / b);
return 1;
}
-1
}
unsafe fn integer_udiv(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
{
let a = arg0.integer_value() as u64;
let b = unsafe { args.value_unchecked() }.integer_value() as u64;
if b == 0 {
return -1;
}
unsafe { result.value_unchecked() }.set_integer((a / b) as i64);
return 1;
}
-1
}
unsafe fn integer_band(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
let mut value = arg0.integer_value() as u64;
for index in 2..=nparams {
let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
if !arg.is_integer() {
return -1;
}
value &= arg.integer_value() as u64;
}
unsafe { result.value_unchecked() }.set_integer(value as i64);
return 1;
}
-1
}
unsafe fn integer_bor(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
let mut value = arg0.integer_value() as u64;
for index in 2..=nparams {
let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
if !arg.is_integer() {
return -1;
}
value |= arg.integer_value() as u64;
}
unsafe { result.value_unchecked() }.set_integer(value as i64);
return 1;
}
-1
}
unsafe fn integer_bxor(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
let mut value = arg0.integer_value() as u64;
for index in 2..=nparams {
let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
if !arg.is_integer() {
return -1;
}
value ^= arg.integer_value() as u64;
}
unsafe { result.value_unchecked() }.set_integer(value as i64);
return 1;
}
-1
}
unary_int!("`luauF_integerbnot`", integer_bnot, |a: i64| !(a as u64)
as i64);
unsafe fn integer_byte_swap(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
unsafe { result.value_unchecked() }
.set_integer((arg0.integer_value() as u64).swap_bytes() as i64);
return 1;
}
-1
}
unsafe fn integer_btest(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
let mut value = arg0.integer_value() as u64;
for index in 2..=nparams {
let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
if !arg.is_integer() {
return -1;
}
value &= arg.integer_value() as u64;
}
unsafe { result.value_unchecked() }.set_boolean(i32::from(value != 0));
return 1;
}
-1
}
unsafe fn integer_min(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
{
let mut value = arg0
.integer_value()
.min(unsafe { args.value_unchecked() }.integer_value());
for index in 3..=nparams {
let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
if !arg.is_integer() {
return -1;
}
value = value.min(arg.integer_value());
}
unsafe { result.value_unchecked() }.set_integer(value);
return 1;
}
-1
}
unsafe fn integer_max(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
{
let mut value = arg0
.integer_value()
.max(unsafe { args.value_unchecked() }.integer_value());
for index in 3..=nparams {
let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
if !arg.is_integer() {
return -1;
}
value = value.max(arg.integer_value());
}
unsafe { result.value_unchecked() }.set_integer(value);
return 1;
}
-1
}
unsafe fn integer_rem(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
{
let a = arg0.integer_value();
let b = unsafe { args.value_unchecked() }.integer_value();
if b == 0 {
return -1;
}
let value = if a == i64::MIN && b == -1 { 0 } else { a % b };
unsafe { result.value_unchecked() }.set_integer(value);
return 1;
}
-1
}
unsafe fn integer_idiv(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
{
let a = arg0.integer_value();
let b = unsafe { args.value_unchecked() }.integer_value();
if b == 0 || (a == i64::MIN && b == -1) {
return -1;
}
let quotient = a / b;
let value = if quotient < 0 && a % b != 0 {
quotient - 1
} else {
quotient
};
unsafe { result.value_unchecked() }.set_integer(value);
return 1;
}
-1
}
unsafe fn integer_urem(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
{
let a = arg0.integer_value() as u64;
let b = unsafe { args.value_unchecked() }.integer_value() as u64;
if b == 0 {
return -1;
}
unsafe { result.value_unchecked() }.set_integer((a % b) as i64);
return 1;
}
-1
}
unsafe fn integer_mod(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
{
let a = arg0.integer_value();
let b = unsafe { args.value_unchecked() }.integer_value();
if b == 0 {
return -1;
}
if a == i64::MIN && b == -1 {
unsafe { result.value_unchecked() }.set_integer(0);
return 1;
}
let mut remainder = a % b;
if remainder != 0 && ((a < 0) != (b < 0)) {
remainder += b;
}
unsafe { result.value_unchecked() }.set_integer(remainder);
return 1;
}
-1
}
unsafe fn integer_clamp(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 3
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
&& unsafe { args.add(1).value_unchecked() }.is_integer()
{
let value = arg0.integer_value();
let min = unsafe { args.value_unchecked() }.integer_value();
let max = unsafe { args.add(1).value_unchecked() }.integer_value();
if min > max {
return -1;
}
unsafe { result.value_unchecked() }.set_integer(value.clamp(min, max));
return 1;
}
-1
}
unsafe fn integer_rotate_left(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
{
let value = arg0.integer_value() as u64;
let shift = ((unsafe { args.value_unchecked() }.integer_value() as u64) % 64) as u32;
let rotated = value.rotate_left(shift);
unsafe { result.value_unchecked() }.set_integer(rotated as i64);
return 1;
}
-1
}
unsafe fn integer_r_rotate(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
{
let value = arg0.integer_value() as u64;
let shift = ((unsafe { args.value_unchecked() }.integer_value() as u64) % 64) as u32;
let rotated = value.rotate_right(shift);
unsafe { result.value_unchecked() }.set_integer(rotated as i64);
return 1;
}
-1
}
unsafe fn integer_shift_left(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
{
let value = arg0.integer_value() as u64;
let shift = unsafe { args.value_unchecked() }.integer_value();
let shifted = if (-63..=63).contains(&shift) {
if shift < 0 {
value >> (-shift)
} else {
value << shift
}
} else {
0
};
unsafe { result.value_unchecked() }.set_integer(shifted as i64);
return 1;
}
-1
}
unsafe fn integer_ar_shift(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
{
let value = arg0.integer_value();
let shift = unsafe { args.value_unchecked() }.integer_value();
let shifted = if (-63..=63).contains(&shift) {
if shift < 0 {
((value as u64) << (-shift)) as i64
} else {
value >> shift
}
} else if shift < -63 {
0
} else if value < 0 {
-1
} else {
0
};
unsafe { result.value_unchecked() }.set_integer(shifted);
return 1;
}
-1
}
unsafe fn integer_r_shift(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
{
let value = arg0.integer_value() as u64;
let shift = unsafe { args.value_unchecked() }.integer_value();
let shifted = if (-63..=63).contains(&shift) {
if shift < 0 {
value << (-shift)
} else {
value >> shift
}
} else {
0
};
unsafe { result.value_unchecked() }.set_integer(shifted as i64);
return 1;
}
-1
}
unsafe fn integer_extract(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 3 && !unsafe { args.add(1).value_unchecked() }.is_integer() {
return -1;
}
if nparams >= 2
&& nresults <= 1
&& arg0.is_integer()
&& unsafe { args.value_unchecked() }.is_integer()
{
let value = arg0.integer_value() as u64;
let field = unsafe { args.value_unchecked() }.integer_value();
let width = if nparams >= 3 {
unsafe { args.add(1).value_unchecked() }.integer_value()
} else {
1
};
if !(0..=63).contains(&field) || !(1..=64).contains(&width) || field + width > 64 {
return -1;
}
unsafe { result.value_unchecked() }
.set_integer(((value >> field) & (u64::MAX >> (64 - width))) as i64);
return 1;
}
-1
}
unsafe fn integer_count_lz(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
let value = arg0.integer_value() as u64;
unsafe { result.value_unchecked() }.set_integer(if value == 0 {
64
} else {
value.leading_zeros() as i64
});
return 1;
}
-1
}
unsafe fn integer_count_rz(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
_args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
let value = arg0.integer_value() as u64;
unsafe { result.value_unchecked() }.set_integer(if value == 0 {
64
} else {
value.trailing_zeros() as i64
});
return 1;
}
-1
}
fn check_out_of_bounds(offset: i32, len: u32, access_size: usize) -> bool {
(offset as u32 as u64) + (access_size as u64 - 1) >= len as u64
}
unsafe fn read_buffer_data<T: Copy>(buffer: Buffer, offset: usize) -> T {
let mut value = MaybeUninit::<T>::uninit();
unsafe {
ptr::copy_nonoverlapping(
buffer.data_ptr().add(offset),
value.as_mut_ptr().cast::<u8>(),
core::mem::size_of::<T>(),
);
value.assume_init()
}
}
unsafe fn write_buffer_data<T: Copy>(buffer: Buffer, offset: usize, value: T) {
unsafe {
ptr::copy_nonoverlapping(
(&raw const value).cast::<u8>(),
buffer.data_mut_ptr().add(offset),
core::mem::size_of::<T>(),
);
}
}
#[cfg(not(target_endian = "big"))]
unsafe fn read_integer<T>(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32
where
T: Copy + Into<f64>,
{
if nparams >= 2
&& nresults <= 1
&& arg0.is_buffer()
&& unsafe { args.value_unchecked() }.is_number()
{
let offset = unsafe { args.value_unchecked() }.number_value() as i32;
let buffer = arg0.buffer_value();
if check_out_of_bounds(
offset,
unsafe { buffer.as_ptr().as_ref().unwrap_unchecked().len },
std::mem::size_of::<T>(),
) {
return -1;
}
let value = unsafe { read_buffer_data::<T>(buffer, offset as usize) };
unsafe { result.value_unchecked() }.set_number(value.into());
return 1;
}
-1
}
#[cfg(target_endian = "big")]
unsafe fn read_integer<T>(
_thread: &Thread,
_result: TValueCursor,
_arg0: TValue,
_nresults: i32,
_args: TValueCursor,
_nparams: i32,
) -> i32
where
T: Copy + Into<f64>,
{
-1
}
#[cfg(not(target_endian = "big"))]
unsafe fn write_unsigned<T>(
_thread: &Thread,
_result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32
where
T: Copy,
{
if nparams >= 3
&& nresults <= 0
&& arg0.is_buffer()
&& unsafe { args.value_unchecked() }.is_number()
&& unsafe { args.add(1).value_unchecked().is_number() }
{
let offset = unsafe { args.value_unchecked() }.number_value() as i32;
let buffer = arg0.buffer_value();
if check_out_of_bounds(
offset,
unsafe { buffer.as_ptr().as_ref().unwrap_unchecked().len },
std::mem::size_of::<T>(),
) {
return -1;
}
let value =
((unsafe { args.add(1).value_unchecked().number_value() } as i64) as u32).to_le_bytes();
unsafe {
ptr::copy_nonoverlapping(
value.as_ptr(),
buffer.data_mut_ptr().add(offset as usize),
std::mem::size_of::<T>(),
);
}
return 0;
}
-1
}
#[cfg(target_endian = "big")]
unsafe fn write_unsigned<T>(
_thread: &Thread,
_result: TValueCursor,
_arg0: TValue,
_nresults: i32,
_args: TValueCursor,
_nparams: i32,
) -> i32
where
T: Copy,
{
-1
}
#[cfg(not(target_endian = "big"))]
unsafe fn read_fp<T>(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32
where
T: Copy,
{
if nparams >= 2
&& nresults <= 1
&& arg0.is_buffer()
&& unsafe { args.value_unchecked() }.is_number()
{
let offset = unsafe { args.value_unchecked() }.number_value() as i32;
let buffer = arg0.buffer_value();
if check_out_of_bounds(
offset,
unsafe { buffer.as_ptr().as_ref().unwrap_unchecked().len },
std::mem::size_of::<T>(),
) {
return -1;
}
match std::mem::size_of::<T>() {
4 => {
let value = unsafe { read_buffer_data::<f32>(buffer, offset as usize) };
unsafe { result.value_unchecked() }.set_number(f64::from(value));
}
8 => {
let value = unsafe { read_buffer_data::<f64>(buffer, offset as usize) };
unsafe { result.value_unchecked() }.set_number(value);
}
_ => unreachable!(),
}
return 1;
}
-1
}
#[cfg(target_endian = "big")]
unsafe fn read_fp<T>(
_thread: &Thread,
_result: TValueCursor,
_arg0: TValue,
_nresults: i32,
_args: TValueCursor,
_nparams: i32,
) -> i32
where
T: Copy,
{
-1
}
#[cfg(not(target_endian = "big"))]
unsafe fn write_fp<T>(
_thread: &Thread,
_result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32
where
T: Copy,
{
if nparams >= 3
&& nresults <= 0
&& arg0.is_buffer()
&& unsafe { args.value_unchecked() }.is_number()
&& unsafe { args.add(1).value_unchecked().is_number() }
{
let offset = unsafe { args.value_unchecked() }.number_value() as i32;
let buffer = arg0.buffer_value();
if check_out_of_bounds(
offset,
unsafe { buffer.as_ptr().as_ref().unwrap_unchecked().len },
std::mem::size_of::<T>(),
) {
return -1;
}
match std::mem::size_of::<T>() {
4 => {
let value = unsafe { args.add(1).value_unchecked().number_value() } as f32;
unsafe { write_buffer_data::<f32>(buffer, offset as usize, value) };
}
8 => {
let value = unsafe { args.add(1).value_unchecked().number_value() };
unsafe { write_buffer_data::<f64>(buffer, offset as usize, value) };
}
_ => unreachable!(),
}
return 0;
}
-1
}
#[cfg(target_endian = "big")]
unsafe fn write_fp<T>(
_thread: &Thread,
_result: TValueCursor,
_arg0: TValue,
_nresults: i32,
_args: TValueCursor,
_nparams: i32,
) -> i32
where
T: Copy,
{
-1
}
#[cfg(not(target_endian = "big"))]
unsafe fn buffer_read_long(
_thread: &Thread,
result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 2
&& nresults <= 1
&& arg0.is_buffer()
&& unsafe { args.value_unchecked() }.is_number()
{
let offset = unsafe { args.value_unchecked() }.number_value() as i32;
let buffer = arg0.buffer_value();
if check_out_of_bounds(
offset,
unsafe { buffer.as_ptr().as_ref().unwrap_unchecked().len },
std::mem::size_of::<i64>(),
) {
return -1;
}
let value = unsafe { read_buffer_data::<i64>(buffer, offset as usize) };
unsafe { result.value_unchecked() }.set_integer(value);
return 1;
}
-1
}
#[cfg(target_endian = "big")]
unsafe fn buffer_read_long(
_thread: &Thread,
_result: TValueCursor,
_arg0: TValue,
_nresults: i32,
_args: TValueCursor,
_nparams: i32,
) -> i32 {
-1
}
#[cfg(not(target_endian = "big"))]
unsafe fn buffer_write_long(
_thread: &Thread,
_result: TValueCursor,
arg0: TValue,
nresults: i32,
args: TValueCursor,
nparams: i32,
) -> i32 {
if nparams >= 3
&& nresults <= 0
&& arg0.is_buffer()
&& unsafe { args.value_unchecked() }.is_number()
{
unsafe {
if !args.add(1).value_unchecked().is_integer() {
return -1;
}
let offset = args.value_unchecked().number_value() as i32;
let buffer = arg0.buffer_value();
if check_out_of_bounds(
offset,
buffer.as_ptr().as_ref().unwrap_unchecked().len,
std::mem::size_of::<i64>(),
) {
return -1;
}
write_buffer_data::<i64>(
buffer,
offset as usize,
args.add(1).value_unchecked().integer_value(),
);
return 0;
}
}
-1
}
#[cfg(target_endian = "big")]
unsafe fn buffer_write_long(
_thread: &Thread,
_result: TValueCursor,
_arg0: TValue,
_nresults: i32,
_args: TValueCursor,
_nparams: i32,
) -> i32 {
-1
}
pub static LUAU_F_TABLE: [LuauFastFunction; 256] = [
missing,
assert,
abs,
acos,
asin,
atan2,
atan,
ceil,
cosh,
cos,
deg,
exp,
floor,
fmod,
frexp,
ldexp,
log10,
log,
max,
min,
modf,
pow,
rad,
sinh,
sin,
sqrt,
tanh,
tan,
ar_shift,
band,
bnot,
bor,
bxor,
btest,
extract,
rotate_left,
shift_left,
replace,
r_rotate,
r_shift,
type_,
byte,
char,
len,
type_of,
sub,
clamp,
sign,
round,
raw_set,
raw_get,
raw_equal,
t_insert,
t_unpack,
vector,
count_lz,
count_rz,
select,
raw_len,
extract_k,
get_metatable,
set_metatable,
to_number,
to_string,
byte_swap,
read_integer::<i8>,
read_integer::<u8>,
write_unsigned::<u8>,
read_integer::<i16>,
read_integer::<u16>,
write_unsigned::<u16>,
read_integer::<i32>,
read_integer::<u32>,
write_unsigned::<u32>,
read_fp::<f32>,
write_fp::<f32>,
read_fp::<f64>,
write_fp::<f64>,
vector_magnitude,
vector_normalize,
vector_cross,
vector_dot,
vector_floor,
vector_ceil,
vector_abs,
vector_sign,
vector_clamp,
vector_min,
vector_max,
lerp,
vector_lerp,
is_nan,
is_inf,
is_finite,
integer_create,
integer_to_number,
integer_neg,
integer_add,
integer_sub,
integer_mul,
integer_div,
integer_min,
integer_max,
integer_rem,
integer_idiv,
integer_udiv,
integer_urem,
integer_mod,
integer_clamp,
integer_band,
integer_bor,
integer_bnot,
integer_bxor,
integer_lt,
integer_le,
integer_ult,
integer_ule,
integer_gt,
integer_ge,
integer_ugt,
integer_uge,
integer_shift_left,
integer_r_shift,
integer_ar_shift,
integer_rotate_left,
integer_r_rotate,
integer_extract,
integer_btest,
integer_count_rz,
integer_count_lz,
integer_byte_swap,
buffer_read_long,
buffer_write_long,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
missing,
];