use a patch to hotfix

This commit is contained in:
Cloud Wu
2014-04-17 13:49:25 +08:00
parent 58dfe38c4b
commit 6d4d9f8758
4 changed files with 88 additions and 91 deletions

View File

@@ -101,9 +101,9 @@ local function test_result(ok, ...)
end end
end end
function snax.hotfix(obj, ...) function snax.hotfix(obj, source, ...)
local t = snax.interface(obj.type) local t = snax.interface(obj.type)
test_result(skynet_call(obj.handle, "lua", t.system.hotfix, obj.type, ...)) return test_result(skynet_call(obj.handle, "lua", t.system.hotfix, source, ...))
end end
return snax return snax

View File

@@ -3,105 +3,92 @@ local io = io
local hotfix = {} local hotfix = {}
local function loader(filename, ...) local function collect_uv(f , uv)
local f = io.open(filename, "rb")
if not f then
return false, string.format("Can't open %s", filename)
end
local source = f:read "*a"
f:close()
return load(source, "@"..filename, ...)
end
local function check_funcs(f1,f2)
for k,a in pairs(f1) do
local b = f2[k]
assert(a[1] == b[1] and a[2] == b[2] and a[3] == b[3] ,
string.format("%s.%s %s.%s , function mismatch", a[2], a[3] , b[2], b[3]))
end
end
local function get_upvalues(f, global)
local uv = {}
local i = 1 local i = 1
while true do while true do
local name = debug.getupvalue(f, i) local name, value = debug.getupvalue(f, i)
if name then if name == nil then
local uid = debug.upvalueid(f, i)
uv[name] = i
if global[name] and global[name][1] ~= uid then
error(string.format("ambiguity upvalue %s", name))
end
global[name] = { uid , f , i }
else
break break
end end
i = i + 1 local id = debug.upvalueid(f, i)
end
return uv
end
local function join_upvalues(f, new_f, uv) if uv[name] then
local i = 1 assert(uv[name].id == id, string.format("ambiguity local value %s", name))
while true do
local name = debug.getupvalue(new_f, i)
if name then
if uv[name] then
debug.upvaluejoin(new_f, i, f, uv[name])
end
else else
break uv[name] = { func = f, index = i, id = id }
if type(value) == "function" then
collect_uv(value, uv)
end
end end
i = i + 1 i = i + 1
end end
end end
local function join_global_upvalues(f, global) local function collect_all_uv(funcs)
local i = 1
while true do
local name = debug.getupvalue(f, i)
if name then
local uv = global[name]
if uv then
debug.upvaluejoin(f, i, uv[2], uv[3])
end
else
break
end
i = i + 1
end
end
local function update_upvalues(funcs, newfuncs)
local global = {} local global = {}
for id,v in pairs(funcs) do for _, v in pairs(funcs) do
local f = v[4] if v[4] then
local new_f = newfuncs[id][4] collect_uv(v[4], global)
if f and new_f then
local uv = get_upvalues(f, global)
v[4] = new_f
join_upvalues(f, new_f, uv)
newfuncs[id] = nil
end end
end end
for id,v in pairs(newfuncs) do
if v[2] == "system" then return global
funcs[id] = v end
local f = v[4]
join_global_upvalues(f, global) local function loader(source)
else return function (filename, ...)
error(string.format("Detect unkown function %s.%s", v[2], v[3])) return load(source, "=patch", ...)
end
end
local function find_func(funcs, group , name)
for _, desc in pairs(funcs) do
local _, g, n = table.unpack(desc)
if group == g and name == n then
return desc
end end
end end
end end
local function inject(funcs, new) local dummy_env = {}
check_funcs(funcs, new)
update_upvalues(funcs, new) local function patch_func(funcs, global, group, name, f)
local desc = assert(find_func(funcs, group, name) , string.format("Patch mismatch %s.%s", group, name))
local i = 1
while true do
local name, value = debug.getupvalue(f, i)
if name == nil then
break
elseif value == nil or value == dummy_env then
local old_uv = global[name]
if old_uv then
debug.upvaluejoin(f, i, old_uv.func, old_uv.index)
end
end
i = i + 1
end
desc[4] = f
end end
return function (funcs, modname) local function inject(funcs, source, ...)
local new_funcs = si(modname, _ENV, loader) local patch = si("patch", dummy_env, loader(source))
return pcall(inject, funcs, new_funcs) local global = collect_all_uv(funcs)
for _, v in pairs(patch) do
local _, group, name, f = table.unpack(v)
if f then
patch_func(funcs, global, group, name, f)
end
end
local hf = find_func(patch, "system", "hotfix")
if hf and hf[4] then
return hf[4](...)
end
end
return function (funcs, source, ...)
return pcall(inject, funcs, source, ...)
end end

View File

@@ -11,13 +11,7 @@ skynet.start(function()
local command = method[3] local command = method[3]
if command == "hotfix" then if command == "hotfix" then
local hotfix = require "snax_hotfix" local hotfix = require "snax_hotfix"
local ok, msg = hotfix(func, ...) skynet.ret(skynet.pack(hotfix(func, ...)))
if ok then
local hf = func[id][4]
skynet.ret(skynet.pack(pcall(hf, select(2,...))))
else
skynet.ret(skynet.pack(ok, msg))
end
elseif command == "init" then elseif command == "init" then
assert(not init, "Already init") assert(not init, "Already init")
local initfunc = method[4] or function() end local initfunc = method[4] or function() end

View File

@@ -6,7 +6,23 @@ skynet.start(function()
print(ps.pub.hello()) print(ps.pub.hello())
print(ps.req.ping("foobar")) print(ps.req.ping("foobar"))
print(pcall(ps.req.error)) print(pcall(ps.req.error))
snax.hotfix(ps) print("Hotfix (i) :", snax.hotfix(ps, [[
local i
local hello
function subscribe.hello()
i = i + 1
print ("fix", i, hello)
end
function hotfix(...)
local temp = i
i = 100
return temp
end
]]))
print(ps.pub.hello()) print(ps.pub.hello())
print(snax.kill(ps,"exit")) print(snax.kill(ps,"exit"))
skynet.exit() skynet.exit()