Refactor codebase

This commit is contained in:
Aaron Smith 2023-08-22 15:50:03 +01:00
parent 07ae542e28
commit a3f03fd067
15 changed files with 228 additions and 128 deletions

View file

@ -3,15 +3,41 @@ if not game then script = require "test/relative-string" end
local flags = require(script.Parent.flags)
local throw = require(script.Parent.throw)
--[[
Cleanups associate a callback with an arbitrary value with an unknown lifetime.
Anytime a new callback is registered with a value that already has one registered,
the registered callback is ran and then replaced with the new one.
When the value is eventually garbage collected, Vide checks for callbacks
without an associated value, which it will then run and clear, recycling its
cleanup id.
By default the arbitrary value is the function object that calls `cleanup()`.
There are exceptions such as with `indexes()` and `values()` where the
arbitrary value is manually set to be the new source created instead of the
caller, as the same caller can be used to create multiple new objects.
todo: remove need for ref to id maps?
]]
-- maps a ref to cleanup id
local ref_to_id = {} :: { [string]: number }
-- maps a cleanup id to a ref
local id_to_ref = {} :: { [number]: string }
-- array of all cleanup callbacks
local cleanup_callbacks = {} :: { [number]: () -> () } -- always dense
-- weak array of all cleanup lifetimes
local cleanup_lifetime = {} :: { [number]: unknown } -- can be sparse
setmetatable(cleanup_lifetime :: any, { __mode = "v" })
-- detects in strict mode when multiple cleanups are registered in the same scope
local debug_caller_to_line = {} :: { [() -> ()]: number }
setmetatable(debug_caller_to_line, { __mode = "k" })
-- when active, cleanup callbacks are not automatically registered but are
-- added to an array for manual registering internally
local manual_mode = {
caller = false :: false | () -> (),
callbacks = {} :: { () -> () }
@ -20,13 +46,14 @@ local manual_mode = {
-- todo: rare case where mem address is reused by another function
-- does this case handle itself?
-- registers a callback with the given lifetime using the given ref
local function cleanup_ref(ref: string, lifetime: unknown, callback: () -> ())
local id = ref_to_id[ref]
if id then
if id then -- invoke previously registered callback then register new one
cleanup_callbacks[id]()
cleanup_lifetime[id] = lifetime -- rare case where ref is reused while lifetime is nil
else
else -- no previously registered callback, add and register new one
id = #cleanup_callbacks + 1
ref_to_id[ref] = id
id_to_ref[id :: any] = ref -- todo
@ -36,6 +63,7 @@ local function cleanup_ref(ref: string, lifetime: unknown, callback: () -> ())
cleanup_callbacks[id] = callback
end
-- registers a callback with its caller as the lifetime, and caller address as the ref
local function cleanup(callback: () -> ())
local lifetime = debug.info(2, "f") -- `caller of cleanup() is lifetime of cleanup`