• 给table使用strict.module(nil,table,nil)函数,
    • 防止不提前声明访问table中的变量
    • 导入该模块自动给全局变量table _G使用
    • 使用strict.closed_module(table)关闭添加新变量 ```lua —- Checks uses of undeclared global variables. — All global variables must be ‘declared’ through a regular assignment — (even assigning nil will do) in a main chunk before being used — anywhere or assigned to inside a function. Existing metatables newindex and index

      — metamethods are respected.

      — You can set any table to have strict behaviour using strict.module. Creating a new — module with strict.closed_module makes the module immune to monkey-patching, if

      — you don’t wish to encourage monkey business.

      — If the global PENLIGHT_NO_GLOBAL_STRICT is defined, then this module won’t make the

      — global environment strict - if you just want to explicitly set table strictness.

      — @module pl.strict

require ‘debug’ — for Lua 5.2 local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget local strict = {}

local function what () local d = getinfo(3, “S”) return d and d.what or “C” end

—- make an existing table strict. — @string name name of table (optional) — @tab[opt] mod table - if nil then we’ll return a new table — @tab[opt] predeclared - table of variables that are to be considered predeclared. — @return the given table, or a new table function strict.module (name,mod,predeclared) local mt, old_newindex, old_index, old_index_type, global, closed if predeclared then global = predeclared.global closed = predeclared.closed end if type(mod) == ‘table’ then mt = getmetatable(mod) if mt and rawget(mt,’declared’) then return end — already patched… else mod = {} end if mt == nil then mt = {} setmetatable(mod, mt) else old_newindex = mt.newindex old_index = mt.index old_index_type = type(old_index) end mt.declared = predeclared or {} mt.newindex = function(t, n, v) if old_newindex then old_newindex(t, n, v) if rawget(t,n)~=nil then return end end if not mt.declared[n] then if global then local w = what() if w ~= “main” and w ~= “C” then error(“assign to undeclared global ‘“..n..”‘“, 2) end end mt.declared[n] = true end rawset(t, n, v) end mt.index = function(t,n) if not mt.__declared[n] and what() ~= “C” then if old_index then if old_index_type == “table” then local fallback = old_index[n] if fallback ~= nil then return fallback end else local res = old_index(t, n) if res then return res end end end local msg = “variable ‘“..n..”‘ is not declared” if name then msg = msg .. “ in ‘“..name..”‘“ end error(msg, 2) end return rawget(t, n) end return mod end

—- make all tables in a table strict. — So strict.make_all_strict(_G) prevents monkey-patching — of any global table — @tab T function strict.make_all_strict (T) for k,v in pairs(T) do if type(v) == ‘table’ and v ~= T then strict.module(k,v) end end end

—- make a new module table which is closed to further changes. function strict.closed_module (mod,name) local M = {} mod = mod or {} local mt = getmetatable(mod) if not mt then mt = {} setmetatable(mod,mt) end mt.__newindex = function(t,k,v) M[k] = v end return strict.module(name,M) end

if not rawget(_G,’PENLIGHT_NO_GLOBAL_STRICT’) then strict.module(nil,_G,{_PROMPT=true,__global=true}) end

return strict

  1. ```lua
  2. ---检查未声明的全局变量的使用情况(防止不声明就使用变量)
  3. --
  4. -- 所有全局变量必须在主chunk中通过常规赋值声明(即使赋值为nil也可以),
  5. --然后才能在任何位置使用 或 在函数内部使用。
  6. --元表中的 __newindex 和 __index 元方法 不会受该模块的影响
  7. --
  8. -- 通过使用strict.module可以给任何table设置strict行为。
  9. --如果不想让该模块支持monkey-patching,那么使用strict.closed_module设置该模块
  10. --使其不能添加monkey-patching(猴子补丁:运行时候能动态修改),
  11. --
  12. -- 如果定义了全局变量 PENLIGHT_NO_GLOBAL_STRICT,那么该模块 不会 让全局变量(_G)变的strict
  13. --如果只想通过显式来设置table的strict
  14. --
  15. -- @module pl.strict
  16. require 'debug' -- for Lua 5.2
  17. local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget
  18. local strict = {}
  19. local function what ()
  20. local d = getinfo(3, "S")
  21. return d and d.what or "C"
  22. end
  23. --- 给table设置strict(保持原有的方法)
  24. -- @string name - 给table的一个名称,不代表table变量的名字,而是调用该函数时给table的标识符 (可选) <-- name of table (optional)
  25. -- @tab[opt] mod table - 如果为nil,那么将返回一个新table;不为nil,则为table的变量名 <-- if nil then we'll return a new table
  26. -- @tab[opt] predeclared - 在该table中提前进行变量声明, <-- table of variables that are to be considered predeclared.
  27. -- 一般为__global和__closed
  28. -- @return - 返回给定的 table 或 新table <-- return the given table, or a new table
  29. function strict.module (name,mod,predeclared)
  30. local mt, old_newindex, old_index, old_index_type, global, closed
  31. if predeclared then
  32. global = predeclared.__global
  33. closed = predeclared.__closed
  34. end
  35. if type(mod) == 'table' then --如果mod为一个table
  36. mt = getmetatable(mod) --获得mod所对应的元表
  37. if mt and rawget(mt,'__declared') then return end -- 如果mod有元表,且__declared不为nil,代表该模块已添加,则返回;
  38. --__declared用于检查该table是否是第一次设置,如果不是,那么__declared不会为nil
  39. else
  40. mod = {}
  41. end
  42. --给mod设置元表mt
  43. if mt == nil then
  44. mt = {}
  45. setmetatable(mod, mt)
  46. else
  47. old_newindex = mt.__newindex --保存旧的元方法,闭包
  48. old_index = mt.__index
  49. old_index_type = type(old_index)
  50. end
  51. ---设置元表mt的内容
  52. --设置__declared,用于储存已声明过的变量
  53. mt.__declared = predeclared or {}
  54. --设置__newindex元方法,用于新变量赋值 <- 只有在主chunk中才能够赋值(可以赋nil)
  55. mt.__newindex = function(t, n, v)
  56. --用于保持 已有的元方法 不受该模块影响
  57. if old_newindex then
  58. old_newindex(t, n, v) --使用旧的__newindex,对进行赋值
  59. if rawget(t,n)~=nil then return end --能够成功赋值,则返回
  60. end
  61. --旧的 元方法 为 nil 或 无效
  62. if not mt.__declared[n] then --如果 不在 该变量不在__declared中
  63. print("this is newindex")
  64. if global then
  65. local w = what() --如果在主chunk声明全局变量,则能够成功赋值
  66. if w ~= "main" and w ~= "C" then
  67. error("assign to undeclared global '"..n.."'", 2)
  68. end
  69. end
  70. mt.__declared[n] = true --保存在辅助table __declare中,使用辅助table,创建变量(为nil也会保存)
  71. end
  72. rawset(t, n, v) --绕过原方法保存在table中
  73. end
  74. --设置__index元方法
  75. mt.__index = function(t,n)
  76. if not mt.__declared[n] and what() ~= "C" then --如果 变量未声明 且 不在C代码 中
  77. --用于保持 已有的元方法 不受该模块影响
  78. if old_index then
  79. --如果原__index为table,则直接从table中获取
  80. if old_index_type == "table" then
  81. local fallback = old_index[n]
  82. if fallback ~= nil then
  83. return fallback --在原__index的table存在
  84. end
  85. --原__index为function
  86. else
  87. local res = old_index(t, n) --使用原__index的元方法获取
  88. if res then return res end --获取成功,则返回
  89. end
  90. end
  91. --旧的 元方法 为 nil 或 无效
  92. print("this is index")
  93. local msg = "variable '"..n.."' is not declared"
  94. if name then
  95. msg = msg .. " in '"..name.."'"
  96. end
  97. error(msg, 2) --返回错误信息
  98. end
  99. return rawget(t, n) --如果在__declared辅助table中,则返回t[n]
  100. end
  101. return mod
  102. end
  103. --- 让table中所有的table都设置strict
  104. --使用strict.make_all_strict(_G)函数防止对任何全局table添加monkey-patching(进行修补)
  105. -- @tab T
  106. function strict.make_all_strict (T)
  107. --将table T中除了自己外所有table加上strict模块
  108. for k,v in pairs(T) do
  109. if type(v) == 'table' and v ~= T then
  110. strict.module(k,v)
  111. end
  112. end
  113. end
  114. --- make a new module table which is closed to further changes.
  115. ---将原table添加新变量的功能关闭,返回一个新table,添加到原table中的变量会添加到新table中
  116. ---原table的添加新变量会到新table中
  117. function strict.closed_module (mod,name)
  118. local M = {}
  119. mod = mod or {} --如果mod为空,则创建空的table
  120. local mt = getmetatable(mod) --获得mod的元表
  121. --mod没有元表,则创建,添加元表
  122. if not mt then
  123. mt = {}
  124. setmetatable(mod,mt)
  125. end
  126. --设置__newindex元方法
  127. mt.__newindex = function(t,k,v)
  128. M[k] = v
  129. end
  130. return strict.module(name,M)
  131. end
  132. --给全局变量table设置strict模块
  133. if not rawget(_G,'PENLIGHT_NO_GLOBAL_STRICT') then
  134. strict.module(nil,_G,{_PROMPT=true,__global=true})
  135. end
  136. return strict