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:
parent
fd68986bc7
commit
a65d168317
3 changed files with 73 additions and 35 deletions
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 += ")";
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue