Created
April 9, 2019 08:23
-
-
Save tkokof/ed2e69b309a42c37607ea03cdbdb3327 to your computer and use it in GitHub Desktop.
a simple reload module implementation of Lua
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 function try_get_upvalue(func, name) | |
local upvalue_index = 1 | |
while true do | |
local u_name, u_value = debug.getupvalue(func, upvalue_index) | |
if not u_name then | |
break | |
elseif u_name == name then | |
return u_name, u_value, upvalue_index | |
end | |
upvalue_index = upvalue_index + 1 | |
end | |
end | |
local function merge_upvalues_recur(old_func, new_func) | |
local upvalue_index = 1 | |
while true do | |
local new_name, new_value = debug.getupvalue(new_func, upvalue_index) | |
if not new_name then | |
break | |
elseif new_name ~= "_ENV" then -- skip "_ENV" now ... | |
local name, value, index = try_get_upvalue(old_func, new_name) | |
if not name then | |
print("can not resolve upvalue(reload could be incorrect) : " .. tostring(new_name)) | |
else | |
if type(value) == "function" then | |
return merge_upvalues_recur(value, new_value) | |
else | |
debug.upvaluejoin(new_func, upvalue_index, old_func, index) | |
end | |
end | |
end | |
upvalue_index = upvalue_index + 1 | |
end | |
return true | |
end | |
-- limitation reload module | |
function reload_module(name, path) | |
if name and path then | |
local global_module = _G[name] | |
local package_module = package.loaded[path] | |
-- when package_module is true, check global_module and package_module | |
if package_module ~= true then | |
if global_module and global_module ~= package_module then | |
return false, "incorrect module internal status : " .. tostring(name) | |
end | |
end | |
-- clear references | |
_G[name] = nil | |
package.loaded[path] = nil | |
-- require new module | |
-- NOTE module should not have side effects when require | |
local status, error = pcall(require, path) | |
if not status then | |
-- rollback for error handling | |
_G[name] = global_module | |
package.loaded[path] = package_module | |
return false, error | |
end | |
local new_global_module = _G[name] | |
local new_package_module = package.loaded[path] | |
-- rollback first for error handling | |
_G[name] = global_module | |
package.loaded[path] = package_module | |
local old_module = type(package_module) == "table" and package_module or global_module | |
local new_module = type(new_package_module) == "table" and new_package_module or new_global_module | |
if type(old_module) ~= "table" or type(new_module) ~= "table" then | |
return false, "error to get old and new module data : " .. tostring(name) | |
end | |
for k, v in pairs(old_module) do | |
if type(v) ~= "function" then | |
-- simple copy non-function stuff from old to new | |
-- TODO improve ? | |
new_module[k] = v | |
else | |
-- merge function upvalues | |
-- TODO improve ? | |
local old_func = v | |
local new_func = new_module[k] | |
if type(new_func) ~= "function" then | |
return false, "only support same function layout module reload now : " .. tostring(name) .. "." .. tostring(k) | |
else | |
local ret = merge_upvalues_recur(old_func, new_func) | |
if not ret then | |
return false, "error to resolve upvalues : " .. tostring(name) .. "." .. tostring(k) | |
end | |
end | |
end | |
end | |
-- extra clear stuff | |
if type(old_module.release) == "function" then | |
old_module.release() | |
end | |
if type(new_module.init) == "function" then | |
new_module.init() | |
end | |
-- set to new module | |
_G[name] = new_global_module | |
package.loaded[path] = new_package_module | |
return true | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment