trampoline_call

Call a function with a custom stack and thread context.

Syntax

type TrampolineCallFrame = {
    currentline: number,
    func: (...any) -> ...any,
}

type TrampolineThreadOptions = {
    script: Instance?,
    identity: number?,
    env: {[any]: any}?,
    thread: thread?,
}

trampoline_call(
    func: (...any) -> ...any,
    callstack: {TrampolineCallFrame},
    thread_options: TrampolineThreadOptions,
    ...: any
) -> (boolean, ...any)

Parameters

ParameterTypeDescription
func(...any) -> ...anyFunction to call. Supports Luau and C functions
callstack{TrampolineCallFrame}Ordered caller frames to expose while func runs
thread_optionsTrampolineThreadOptionsScript, identity, environment, or parent thread for the call
...anyArguments passed to func

Both callstack and thread_options are required tables. Pass {} when no custom frames or context options are needed.

Returns

ResultDescription
true, ...resultsThe call succeeded. Remaining values came from func
false, errorValueThe call failed. The next value is the error

If func returns no values, a successful call returns only true.

false is returned only when func errors. Invalid arguments passed to trampoline_call raise an error normally.

Call Stack Frames

Each callstack entry adds one caller frame. Index 1 is closest to func. Later entries are farther out.

FieldTypeDescription
funcfunctionSets the frame's function name, source, and debug details
currentlinenumberSets the source line. It is still required for a C function

Use a dense array. Do not skip indexes. Frame functions are not called.

local function test()
    print(debug.info(1, "nl")) -- test, 2
    print(debug.info(2, "nl")) -- fake_caller, 10
end

local function fake_caller()
end

trampoline_call(
    test,
    {
        {
            func = fake_caller,
            currentline = 10,
        },
    },
    {}
)

Stack level 1 is the real test frame. Stack level 2 is the fake_caller frame. It reports line 10.

debug.getcallstack omits currentline for C frames. Remove those frames or add a number before calling trampoline_call.

Thread Options

FieldTypeDescription
scriptInstance?Script context for the call. It must be a LuaSourceContainer
identitynumber?Thread identity used while func runs
env{[any]: any}?Global environment table used by the call
threadthread?Parent thread for the call

When thread is set, it becomes the parent of the call. Inside func, coroutine.running() returns that thread. The call inherits its script, identity, and environment. Its coroutine function is not run or resumed. The thread context overrides script, identity, and env.

Parent Thread Example

local fake_thread = coroutine.create(function(...)
end)

local function test()
    print(coroutine.running() == fake_thread) -- true
end

trampoline_call(test, {}, {
    thread = fake_thread,
})

Script Context Example

local fake_script = Instance.new("LocalScript")

local function test()
    print(getcallingscript() == fake_script) -- true
end

trampoline_call(test, {}, {
    script = fake_script,
})

getcallingscript returns fake_script while test runs.