Last active
July 9, 2020 10:33
-
-
Save sharpobject/09d4fdbb7453f96492b838a3e274aa32 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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