# debug

This is an expansion to the existing Lua debug library.
This allows you to create many ways of accessing data that would be otherwise impossible.
As well as providing security features for safety of instrusions to lua-based APIs not from the Interstellar Engine.


# Functions

# debug.getfenv(obj: function | number): table?

debug.getfenv(obj: function | number): table?
  • Returns the environment of object obj.
  • This differs a bit from the base function getfenv, which takes a function or a level number, and converts the level number into a function.

# debug.gethook(thread: thread | function | number): function, number, number

debug.gethook(thread: thread | function | number): function, number, number
  • Returns the current hook settings as three values: Hook Function, Mask, Count

# debug.getinfo(thread: thread | function | number, field?: string, what?: function): table

debug.getinfo(thread: thread | function | number, field?: string, what?: function): table
  • Returns a table with information about a function or stack level
f - returns "func" field
l - returns "currentline" field
L - returns a table whose indices are the numbers of the lines that are valid on the function. (A valid line is a line with some associated code, that is, a line where you can put a break point. Non-valid lines include empty lines and comments.)
n - returns "name" and "namewhat" fields
S - returns "source", "short_src", "linedefined" and "what" fields
u - returns "nups" field

# debug.getlocal(level: number | function, local: number): string, any

debug.getlocal(level: number, local: number): string, any
  • Returns the name and value of the local variable with the index 'local' of the function at 'level' on the stack.

# debug.getmetatable(obj: proto | function | table | userdata): table?

debug.getmetatable(obj: proto | function | table | userdata): table?
  • Returns the metatable of the given object or nil if it does not have a metatable.

# debug.getregistry(): table

debug.getregistry(): table
  • Returns the registry table, which is a special table used by C functions to store information it wants to keep isolated from Lua functions.
  • This can act as permanent storage for variables to be retreived later on.

# debug.getupvalue(level: number | function, upvalue: number): string, any

debug.getupvalue(level: number | function, upvalue: number): string, any
  • Similar to debug.getlocal, returns the names and values of the upvalues for the nominated function.

# debug.setfenv(obj: function | number, tbl: table): any

debug.setfenv(obj: function | number, tbl: table): any
  • Sets the environment of the given object to the given table, returns the object.

# debug.setlocal(obj: function | number, local: number, value: any)

debug.setlocal(obj: function | number, local: number, value: any)
  • Sets the value of the local variable with the index 'local' of the function at 'level' on the stack.

# debug.sethook(thread: thread | function | number, f?: function, mask?: number, count?: number)

debug.sethook(thread: thread | function | number, f?: function, mask?: number, count?: number)
  • Sets the function f as a hook to be called when the "mask" condition is satisfied.
  • If count is non-zero, it is called after every "count" instructions.
  • The thread argument is optional and defaults to the current thread.
  • If called without arguments, turns off the hook.
  • The function f is called with its first parameter being a string, which can be one of: call, return, tail return, line, count
c - called every time Lua calls a function
r - called every time Lua returns from a function
l - called every time Lua enters a new line of code

# debug.setmetatable(obj: proto | function | table | userdata, tbl: table): any

debug.setmetatable(obj: proto | function | table | userdata, tbl: table): any
  • Sets the metatable for the given object to the given table (which can be nil).
  • Bypasses the check for the "__metatable" entry.
  • Returns the object.

# debug.setupvalue(level: number | function, upvalue: number, value: number)

debug.setupvalue(level: number | function, upvalue: number, value: number)
  • Similar to debug.setlocal, sets the values of the upvalue for the nominated function.

# debug.traceback(thread?: number | function | string, message?: string, level?: number)

debug.traceback(thread?: number | function | string, message?: string, level?: number)
  • Returns a string with a traceback of the stack call.
  • An optional message string is prepended to the beginning of the traceback message.

# Extension

# debug.registry(): table

debug.registry(): table
  • Gets the true debug registry table.
  • This table is used to keep permanent objects, preventing GC from removing them.

# debug.global(): table

debug.global(): table
  • Gets the true global table.

# debug.env(): table

debug.env(): table
  • Gets the current stack's environment.

# debug.detour(target: function, detour: function): function

debug.detour(target: function, detour: function): function
  • Creates a memory-based hook towards a target lua function or C function.

# debug.isdetour(target: function): boolean

debug.isdetour(target: function): boolean
  • If a function has a memory-mapping to another function.

# debug.original(target: function): function

debug.original(target: function): function
  • If a function has a memory-mapping, this will return the original.

# debug.restore(target: function)

debug.restore(target: function)
  • Restores the memory-mapped function.
local original
original = debug.detour(print, function(...)
       MsgC( Color( 255, 0, 0 ), "[Detour working!]" )
       return original(...)
end)

print("This is a test!") -- Outputs: [Detour working!]This is a test!

if debug.isdetour(print) then
    print("The `print` function has been detoured.")
else
    print("The `print` function is not detoured.")
end

local original_print = debug.original(print)
original_print("This bypasses the detour!")

debug.restore(print) -- Restores the original `print` function.
print("Detour has been restored!") -- Outputs normally without detour.

# debug.clone(value: function | proto | userdata): function | proto | userdata

debug.clone(value: function | proto | userdata): function | proto | userdata
  • Copys a value in memory, creating a completely new value but with the same characteristics.
local original_func = function(a, b) return a + b end
local cloned_func = debug.clone(original_func)

print(original_func(5, 3))  -- Output: 8
print(cloned_func(5, 3))    -- Output: 8 (cloned function has the same behavior)

# debug.replace(target: function | proto | userdata, value: function | proto | userdata)

debug.replace(target: function | proto | userdata, value: function | proto | userdata)
  • Replaces a value by reference in memory, effectively replacing all occurences of it.
local original = function() print("original call!") end
local replaced = function() print("replaced call!") end 
original()
debug.replace(original, replaced) --  replace, now its gonna print "replaced call!" instead of "original call!"
original()

# debug.newcclosure(func: function): function

debug.newcclosure(func: function): function
  • Creates a new cfunction closure which is untraceable.

# debug.getgc(iterator?: boolean): (table | function | proto | userdata)[] | function

debug.getgc(iterator?: boolean): (table | function | proto | userdata)[] | function
  • Gets the garbage collector table.
  • This has iterator functionality in foreach-loops.
  • Do note this contains every value in existance.

# debug.topointer(data: any, str: boolean): string | number

debug.topointer(data: any, str: boolean): string | number
  • Converts a datatype into it's absolute pointer location.
  • Output looks like this: "0xd8ad9caa" or 3635244450

# debug.frompointer(ptr: number): function | table | proto | userdata

debug.frompointer(ptr: number): function | table | proto | userdata
  • Scans memory to find the pointer association.

# debug.toproto(f: function): proto

debug.toproto(f: function): proto
  • Converts an function to a prototype object

# debug.fromproto(p: proto): function

debug.fromproto(p: proto): function
  • Converts an prototype into a function handle
  • Do note this creates a completely new function

# debug.getconstant(f: function, index: number): any

debug.getconstant(f: function, index: number): any
  • Gets a function's constant by their index

# debug.getconstants(f: function): {[index: string]: any}

debug.getconstants(f: function): {[index: string]: any}
  • Gets all constants from a function

# debug.setconstant(f: function, index: number, value: any): boolean

debug.setconstant(f: function, index: number, value: any): boolean
  • Sets a constant in a function at an index
  • Returns a boolean on if it was able to do so

# debug.iscfunction(f: function): boolean

debug.iscfunction(f: function): boolean
  • Checks if the provided function is made by "C"

# debug.islfunction(f: function): boolean

debug.islfunction(f: function): boolean
  • Checks if the provided function is made by "Lua"

# debug.setbuiltin(func: cfunction, ffid: number)

debug.setbuiltin(func: cfunction, ffid: number)
  • Changes a cfunction to be a Fast Function with an ID.
  • This will make them appear as "builtin: xx"
  • Use newcclosure if you plan to make a detour with this.

# debug.getbuiltin(func: cfunction): number

debug.getbuiltin(func: cfunction): number
  • Gets the ffid number of a cfunction.

# debug.getupvalues(func: function): {[index: string]: any}

debug.getupvalues(func: function): {[index: string]: any}
  • Gets a list of upvalues and that a function has.
local a, b, c = 1, 2, 3
local f = function() print(a, b, c) end
PrintTable(debug.getupvalues(f))
-- prints: {"a" = 1, "b" = 2, "c" = 3}

# debug.typestack(count?: number): string

debug.typestack(count?: number): string
  • Fetches a list of the current stack memory.
  • This is used to debug and print out what it looks like.

# debug.getstack(index: number): any

debug.getstack(index: number): any
  • Fetchs directly from the lua stack.
  • This can be used to get values in stack memory.

# debug.setstack(index: number, data: any)

debug.setstack(index: number, data: any)
  • Sets a value at an index in stack memory.
  • Warning: this is rather dangerous and can poison the lua stack.

# CAPI

# L:registry()

L:registry()
  • Gets the true debug registry table, pushing it onto the stack.
  • This table is used to keep permanent objects, preventing GC from removing them.

# L:global()

L:global()
  • Gets the true global table, pushing it onto the stack.

# L:env()

L:env()
  • Gets the current stack's environment, pushing it onto the stack.

# L:getgc()

L:getgc()
  • Gets the garbage collector table, pushing it onto the stack.
  • Do note this contains every value in existance.

# L:newcclosure(index: number)

L:newcclosure(index: number)
  • Creates a new cfunction closure which is untraceable.

# L:newcclosure(index: number)

L:newcclosure(index: number)
  • Creates a new cfunction closure which is untraceable.

# L:clone(index: number)

L:clone(index: number)
  • Copys a value in memory, creating a completely new value but with the same characteristics.

# L:replace(target: number, value: number)

L:replace(target: number, value: number)
  • Replaces a value by reference in memory, effectively replacing all occurences of it.

# L:topointer(index: number, str: boolean): string | number

L:topointer(index: number, str: boolean): string | number
  • Converts a datatype into it's absolute pointer location.
  • Output looks like this: "0xd8ad9caa" or 3635244450

# L:frompointer(ptr: number)

L:frompointer(ptr: number)
  • Scans memory to find the pointer association, pushing it onto the stack.