Using the Debug API

We still haven't covered one of the most important APIs within Synapse - the debug API. The debug API allows full control over the execution state of any running script, which we will now explain how to use.

debug.*upvalue(s)

We will first be explaining the most simple (but arguably the most used) function within the debug API - the upvalue functions.

You might be asking, what is a upvalue? An upvalue is a local variable that is used in more than 1 function. See an example below of an upvalue:

local TestVariable = "Hello, world!"

local function Test()
    print(TestVariable)
    TestVariable = "Still - Hello, world!"
    print(TestVariable)
end

Test()

The TestVariable is an upvalue within the Test function. We can grab and modify that variable with the debug.setupvalue function.

An example of this is shown below:

local TestVariable = "Hello, world!"

local function Test()
    print(TestVariable)
    TestVariable = "Still - Hello, world!"
    print(TestVariable)
end

debug.setupvalue(Test, 1, "Hello, modified world!")
Test()

Instead of printing "Hello, world!" followed by "Still - Hello, world!" as it normally would, it instead prints Hello, modified world! first!

Call-stack Levels

You can also call the full set of debug functions with a function level instead of the function itself, just like getfenv/setfenv.

local TestVariable = "Hello, world!"

local function Test()
    local function InnerFunction()
        --2 refers to the Test function
        debug.setupvalue(2, 1, "Hello, modified world!")
    end

    InnerFunction()

    print(TestVariable)
    TestVariable = "Still - Hello, world!"
    print(TestVariable)
end

Test()

This will produce the same results as the above script.

debug.*constants

We will next be explaining the constant functions, which can be used to get/modify the constant values within a script.

An example is provided below:

local TestVariable = "Hello, world!"

local function Test()
    print(TestVariable)
    TestVariable = "Still - Hello, world!"
    print(TestVariable)
end

debug.setconstant(Test, 2, "Modified - Hello, world!")
Test()

This will print out "Hello, world!" proceeded by "Modified - Hello world!" instead of "Still - Hello, world!".

Note: Certain low number values (0-65535) may not show up in the constant functions. We plan to add support for them later.

debug.*stack

The debug API also has one other set of functions - the stack functions.

The stack functions allow you to modify any value on the stack of any function currently running that has called you. This is mostly used to modify local variables that are not upvalues. Please note this only supports call-stack levels unlike the rest of the functions.

An example is shown below:

local function Test()
    local TestVariable = "Hello, world!"
    local function InnerFunction()
        --2 refers to the Test function
        debug.setstack(2, 1, "Hello, modified world!")
    end

    InnerFunction()

    print(TestVariable)
    TestVariable = "Still - Hello, world!"
    print(TestVariable)
end

Test()

This will also produce the same results as the upvalue testing script.

Miscellaneous debug functions

The debug API also contains some other useful functions:

debug.validlevel - This will allow you to check if a call-stack level actually exists or not. Makes you not need to do pcall for debug functions with stack levels.

debug.getregistry - This allows you to get the Lua registry, which can be used to get connections in memory and other information.

Lets now move on to calling external functions.