- 给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.strictrequire 'debug' -- for Lua 5.2local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawgetlocal 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 tablefunction strict.module (name,mod,predeclared)local mt, old_newindex, old_index, old_index_type, global, closedif predeclared thenglobal = predeclared.__globalclosed = predeclared.__closedendif type(mod) == 'table' then --如果mod为一个tablemt = getmetatable(mod) --获得mod所对应的元表if mt and rawget(mt,'__declared') then return end -- 如果mod有元表,且__declared不为nil,代表该模块已添加,则返回;--__declared用于检查该table是否是第一次设置,如果不是,那么__declared不会为nilelsemod = {}end--给mod设置元表mtif mt == nil thenmt = {}setmetatable(mod, mt)elseold_newindex = mt.__newindex --保存旧的元方法,闭包old_index = mt.__indexold_index_type = type(old_index)end---设置元表mt的内容--设置__declared,用于储存已声明过的变量mt.__declared = predeclared or {}--设置__newindex元方法,用于新变量赋值 <- 只有在主chunk中才能够赋值(可以赋nil)mt.__newindex = function(t, n, v)--用于保持 已有的元方法 不受该模块影响if old_newindex thenold_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 thenlocal w = what() --如果在主chunk声明全局变量,则能够成功赋值if w ~= "main" and w ~= "C" thenerror("assign to undeclared global '"..n.."'", 2)endendmt.__declared[n] = true --保存在辅助table __declare中,使用辅助table,创建变量(为nil也会保存)endrawset(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" thenlocal fallback = old_index[n]if fallback ~= nil thenreturn fallback --在原__index的table存在end--原__index为functionelselocal res = old_index(t, n) --使用原__index的元方法获取if res then return res end --获取成功,则返回endend--旧的 元方法 为 nil 或 无效print("this is index")local msg = "variable '"..n.."' is not declared"if name thenmsg = msg .. " in '"..name.."'"enderror(msg, 2) --返回错误信息endreturn rawget(t, n) --如果在__declared辅助table中,则返回t[n]endreturn modend--- 让table中所有的table都设置strict--使用strict.make_all_strict(_G)函数防止对任何全局table添加monkey-patching(进行修补)-- @tab Tfunction strict.make_all_strict (T)--将table T中除了自己外所有table加上strict模块for k,v in pairs(T) doif type(v) == 'table' and v ~= T thenstrict.module(k,v)endendend--- 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为空,则创建空的tablelocal mt = getmetatable(mod) --获得mod的元表--mod没有元表,则创建,添加元表if not mt thenmt = {}setmetatable(mod,mt)end--设置__newindex元方法mt.__newindex = function(t,k,v)M[k] = vendreturn strict.module(name,M)end--给全局变量table设置strict模块if not rawget(_G,'PENLIGHT_NO_GLOBAL_STRICT') thenstrict.module(nil,_G,{_PROMPT=true,__global=true})endreturn strict
