PDA

View Full Version : Fixed and improved strict/safety.lua



Ian
02-12-2007, 09:30 PM
After a lot of agonizing I figured out how to make the damn thing work without crashing and without the user having to intervene. Yay. :mad:



-- Requires that all global variables be assigned a value (even if nil) in the
-- global scope ("main chunk") before it may be read.

IGNORE = 1
WARNING = 2
ERROR = 3

safety = {internalException = IGNORE,
readUndefinedGlobal = ERROR,
writeUndefinedGlobal = ERROR}

do
local emptyTraceback = "\nstack traceback:"
local stackLevel = 3

function Raise(level, err, ...)
if level == IGNORE then
return
end
local traceback = debug.traceback("", stackLevel)
if traceback == emptyTraceback then
Raise(safety.internalException, err, ...)
elseif level == WARNING then
print(string.format(err, ...), traceback, stackLevel)
elseif level == ERROR then
error(string.format(err, ...), stackLevel)
else
Raise(ERROR, "Invalid error level.")
end
end
end

do
-- Get the metatable for the global namespace.
local globalMetatable = getmetatable(_G)

-- Augment the metatable if it already exists; otherwise make a new one.
if globalMetatable == nil then
globalMetatable = {}
setmetatable(_G, globalMetatable)
end

globalMetatable.__declared = {}

-- Deletes a global variable.
function Delete(variable)
rawset(_G, variable, nil)
globalMetatable.__declared[variable] = nil
end

function Exists(variable)
return globalMetatable.__declared[variable]
end

-- Invoked whenever a global variable is read.
function globalMetatable:__index(variable)
if not Exists(variable)
-- C functions should be allowed to do weird stuff, but for some
-- reason accessing this stack frame causes a crash. Uncomment the
-- following line if this has changed.
--[[and debug.getinfo(2, "S").what ~= "C"--]] then
Raise(safety.readUndefinedGlobal,
"Variable '%s' is not defined.", variable)
end
return rawget(self, variable)
end

-- Invoked whenever a global variable is created.
function globalMetatable:__newindex(variable, value)
if not Exists(variable) then
local what = debug.getinfo(2, "S").what
-- Assignment to undefined variables is allowed in the global
-- namespace ("main chunk") and C functions.
if what ~= "main" and what ~= "C" then
if type(value) ~= "function" then
Raise(safety.writeUndefinedGlobal,
"Assignment to undefined variable '%s'.", variable)
else
Raise(safety.writeUndefinedGlobal,
"Inner function '%s' should be made local.",
variable)
end
end
globalMetatable.__declared[variable] = true
end
rawset(self, variable, value)
end
end

Seth
02-13-2007, 04:15 AM
Very nice, I had to do a few hacks here and there to get the engine to behave but now I think everything is running smoothly, yay!! :D Will be in the next update.