- 给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
```lua
---检查未声明的全局变量的使用情况(防止不声明就使用变量)
--
-- 所有全局变量必须在主chunk中通过常规赋值声明(即使赋值为nil也可以),
--然后才能在任何位置使用 或 在函数内部使用。
--元表中的 __newindex 和 __index 元方法 不会受该模块的影响
--
-- 通过使用strict.module可以给任何table设置strict行为。
--如果不想让该模块支持monkey-patching,那么使用strict.closed_module设置该模块
--使其不能添加monkey-patching(猴子补丁:运行时候能动态修改),
--
-- 如果定义了全局变量 PENLIGHT_NO_GLOBAL_STRICT,那么该模块 不会 让全局变量(_G)变的strict
--如果只想通过显式来设置table的strict
--
-- @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
--- 给table设置strict(保持原有的方法)
-- @string name - 给table的一个名称,不代表table变量的名字,而是调用该函数时给table的标识符 (可选) <-- name of table (optional)
-- @tab[opt] mod table - 如果为nil,那么将返回一个新table;不为nil,则为table的变量名 <-- if nil then we'll return a new table
-- @tab[opt] predeclared - 在该table中提前进行变量声明, <-- table of variables that are to be considered predeclared.
-- 一般为__global和__closed
-- @return - 返回给定的 table 或 新table <-- 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 --如果mod为一个table
mt = getmetatable(mod) --获得mod所对应的元表
if mt and rawget(mt,'__declared') then return end -- 如果mod有元表,且__declared不为nil,代表该模块已添加,则返回;
--__declared用于检查该table是否是第一次设置,如果不是,那么__declared不会为nil
else
mod = {}
end
--给mod设置元表mt
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,用于储存已声明过的变量
mt.__declared = predeclared or {}
--设置__newindex元方法,用于新变量赋值 <- 只有在主chunk中才能够赋值(可以赋nil)
mt.__newindex = function(t, n, v)
--用于保持 已有的元方法 不受该模块影响
if old_newindex then
old_newindex(t, n, v) --使用旧的__newindex,对进行赋值
if rawget(t,n)~=nil then return end --能够成功赋值,则返回
end
--旧的 元方法 为 nil 或 无效
if not mt.__declared[n] then --如果 不在 该变量不在__declared中
print("this is newindex")
if global then
local w = what() --如果在主chunk声明全局变量,则能够成功赋值
if w ~= "main" and w ~= "C" then
error("assign to undeclared global '"..n.."'", 2)
end
end
mt.__declared[n] = true --保存在辅助table __declare中,使用辅助table,创建变量(为nil也会保存)
end
rawset(t, n, v) --绕过原方法保存在table中
end
--设置__index元方法
mt.__index = function(t,n)
if not mt.__declared[n] and what() ~= "C" then --如果 变量未声明 且 不在C代码 中
--用于保持 已有的元方法 不受该模块影响
if old_index then
--如果原__index为table,则直接从table中获取
if old_index_type == "table" then
local fallback = old_index[n]
if fallback ~= nil then
return fallback --在原__index的table存在
end
--原__index为function
else
local res = old_index(t, n) --使用原__index的元方法获取
if res then return res end --获取成功,则返回
end
end
--旧的 元方法 为 nil 或 无效
print("this is index")
local msg = "variable '"..n.."' is not declared"
if name then
msg = msg .. " in '"..name.."'"
end
error(msg, 2) --返回错误信息
end
return rawget(t, n) --如果在__declared辅助table中,则返回t[n]
end
return mod
end
--- 让table中所有的table都设置strict
--使用strict.make_all_strict(_G)函数防止对任何全局table添加monkey-patching(进行修补)
-- @tab T
function strict.make_all_strict (T)
--将table T中除了自己外所有table加上strict模块
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.
---将原table添加新变量的功能关闭,返回一个新table,添加到原table中的变量会添加到新table中
---原table的添加新变量会到新table中
function strict.closed_module (mod,name)
local M = {}
mod = mod or {} --如果mod为空,则创建空的table
local mt = getmetatable(mod) --获得mod的元表
--mod没有元表,则创建,添加元表
if not mt then
mt = {}
setmetatable(mod,mt)
end
--设置__newindex元方法
mt.__newindex = function(t,k,v)
M[k] = v
end
return strict.module(name,M)
end
--给全局变量table设置strict模块
if not rawget(_G,'PENLIGHT_NO_GLOBAL_STRICT') then
strict.module(nil,_G,{_PROMPT=true,__global=true})
end
return strict