module_updater.lua 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. --- Updater to update loaded module.
  2. -- Updating a table is to update metatable and sub-table of the old table,
  3. -- and to update functions of the old table, keeping value fields.
  4. -- Updating a function is to copy upvalues of old function to new function.
  5. -- Functions will be replaced later after updating.
  6. local M = {}
  7. local update_table
  8. local update_func
  9. -- Updated signature set to prevent self-reference dead loop.
  10. local updated_sig = {}
  11. -- Map old function to new functions.
  12. local updated_func_map = {}
  13. -- Do not update and replace protected objects.
  14. -- Set to hotfix.protected.
  15. local protected = {}
  16. -- Set to hotfix.log_debug.
  17. function M.log_debug(msg_str)
  18. end
  19. local tostring = _tostring or tostring
  20. -- Check if function or table has been updated. Return true if updated.
  21. local function check_updated(new_obj, old_obj, name, deep)
  22. local signature = string.format("new(%s) old(%s)", tostring(new_obj), tostring(old_obj))
  23. M.log_debug(string.format("%sUpdate %s: %s", deep, name, signature))
  24. if new_obj == old_obj then
  25. M.log_debug(deep .. " Same")
  26. return true
  27. end
  28. if updated_sig[signature] then
  29. M.log_debug(deep .. " Already updated")
  30. return true
  31. end
  32. updated_sig[signature] = true
  33. return false
  34. end
  35. -- Update new function with upvalues of old function.
  36. -- Parameter name and deep are only for log.
  37. function update_func(new_func, old_func, name, deep)
  38. assert("function" == type(new_func))
  39. assert("function" == type(old_func))
  40. if protected[old_func] then
  41. return
  42. end
  43. if check_updated(new_func, old_func, name, deep) then
  44. return
  45. end
  46. deep = deep .. " "
  47. updated_func_map[old_func] = new_func
  48. -- Get upvalues of old function.
  49. local old_upvalue_map = {}
  50. for i = 1, math.huge do
  51. local name, value = debug.getupvalue(old_func, i)
  52. if not name then
  53. break
  54. end
  55. old_upvalue_map[name] = value
  56. end
  57. local function log_dbg(name, from, to)
  58. M.log_debug(string.format("%ssetupvalue %s: (%s) -> (%s)", deep, name, tostring(from), tostring(to)))
  59. end
  60. -- Update new upvalues with old.
  61. for i = 1, math.huge do
  62. local name, value = debug.getupvalue(new_func, i)
  63. if not name then
  64. break
  65. end
  66. local old_value = old_upvalue_map[name]
  67. if old_value then
  68. local type_old_value = type(old_value)
  69. if type_old_value ~= type(value) then
  70. debug.setupvalue(new_func, i, old_value)
  71. log_dbg(name, value, old_value)
  72. elseif type_old_value == "function" then
  73. update_func(value, old_value, name, deep)
  74. elseif type_old_value == "table" then
  75. update_table(value, old_value, name, deep)
  76. debug.setupvalue(new_func, i, old_value)
  77. else
  78. debug.setupvalue(new_func, i, old_value)
  79. log_dbg(name, value, old_value)
  80. end
  81. end -- if old_value
  82. end -- for i
  83. end -- update_func()
  84. -- Compare 2 tables and update the old table. Keep the old data.
  85. function update_table(new_table, old_table, name, deep)
  86. assert("table" == type(new_table))
  87. assert("table" == type(old_table))
  88. if protected[old_table] then
  89. return
  90. end
  91. if check_updated(new_table, old_table, name, deep) then
  92. return
  93. end
  94. deep = deep .. " "
  95. -- Compare 2 tables, and update old table.
  96. for key, value in pairs(new_table) do
  97. local old_value = old_table[key]
  98. local type_value = type(value)
  99. if type_value ~= type(old_value) then
  100. old_table[key] = value
  101. M.log_debug(
  102. string.format("%sUpdate field %s: (%s) -> (%s)", deep, key, tostring(old_value), tostring(value))
  103. )
  104. elseif type_value == "function" then
  105. update_func(value, old_value, key, deep)
  106. elseif type_value == "table" then
  107. update_table(value, old_value, key, deep)
  108. else
  109. old_table[key] = value
  110. end
  111. end -- for
  112. -- Update metatable.
  113. local old_meta = debug.getmetatable(old_table)
  114. local new_meta = debug.getmetatable(new_table)
  115. if type(old_meta) == "table" and type(new_meta) == "table" then
  116. update_table(new_meta, old_meta, name .. "'s Meta", deep)
  117. end
  118. end -- update_table()
  119. -- Update new loaded object with package.loaded[module_name].
  120. local function update_loaded_module2(module_name, new_obj)
  121. assert(nil ~= new_obj)
  122. assert("string" == type(module_name))
  123. local old_obj = package.loaded[module_name]
  124. local new_type = type(new_obj)
  125. local old_type = type(old_obj)
  126. if new_type == old_type then
  127. if "table" == new_type then
  128. update_table(new_obj, old_obj, module_name, "")
  129. return
  130. end
  131. if "function" == new_type then
  132. update_func(new_obj, old_obj, module_name, "")
  133. return
  134. end
  135. end -- if new_type == old_type
  136. M.log_debug(string.format("Directly replace module: old(%s) -> new(%s)", tostring(old_obj), tostring(new_obj)))
  137. package.loaded[module_name] = new_obj
  138. end -- update_loaded_module2()
  139. -- Update new loaded object with package.loaded[module_name].
  140. -- Return an updated function map (updated_func_map).
  141. -- new_module_obj is the newly loaded module object.
  142. function M.update_loaded_module(module_name, protected_objects, new_module_obj)
  143. assert(type(module_name) == "string")
  144. assert(type(protected_objects) == "table")
  145. protected = protected_objects
  146. updated_func_map = {}
  147. updated_sig = {}
  148. update_loaded_module2(module_name, new_module_obj)
  149. updated_sig = {}
  150. return updated_func_map
  151. end -- update_loaded_module()
  152. return M