enable "strict mode" in wesnoth lua

Enables an ilua feature called "strict mode" in all of our lua
environments. This change causes lua to report an error if a global
variable is used before it is assigned. The benefits of this are:
- Improved maintainability of lua-based add-ons, since you get
better error reporting in case of a typo.
- Improved behavior of the lua interpreter console, since mistyped
or gibberish lines resolve to an error rather than "nil", which
the console treats as a legal return value.

It is possible to disable this or work around it in individual
scripts. For more info see release notes.
This commit is contained in:
Chris Beck 2014-11-22 15:19:19 -05:00
parent fd68986bc7
commit a65d168317
3 changed files with 73 additions and 35 deletions

View file

@ -192,8 +192,24 @@ Note that:
[section="Lua interpreter console"]
As a debugging aide, a lua interpreter console has been added to the game. At time of writing it is accessible from a button the gamestate inspector, also it may be brought up using hotkey '`' by default.
The lua interpreter console supports command-line history, backed up by the GNU readline library. This is an optional dependency, but if you don't have it you won't be able to use the feature.
[/section]
[section="Compatability breaking changes"]
[list]
[*] By default, all lua in wesnoth is now run using "strict globals". This means that you must assign a value to a global variable before using it, or it is a runtime error. Even assigning "nil" is acceptable. While this is strictly speaking a compatability-breaking change, it is quite uncommon that you would require the default behavior for your script to work, and this change greatly improves the maintainability of lua in add-ons, by giving an error message in case of a programmer typo, rather than pretending that everything is fine. This change also improves the behavior of the lua interpreter console.
The implementation is based on a module called "ilua" (interactive lua) available here: http://lua-users.org/files/wiki_insecure/users/steved/ilua.lua
If you must, you can disable the feature in one of the following ways:
[list]
[*] [code] setmetatable(_G, nil) [/code] to disable, and [code] ilua.set_strict() [/code] to re-enable.
[*] [code] result, err = pcall ( possible_global_var ~= nil ) [/code]
[*] [code] result = rawget(_G, "possible_global_var") [/code]
[/list]
[/list]
[section="Example section 2"]
Example contents 2.
[/section]

View file

@ -4,36 +4,22 @@
-- will try to print out tables recursively, subject to the pretty_print_limit value.
-- Steve Donovan, 2007
-- Adapted by iceiceice for wesnoth, 2014
-- Retrived from: http://lua-users.org/files/wiki_insecure/users/steved/ilua.lua
local pretty_print_limit = 20
local max_depth = 7
local table_clever = true
local prompt = '> '
local verbose = false
local strict = true
-- suppress strict warnings
_ = true
-- imported global functions
local sub = string.sub
local match = string.match
local find = string.find
local push = table.insert
local pop = table.remove
local append = table.insert
local concat = table.concat
local pack = table.pack
local floor = math.floor
local savef
local collisions = {}
local G_LIB = {}
local declared = {}
local line_handler_fn, global_handler_fn
local print_handlers = {}
ilua = {}
local num_prec
local num_all
local jstack = {}
@ -89,13 +75,8 @@ local function join(tbl,delim,limit,depth)
return sub(res,2)
end
function val2str(val)
local function val2str(val)
local tp = type(val)
if print_handlers[tp] then
local s = print_handlers[tp](val)
return s or '?'
end
if tp == 'function' then
return tostring(val)
elseif tp == 'table' then
@ -107,23 +88,55 @@ function val2str(val)
elseif tp == 'string' then
return "'"..val.."'"
elseif tp == 'number' then
-- we try only to apply floating-point precision for numbers deemed to be floating-point,
-- unless the 3rd arg to precision() is true.
if num_prec and (num_all or floor(val) ~= val) then
return num_prec:format(val)
else
return tostring(val)
end
-- removed numeric precision features, but we might actually want these... might put them back
return tostring(val)
else
return tostring(val)
end
end
function _pretty_print(...)
arg = table.pack(...)
local function _pretty_print(...)
local arg = pack(...)
for i,val in ipairs(arg) do
print(val2str(val))
end
_G['_'] = arg[1]
end
--
-- strict.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.
--
local function set_strict()
local mt = getmetatable(_G)
if mt == nil then
mt = {}
setmetatable(_G, mt)
end
local function what ()
local d = debug.getinfo(3, "S")
return d and d.what or "C"
end
mt.__newindex = function (t, n, v)
declared[n] = true
rawset(t, n, v)
end
mt.__index = function (t, n)
if not declared[n] and what() ~= "C" then
error("variable '"..n.."' must be assigned before being used", 2)
end
return rawget(t, n)
end
end
return {
val2str = val2str,
_pretty_print = _pretty_print,
set_strict = set_strict
}

View file

@ -205,8 +205,8 @@ lua_kernel_base::lua_kernel_base(CVideo * video)
while(lua_next(L, -2) != 0) {
lua_pop(L, 1);
char const* function = lua_tostring(L, -1);
if(strcmp(function, "traceback") == 0) continue;
lua_pushnil(L);
if(strcmp(function, "traceback") == 0 || strcmp(function, "getinfo") == 0) continue; //traceback is needed for our error handler
lua_pushnil(L); //getinfo is needed for ilua strict mode
lua_setfield(L, -3, function);
}
lua_pop(L, 1);
@ -350,7 +350,16 @@ lua_kernel_base::lua_kernel_base(CVideo * video)
lua_pushstring(L, "lua/ilua.lua");
int result = lua_fileops::intf_require(L);
if (result == 1) {
lua_setglobal(L, "ilua");
//run "ilua.set_strict()"
lua_pushstring(L, "set_strict");
lua_gettable(L, -2);
if (!protected_call(0,0, boost::bind(&lua_kernel_base::log_error, this, _1, _2))) {
cmd_log_ << "Failed to activate strict mode.\n";
} else {
cmd_log_ << "Activated strict mode.\n";
}
lua_setglobal(L, "ilua"); //save ilua table as a global
} else {
cmd_log_ << "Error: failed to load ilua.\n";
}
@ -478,7 +487,7 @@ void lua_kernel_base::run(const char * prog) {
// Tests if a program resolves to an expression, and pretty prints it if it is, otherwise it runs it normally. Throws exceptions.
void lua_kernel_base::interactive_run(char const * prog) {
std::string experiment = "_pretty_print(";
std::string experiment = "ilua._pretty_print(";
experiment += prog;
experiment += ")";