- Notifications
You must be signed in to change notification settings - Fork34
Lua 5.2/5.3 hotfix. Hot update functions and keep old data.
License
jinq0123/hotfix
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Lua 5.2/5.3 hotfix. Hot update functions and keep old data.
If we have a test.lua
localM= {}M.count=0functionM.func()M.count=M.count+1return"v1"endreturnM
Require test and call func(), then count will be 1.
> test = require("test") > test.func() v1 > test.count 1
Change "v1" to "v2" in test.lua, then hotfix module test and call func() again.The result shows that func() has been updated, but the count is kept.
> hotfix = require("hotfix.hotfix") > hotfix.hotfix_module("test") table: 0000000002752060 > test.func() v2 > test.count 2
UsingLuaRocks:
luarocks install hotfix
Or manually copylua/hotfix
directory into your Lua module path.
localhotfix=require("hotfix.hotfix")hotfix.hotfix_module("mymodule.sub_module")
helper/hotfix_helper.lua
is an example to hotfix modified modules usinglfs
.Please seehelper/README.md.
hotfix_module()
usespackage.searchpath(module_name, package.path)
to search the path of module.The module is reloaded and the returned value is updated topackage.loaded[module_name]
.If the returned value isnil
, thenpackage.loaded[module_name]
is assigned totrue
.hotfix_module()
returns the final value ofpackage.loaded[module_name]
.
hotfix_module()
will skip unloaded module to avoid unexpected loading,and also to work around the issue of"Three dots module name will be nil".
Functons are updated to new ones but old upvalues are kept.Old tables are kept and new fields are inserted.All references to old functions are replaced to new ones.
The module may change any global variables if it wants to.See"Why not protect the global variables" below.
Local variable which is not referenced by_G
is not updated.
-- test.lua: return { function func() return "old" end }localtest=require("test")-- referenced by _G.package.loaded["test"]localfunc=test.func-- is not upvalue nor is referenced by _G-- test.lua: return { function func() return "new" end }require("hotfix.hotfix").hotfix_module("test")test.func()-- "new"func()-- "old"
We can protect the global variables on loading in some ways, but there are other problems.
- [1] uses a read only
ENV
to load.
localenv= {}setmetatable(env, {__index=_G })load(chunk,check_name,'t',env)
But it can not stop indirect write.Global variables may be changed.In the following example,t
is OK butmath.sin
is changed.
Lua 5.3.2 Copyright (C) 1994-2015 Lua.org, PUC-Rio> math.sin(123)-0.45990349068959> do>> local _ENV = setmetatable({}, {__index = _G})>> t = 123>> math.sin = print>> end> tnil> math.sin(123)123
- [2] uses a fake
ENV
to load and ignores all operations.In this case, we can not init new local variables.
localM= {}+locallog=require("log")-- Can not require!functionM.foo()+log.info("test")endreturnM
Another problem is the new function's_ENV
is not the realENV
.Following test will fail becauseset_global()
has a protectedENV
.
log("New upvalue which is a function set global...")run_test([[ local M = {} function M.foo() return 12345 end return M]],function()assert(nil==global_test)end,[[ local M = {} local function set_global() global_test = 11111 end function M.foo() set_global() end return M]],function()assert(nil==test.foo())assert(11111==global_test)-- FAIL!global_test=nilend)
Runmain.lua
in test dir.main.lua
will write atest.lua
file and hotfix it.main.lua
will write log tolog.txt
.
D:\Jinq\Git\hotfix\test>..\..\..\tools\lua-5.3.2_Win64_bin\lua53Lua 5.3.2 Copyright (C) 1994-2015 Lua.org, PUC-Rio> require("main").run()main.lua:80: assertion failed!
log
function is changed fromprint
to an empty function.The hotfix will replace allprint
to an empty function which is totally unexpected.
localM= {}locallog=printfunctionM.foo()log("Old")endreturnM
localM= {}locallog=function()endfunctionM.foo()log("Old")endreturnM
hotfix.add_protect{print}
can protectprint
function from being replaced.But it also means thatlog
can not be updated.
hotfix.lua:210: file.lua:1: unexpected symbol near '<\239>'
--- test.lua.-- @module testlocalmodule_name=...print(module_name)
require("test")
will print "test", but hotfix which usesload()
will print "nil".
[1] hotfix by tickbh
https://github.com/tickbh/td_rlua/blob/11523931b0dd271ad4c5e9c532a9d3bae252a264/td_rlua/src/hotfix.rs
http://www.cnblogs.com/tickbh/articles/5459120.html (In Chinese)
Lua 5.2/5.3.Can only update global functions.
localM= {}+functionM.foo()end-- Can not add M.foo().returnM
[2] lua_hotupdate
https://github.com/asqbtcupid/lua_hotupdate
Lua 5.1.Using a fake
ENV
, the module's init statements result in noop.
About
Lua 5.2/5.3 hotfix. Hot update functions and keep old data.