use luau_vm::native::{NativeCallContext, NativeCallResult};
use luau_vm::thread::{StackGuard, upvalue_index};
use super::Function;
use crate::error::Error;
use crate::lua::LuaRef;
use crate::value::IntoLuaMulti;
impl<'lua> Function<'lua> {
pub fn bind(&self, args: impl IntoLuaMulti<'lua>) -> Result<Self, Error> {
let thread = self.thread();
let args = args.into_lua_multi(thread.lua_ref())?;
let arg_count = args.len();
if arg_count == 0 {
return self.try_clone();
}
if arg_count + 1 > u8::MAX as usize {
return Err(Error::BindError);
}
unsafe {
let vm_thread = thread.as_vm();
let _stack = StackGuard::new(vm_thread);
thread.reserve_stack(arg_count + 1)?;
vm_thread
.push_integer(arg_count as i32)
.map_err(|exit| Error::from_thread_exit(vm_thread, exit))?;
for arg in &args {
arg.push_to(&thread)?;
}
vm_thread
.push_native_closure(bind_args_wrapper, Some("__bind_args"), arg_count as i32 + 1)
.map_err(|exit| Error::from_thread_exit(vm_thread, exit))?;
let args_wrapper = Function::from_stack(&thread, -1)
.map_err(|exit| Error::from_thread_exit(vm_thread, exit))?;
let lua = LuaRef::new(thread.reference_thread(), thread.runtime());
lua.load(
r#"
local func, args_wrapper = ...
return function(...)
return func(args_wrapper(...))
end
"#,
)
.set_name("=__luau_bind")
.call((self.try_clone()?, args_wrapper))
}
}
}
fn bind_args_wrapper(context: NativeCallContext<'_>) -> NativeCallResult {
unsafe {
let thread = context.raw_thread();
let arg_count = context.arg_count();
let bound_count = thread.to_integer(upvalue_index(1)).unwrap_or(0);
thread.lua_check_stack(bound_count, None)?;
for index in 0..bound_count {
thread.push_value(upvalue_index(index + 2))?;
}
for _ in 0..bound_count {
thread.insert(1);
}
Ok((arg_count + bound_count) as usize)
}
}