hookfunction
Redirects a function's entry point to a custom hook.
function hookfunction(target: function, hook: function): functionSynopsis
How it works
hookfunction performs a deep swap at the Luau VMLuau VMThe Luau Virtual Machine — a register-based bytecode interpreter. Executes compiled Luau instructions sequentially, managing a value stack, call stack, and upvalue chains. Roblox runs one VM per Actor context. level. For Luau closures (LClosure), it swaps the internal Proto* pointer and upvalueUpvalueA variable captured from an enclosing scope. When a function references a local variable from an outer function, that variable becomes an upvalue — stored in an UpVal struct that persists even after the outer function returns. array between the target and a clone, so all existing references to the target now execute the hook's bytecodeBytecodeThe compiled binary representation of Luau source code. A sequence of 32-bit instructions (opcodes + operands) stored in a Proto's code[] array. Executed by the Luau VM interpreter.. For C closures, it swaps the lua_CFunction pointer. The original implementation is returned as a "trampolineTrampolineA wrapper function that bounces (redirects) calls to another function. In executor context, newcclosure creates a C closure trampoline that calls the Luau function stored in its first upvalue.".VM internals — LClosure swap
An
LClosure in the Luau VMLuau VMThe Luau Virtual Machine — a register-based bytecode interpreter. Executes compiled Luau instructions sequentially, managing a value stack, call stack, and upvalue chains. Roblox runs one VM per Actor context. is laid out as:
typedef struct LClosure {
ClosureHeader; // GCObject header + isC=0 + nupvalues + env
Proto* p; // pointer to compiled bytecode
UpVal* uprefs[]; // captured variable pointers (flexible array)
} LClosure;
hookfunction(target, hook):
- Clones the target's
LClosure(allocating a new GC object with identicalpanduprefs) — this becomes the trampolineTrampolineA wrapper function that bounces (redirects) calls to another function. In executor context, newcclosure creates a C closure trampoline that calls the Luau function stored in its first upvalue. - Overwrites the target's
ppointer with the hook'sProto* - Copies the hook's
uprefs[]array into the target's slot - Returns the clone (trampoline)
VM internals — CClosure swap
A
CClosure is simpler:
typedef struct CClosure {
ClosureHeader; // GCObject header + isC=1 + nupvalues + env
lua_CFunction f; // native function pointer
TValue upvals[]; // C-side upvalues
} CClosure;
For C closures, hookfunction swaps just the f pointer and upvalueUpvalueA variable captured from an enclosing scope. When a function references a local variable from an outer function, that variable becomes an upvalue — stored in an UpVal struct that persists even after the outer function returns. array. The trampolineTrampolineA wrapper function that bounces (redirects) calls to another function. In executor context, newcclosure creates a C closure trampoline that calls the Luau function stored in its first upvalue. gets the original f pointer.Trampoline pattern
The returned trampolineTrampolineA wrapper function that bounces (redirects) calls to another function. In executor context, newcclosure creates a C closure trampoline that calls the Luau function stored in its first upvalue. is critical for calling the original logic without recursion:
- Correct:
local old = hookfunction(target, hook); old(...) - Wrong:
hookfunction(target, function(...) target(...) end)— infinite recursion becausetargetnow IS the hook
Closure object in the GC, so it is safe from further hooks on the original target.Usage
Intercept print
local originalPrint = hookfunction(print, function(...)
"cc">-- Do something before
return originalPrint("[Intercepted]", ...)
end)
print("Hello") "cc">-- Output: "[Intercepted] Hello"Parameters
target function The function whose execution is to be redirected.
hook function The replacement function that will be called instead.
Returns
function A trampoline to the original implementation of the target function.
Trampoline Usage
Always use the returned trampoline (not the original symbol) to call the original function inside your hook. Calling the original symbol will re-trigger your hook.