SaferLua Controller: Lua error messages output improved

This commit is contained in:
Joachim Stolberg 2019-01-19 20:22:26 +01:00
parent 42ea101b15
commit c0b30d88b9
5 changed files with 88 additions and 33 deletions

@ -1,6 +1,21 @@
# Release Notes for ModPack TechPack [techpack] # Release Notes for ModPack TechPack [techpack]
## V2.00.03 (2019-01-19)
### Additions
### Removals
### Changes
- SaferLua-Controller: Lua error messages output improved
- SmartLine Display: row 0 can be used to set the infotext
### Fixes
## V2.00.02 (2019-01-15) ## V2.00.02 (2019-01-15)
### Additions ### Additions

@ -12,10 +12,9 @@
]]-- ]]--
safer_lua.MaxCodeSize = 2000 -- size in length of byte code safer_lua.MaxCodeSize = 5000 -- size if source code in bytes
safer_lua.MaxTableSize = 1000 -- sum over all table sizes safer_lua.MaxTableSize = 1000 -- sum over all table sizes
local function memsize() local function memsize()
return safer_lua.MaxTableSize return safer_lua.MaxTableSize
end end
@ -60,14 +59,31 @@ function safer_lua.config(max_code_size, max_table_size)
safer_lua.MaxTableSize = max_table_size safer_lua.MaxTableSize = max_table_size
end end
local function format_error(err, tab) local function format_error_str(str, label)
local tbl = {}
for s in str:gmatch("[^\r\n]+") do
s = s:match("^%s*(.-)%s*$")
if s:find("function 'xpcall'") then
break
elseif s:find(".-%.lua:%d+:(.+)") then
local err = s:gsub(".-%.lua:%d+:%s*(.+)", "extern: %1")
table.insert(tbl, err)
elseif s:find('%[string ".-"%]') then
local line, err = s:match('^%[string ".-"%]:(%d+): (.+)$')
table.insert(tbl, label..":"..line..": "..err)
elseif s:find('%(load%):(%d+):') then
local line, err = s:match('%(load%):(%d+): (.+)$')
table.insert(tbl, label..":"..line..": "..err)
end
end
return "Error: "..table.concat(tbl, "\n >> ")
end
local function format_error(err, label)
if err:find("stack overflow") then if err:find("stack overflow") then
return "Error: Stack overflow due to recursive function calls!" return "Error: Stack overflow due to recursive function calls!"
end end
err = err:gsub('%[string "%-%-.-"%]:', "in "..tab.." line ") return format_error_str(err, label)
err = err:gsub('in main chunk.+', "")
err = err:gsub('%.%.%..-:%d+:', "Error")
return err
end end
local function compile(pos, text, label, err_clbk) local function compile(pos, text, label, err_clbk)
@ -75,8 +91,7 @@ local function compile(pos, text, label, err_clbk)
text = text:gsub("%$", "S:") text = text:gsub("%$", "S:")
local code, err = loadstring(text) local code, err = loadstring(text)
if not code then if not code then
err = err:gsub("%(load%):", label) err_clbk(pos, format_error(err, label))
err_clbk(pos, err)
else else
return code return code
end end
@ -87,15 +102,11 @@ end
-- Standard init/loop controller -- Standard init/loop controller
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
function safer_lua.init(pos, init, loop, environ, err_clbk) function safer_lua.init(pos, init, loop, environ, err_clbk)
if #init > safer_lua.MaxCodeSize then if (#init + #loop) > safer_lua.MaxCodeSize then
err_clbk(pos, "init() Code size limit exceeded") err_clbk(pos, "Error: Code size limit exceeded")
return return
end end
if #loop > safer_lua.MaxCodeSize then local code = compile(pos, init, "init", err_clbk, 0)
err_clbk(pos, "loop() Code size limit exceeded")
return
end
local code = compile(pos, init, "init() ", err_clbk)
if code then if code then
local env = table.copy(BASE_ENV) local env = table.copy(BASE_ENV)
env.S = {} env.S = {}
@ -103,10 +114,10 @@ function safer_lua.init(pos, init, loop, environ, err_clbk)
setfenv(code, env) setfenv(code, env)
local res, err = xpcall(code, debug.traceback) local res, err = xpcall(code, debug.traceback)
if not res then if not res then
err_clbk(pos, format_error(err, "init()")) err_clbk(pos, format_error(err, "init"))
else else
env = getfenv(code) env = getfenv(code)
code = compile(pos, loop, "loop() ", err_clbk) code = compile(pos, loop, "loop", err_clbk)
if code then if code then
setfenv(code, env) setfenv(code, env)
return code return code
@ -126,11 +137,11 @@ function safer_lua.run_loop(pos, elapsed, code, err_clbk)
end end
local res, err = xpcall(code, debug.traceback) local res, err = xpcall(code, debug.traceback)
if calc_used_mem_size(env) > safer_lua.MaxTableSize then if calc_used_mem_size(env) > safer_lua.MaxTableSize then
err_clbk(pos, "Memory limit exceeded") err_clbk(pos, "Error: Data memory limit exceeded")
return false return false
end end
if not res then if not res then
err_clbk(pos, format_error(err, "loop()")) err_clbk(pos, format_error(err, "loop"))
return false return false
end end
return true return true
@ -141,15 +152,14 @@ end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
local function thread(pos, code, err_clbk) local function thread(pos, code, err_clbk)
while true do while true do
local res, err = pcall(code) local res, err = xpcall(code, debug.traceback)
if not res then if not res then
err = err:gsub("%[string .+%]:", "loop() ") err_clbk(pos, format_error(err, "loop"))
err_clbk(pos, err)
return false return false
end end
local env = getfenv(code) local env = getfenv(code)
if calc_used_mem_size(env) > safer_lua.MaxTableSize then if calc_used_mem_size(env) > safer_lua.MaxTableSize then
err_clbk(pos, "Memory limit exceeded") err_clbk(pos, "Error: Memory limit exceeded")
return false return false
end end
coroutine.yield() coroutine.yield()
@ -164,8 +174,7 @@ end
function safer_lua.co_resume(pos, co, code, err_clbk) function safer_lua.co_resume(pos, co, code, err_clbk)
local res, err = coroutine.resume(co, pos, code, err_clbk) local res, err = coroutine.resume(co, pos, code, err_clbk)
if not res then if not res then
err = err:gsub("%[string .+%]:", "loop() ") err_clbk(pos, format_error(err, "loop"))
err_clbk(pos, err)
return false return false
end end
return true return true

@ -105,15 +105,15 @@ function safer_lua:check(pos, text, label, err_clbk)
if token == "for" then if token == "for" then
-- invalid for statement? -- invalid for statement?
if lToken[idx + 3] ~= "in" or lToken[idx + 5] ~= "next" then if lToken[idx + 3] ~= "in" or lToken[idx + 5] ~= "next" then
err_clbk(pos, label..lineno..": Invalid use of 'for'") err_clbk(pos, label..":"..lineno..": Invalid use of 'for'")
errno = errno + 1 errno = errno + 1
end end
else else
err_clbk(pos, label..lineno..": Invalid keyword '"..token.."'") err_clbk(pos, label..":"..lineno..": Invalid keyword '"..token.."'")
errno = errno + 1 errno = errno + 1
end end
elseif InvalidChars[token] then elseif InvalidChars[token] then
err_clbk(pos, label..lineno..": Invalid character '"..token.."'") err_clbk(pos, label..":"..lineno..": Invalid character '"..token.."'")
errno = errno + 1 errno = errno + 1
end end
end end

@ -205,7 +205,7 @@ local function formspec4(meta)
default.gui_bg_img.. default.gui_bg_img..
default.gui_slots.. default.gui_slots..
"tabheader[0,0;tab;init,func,loop,outp,notes,help;4;;true]".. "tabheader[0,0;tab;init,func,loop,outp,notes,help;4;;true]"..
"table[0.2,0.2;9.5,7;output;"..output..";]".. "table[0.2,0.2;9.5,7;output;"..output..";200]"..
"button[4.4,7.5;1.8,1;clear;Clear]".. "button[4.4,7.5;1.8,1;clear;Clear]"..
"button[6.3,7.5;1.8,1;update;Update]".. "button[6.3,7.5;1.8,1;update;Update]"..
"button[8.2,7.5;1.8,1;"..cmnd.."]" "button[8.2,7.5;1.8,1;"..cmnd.."]"
@ -236,9 +236,31 @@ local function formspec6(items, pos, text)
"textarea[0.3,1.3;10,8;help;Help:;"..text.."]" "textarea[0.3,1.3;10,8;help;Help:;"..text.."]"
end end
local function patch_error_string(err, line_offs)
local tbl = {}
for s in err:gmatch("[^\r\n]+") do
if s:find("loop:(%d+):") then
local prefix, line, err = s:match("(.+)loop:(%d+):(.+)")
if tonumber(line) < line_offs then
table.insert(tbl, prefix.."func:"..line..":"..err)
else
line = tonumber(line) - line_offs
table.insert(tbl, prefix.."loop:"..line..":"..err)
end
else
table.insert(tbl, s)
end
end
return table.concat(tbl, "\n")
end
local function error(pos, err) local function error(pos, err)
output(pos, err)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local func = meta:get_string("func")
local _,line_offs = string.gsub(func, "\n", "\n")
line_offs = line_offs + 1
err = patch_error_string(err, line_offs)
output(pos, err)
local number = meta:get_string("number") local number = meta:get_string("number")
meta:set_string("infotext", "Controller "..number..": error") meta:set_string("infotext", "Controller "..number..": error")
meta:set_int("state", tubelib.STOPPED) meta:set_int("state", tubelib.STOPPED)
@ -255,7 +277,7 @@ local function compile(pos, meta, number)
local owner = meta:get_string("owner") local owner = meta:get_string("owner")
local env = table.copy(tCommands) local env = table.copy(tCommands)
env.meta = {pos=pos, owner=owner, number=number, error=error} env.meta = {pos=pos, owner=owner, number=number, error=error}
local code = safer_lua.init(pos, init .."\n".. func, loop, env, error) local code = safer_lua.init(pos, init, func.."\n"..loop, env, error)
if code then if code then
Cache[number] = {code=code, inputs={}} Cache[number] = {code=code, inputs={}}
@ -457,6 +479,10 @@ minetest.register_node("sl_controller:controller", {
meta:set_string("func", "-- for your functions") meta:set_string("func", "-- for your functions")
meta:set_string("loop", "-- called every second") meta:set_string("loop", "-- called every second")
meta:set_string("notes", "For your notes / snippets") meta:set_string("notes", "For your notes / snippets")
meta:mark_as_private("init")
meta:mark_as_private("func")
meta:mark_as_private("loop")
meta:mark_as_private("notes")
meta:set_string("formspec", formspec1(meta)) meta:set_string("formspec", formspec1(meta))
meta:set_string("infotext", "Controller "..number..": stopped") meta:set_string("infotext", "Controller "..number..": stopped")
end, end,

@ -108,8 +108,13 @@ end
local function write_row(meta, payload) local function write_row(meta, payload)
local text = meta:get_string("text") local text = meta:get_string("text")
if type(payload) == "table" then if type(payload) == "table" then
local row = tonumber(payload.row) or 1 local row = tonumber(payload.row) or 0
if row > 9 then row = 9 end
local str = payload.str or "oops" local str = payload.str or "oops"
if row == 0 then
meta:set_string("infotext", str)
return
end
local rows local rows
if meta:get_int("startscreen") == 1 then if meta:get_int("startscreen") == 1 then
rows = {} rows = {}