Skip to content

Instantly share code, notes, and snippets.

@sharpobject
Last active July 9, 2020 10:33
Show Gist options
  • Save sharpobject/09d4fdbb7453f96492b838a3e274aa32 to your computer and use it in GitHub Desktop.
Save sharpobject/09d4fdbb7453f96492b838a3e274aa32 to your computer and use it in GitHub Desktop.
local debug = debug
local coroutine = coroutine
local type = type
local next = next
local pairs = pairs
local package = package
-- todo: lightuserdata?
local all_types = {
1,
true,
"hello",
function() return type end,
coroutine.create(function() end)
}
local function maybe_visit(visited, to_visit, item)
local typ = type(item)
if (typ == "table" or typ == "thread" or typ == "function") and not visited[item] then
to_visit[item] = true
end
end
local function real_replace(old, new)
local to_visit = {}
local visited = {}
visited[coroutine.running()] = true
to_visit[debug.getregistry()] = true
for i=0,#all_types do
maybe_visit(visited, to_visit, debug.getmetatable(all_types[i]))
end
while true do
local item = next(to_visit)
if item == nil then break end
if type(item) == "table" then
local mt = debug.getmetatable(item)
maybe_visit(visited, to_visit, mt)
if mt == old then mt = new end
debug.setmetatable(item, nil)
for k,v in pairs(item) do
if v == old then
item[k] = new
end
maybe_visit(visited, to_visit, k)
maybe_visit(visited, to_visit, v)
end
if item[old] then
item[new] = item[old]
item[old] = nil
end
debug.setmetatable(item, mt)
elseif type(item) == "thread" then
for qq=0,1e99 do
local info = debug.getinfo(item, qq)
if not info then break end
for i=1,1e99 do
local name,value = debug.getlocal(item, qq, i)
if name == nil then break end
maybe_visit(visited, to_visit, value)
if value == old then
debug.setlocal(item, qq, i, new)
end
end
for i=-1,-1e99,-1 do
local name,value = debug.getlocal(item, qq, i)
if name == nil then break end
maybe_visit(visited, to_visit, value)
if value == old then
debug.setlocal(item, qq, i, new)
end
end
end
elseif type(item) == "function" then
for i=1,1e99 do
local name,value = debug.getupvalue(item, i)
if name == nil then break end
maybe_visit(visited, to_visit, value)
if value == old then
debug.setupvalue(item, i, new)
end
end
end
to_visit[item] = nil
visited[item] = true
end
end
local function replace(old, new)
local f = coroutine.wrap(real_replace)
f(old, new)
end
return replace
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment