diff --git a/lualib/snax.lua b/lualib/snax.lua index cca3032e..6af7fbd6 100644 --- a/lualib/snax.lua +++ b/lualib/snax.lua @@ -21,13 +21,7 @@ function snax.interface(name) for _,v in ipairs(si) do local id, group, name, f = table.unpack(v) - if group == "system" then - if f then - ret.system[name] = id - end - else - ret[group][name] = id - end + ret[group][name] = id end typeclass[name] = ret @@ -65,7 +59,6 @@ local function wrapper(handle, name, type) req = gen_req(type, handle), type = name, handle = handle, - kill = function(...) return skynet_call(handle, "lua", type.system.exit, ...) end, }, meta) end @@ -95,4 +88,22 @@ function snax.bind(handle, type) return ret end +function snax.kill(obj, ...) + local t = snax.interface(obj.type) + return skynet_call(obj.handle, "lua", t.system.exit, ...) +end + +local function test_result(ok, ...) + if ok then + return ... + else + error(...) + end +end + +function snax.hotfix(obj, ...) + local t = snax.interface(obj.type) + test_result(skynet_call(obj.handle, "lua", t.system.hotfix, obj.type, ...)) +end + return snax diff --git a/lualib/snax_hotfix.lua b/lualib/snax_hotfix.lua new file mode 100644 index 00000000..bbe5c214 --- /dev/null +++ b/lualib/snax_hotfix.lua @@ -0,0 +1,107 @@ +local si = require "snax_interface" +local io = io + +local hotfix = {} + +local function loader(filename, ...) + 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 + while true do + local name = debug.getupvalue(f, i) + if name 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 + end + i = i + 1 + end + return uv +end + +local function join_upvalues(f, new_f, uv) + local i = 1 + 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 + break + end + i = i + 1 + end +end + +local function join_global_upvalues(f, global) + 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 = {} + for id,v in pairs(funcs) do + local f = v[4] + local new_f = newfuncs[id][4] + 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 + for id,v in pairs(newfuncs) do + if v[2] == "system" then + funcs[id] = v + local f = v[4] + join_global_upvalues(f, global) + else + error(string.format("Detect unkown function %s.%s", v[2], v[3])) + end + end +end + +local function inject(funcs, new) + check_funcs(funcs, new) + update_upvalues(funcs, new) +end + +return function (funcs, modname) + local new_funcs = si(modname, _ENV, loader) + return pcall(inject, funcs, new_funcs) +end diff --git a/lualib/snax_interface.lua b/lualib/snax_interface.lua index 7f4c24dc..2dbb06b2 100644 --- a/lualib/snax_interface.lua +++ b/lualib/snax_interface.lua @@ -1,6 +1,7 @@ local skynet = require "skynet" -return function (name , G) +return function (name , G, loader) + loader = loader or loadfile local mainfunc local function func_id(id, group) @@ -33,7 +34,7 @@ return function (name , G) local env = {} local func = {} - local system = { "init", "exit" } + local system = { "init", "exit", "hotfix" } do for k, v in ipairs(system) do @@ -62,7 +63,7 @@ return function (name , G) for pat in string.gmatch(path,"[^;]+") do local filename = string.gsub(pat, "?", name) - local f , err = loadfile(filename, "bt", G) + local f , err = loader(filename, "bt", G) if f then mainfunc = f break diff --git a/service/snaxd.lua b/service/snaxd.lua index 6bd68086..2b62ddee 100644 --- a/service/snaxd.lua +++ b/service/snaxd.lua @@ -8,13 +8,24 @@ skynet.start(function() skynet.dispatch("lua", function ( _, _, id, ...) local method = func[id] if method[2] == "system" then - if method[3] == "init" then + local command = method[3] + if command == "hotfix" then + local hotfix = require "snax_hotfix" + local ok, msg = 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 assert(not init, "Already init") local initfunc = method[4] or function() end skynet.ret(skynet.pack(initfunc(...))) init = true else assert(init, "Never init") + assert(command == "exit") local exitfunc = method[4] or function() end skynet.ret(skynet.pack(exitfunc(...))) init = false diff --git a/test/pingserver.lua b/test/pingserver.lua index aeeda6cb..cedc26d3 100644 --- a/test/pingserver.lua +++ b/test/pingserver.lua @@ -1,12 +1,16 @@ local skynet = require "skynet" +local i = 0 +local hello = "hello" + function response.ping(hello) skynet.sleep(100) return hello end function subscribe.hello() - print "hello" + i = i + 1 + print (i, hello) end function response.error() diff --git a/test/testping.lua b/test/testping.lua index 9a141b43..512cbe31 100644 --- a/test/testping.lua +++ b/test/testping.lua @@ -6,6 +6,8 @@ skynet.start(function() print(ps.pub.hello()) print(ps.req.ping("foobar")) print(pcall(ps.req.error)) - print(ps.kill("exit")) + snax.hotfix(ps) + print(ps.pub.hello()) + print(snax.kill(ps,"exit")) skynet.exit() end)