first commit
commit
1c4a388a1f
|
@ -0,0 +1,364 @@
|
|||
--[[
|
||||
|
||||
SaferLua [safer_lua]
|
||||
====================
|
||||
|
||||
Copyright (C) 2018 Joachim Stolberg
|
||||
|
||||
LGPLv2.1+
|
||||
See LICENSE.txt for more information
|
||||
|
||||
data_struct.lua:
|
||||
|
||||
see https://github.com/joe7575/techpack/wiki/Data-Structures
|
||||
|
||||
]]--
|
||||
|
||||
safer_lua.DataStructHelp = [[
|
||||
Data structures as a secure shell over the LUA table type.
|
||||
see https://github.com/joe7575/techpack/wiki/Data-Structures
|
||||
|
||||
'Arrays' are lists of elements, which can be addressed
|
||||
by means of an index:
|
||||
|
||||
a = Array(1,2,3,4) --> {1,2,3,4}
|
||||
a.add(6) --> {1,2,3,4,6}
|
||||
a.set(2, 8) --> {1,8,3,4,6}
|
||||
a.insert(5,7) --> {1,8,3,4,7,6}
|
||||
a.remove(3) --> {1,8,4,7,6}
|
||||
a.insert(1, "hello") --> {"hello",1,8,4,7,6}
|
||||
a.size() --> function returns 6
|
||||
a.memsize() --> return returns 10
|
||||
a.next() --> for loop iterator function
|
||||
a.sort(reverse) --> sort the array elements in place
|
||||
|
||||
Unlike arrays, which are indexed by a range of numbers,
|
||||
'stores' are indexed by keys:
|
||||
|
||||
s = Store("a",4,"b",5) --> {a = 4, b = 5}
|
||||
s.set("val", 12) --> {a = 4, b = 5, val = 12}
|
||||
s.get("val") --> returns 12
|
||||
s.set(0, "hello") --> {a = 4, b = 5, val = 12, [0] = "hello"}
|
||||
s.del("val") --> {[0] = "hello"}
|
||||
s.size() --> function returns 4
|
||||
s.memsize() --> function returns 8
|
||||
s.next() --> for loop iterator function
|
||||
s.keys(order) --> return an array with the keys
|
||||
|
||||
A 'set' is an unordered collection with no duplicate
|
||||
elements.
|
||||
|
||||
s = Set("Tom", "Lucy")
|
||||
--> {Tom = true, Lucy = true}
|
||||
s.del("Tom") --> {Lucy = true}
|
||||
s.add("Susi") --> {Lucy = true, Susi = true}
|
||||
s.has("Susi") --> function returns `true`
|
||||
s.has("Mike") --> function returns `false`
|
||||
s.size() --> function returns 2
|
||||
s.memsize() --> function returns 8
|
||||
s.next() --> for loop iterator function
|
||||
]]
|
||||
|
||||
local function var_count(v)
|
||||
if type(v) == "number" then
|
||||
return 1
|
||||
elseif type(v) == "boolean" then
|
||||
return 1
|
||||
elseif v == nil then
|
||||
return 0
|
||||
elseif type(v) == "string" then
|
||||
return #v
|
||||
elseif type(v) == "table" then
|
||||
return v.memsize()
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function safer_lua.Store(...)
|
||||
|
||||
local new_t = {}
|
||||
local mt = {}
|
||||
|
||||
local MemSize = 0
|
||||
local Size = 0
|
||||
local Data = {}
|
||||
|
||||
mt.__newindex = function(t, k, v) return end
|
||||
|
||||
mt.count = var_count
|
||||
|
||||
new_t.set = function(k,v)
|
||||
if type(k) == "number" then
|
||||
if rawget(Data, k) then -- has entry?
|
||||
MemSize = MemSize - mt.count(rawget(Data, k))
|
||||
else
|
||||
Size = Size + 1
|
||||
end
|
||||
MemSize = MemSize + mt.count(v)
|
||||
rawset(Data, k, v)
|
||||
elseif type(k) == "string" then
|
||||
if rawget(Data, k) then -- has entry?
|
||||
MemSize = MemSize - mt.count(rawget(Data, k))
|
||||
else
|
||||
MemSize = MemSize + mt.count(k)
|
||||
Size = Size + 1
|
||||
end
|
||||
MemSize = MemSize + mt.count(v)
|
||||
rawset(Data, k, v)
|
||||
end
|
||||
end
|
||||
|
||||
new_t.get = function(k)
|
||||
return rawget(Data, k)
|
||||
end
|
||||
|
||||
new_t.del = function(k)
|
||||
if rawget(Data, k) then -- has entry?
|
||||
MemSize = MemSize - mt.count(k)
|
||||
MemSize = MemSize - mt.count(rawget(Data, k))
|
||||
rawset(Data, k, nil)
|
||||
Size = Size - 1
|
||||
end
|
||||
end
|
||||
|
||||
new_t.memsize = function(t)
|
||||
return MemSize
|
||||
end
|
||||
|
||||
new_t.size = function(t)
|
||||
return Size
|
||||
end
|
||||
|
||||
new_t.next = function(t)
|
||||
local n = nil
|
||||
return function ()
|
||||
n = next(Data, n)
|
||||
if n then return n, Data[n] end
|
||||
end
|
||||
end
|
||||
|
||||
new_t.keys = function(order)
|
||||
local keyset = {}
|
||||
local n = 0
|
||||
local size = 0
|
||||
|
||||
for k,v in pairs(Data) do
|
||||
n = n + 1
|
||||
keyset[n] = k
|
||||
size = size + var_count(k)
|
||||
end
|
||||
|
||||
if order == "up" then
|
||||
table.sort(keyset, function(a,b) return a > b end)
|
||||
elseif order == "down" then
|
||||
table.sort(keyset)
|
||||
end
|
||||
local a = safer_lua.Array()
|
||||
a.__load(size, keyset)
|
||||
return a
|
||||
end
|
||||
|
||||
new_t.__dump = function()
|
||||
-- remove the not serializable meta data
|
||||
return {Type = "Store", Size = Size, MemSize = MemSize, Data = Data}
|
||||
end
|
||||
|
||||
new_t.__load = function(size, memsize, data)
|
||||
Size = size
|
||||
MemSize = memsize
|
||||
Data = data
|
||||
end
|
||||
|
||||
for idx = 1,select('#',...),2 do
|
||||
local k,v = select(idx,...),select(idx+1,...)
|
||||
new_t.set(k,v)
|
||||
end
|
||||
|
||||
return setmetatable(new_t, mt)
|
||||
end
|
||||
|
||||
|
||||
function safer_lua.Array(...)
|
||||
|
||||
local new_t = {}
|
||||
local mt = {}
|
||||
|
||||
local MemSize = 0
|
||||
local Data = {}
|
||||
|
||||
mt.__newindex = function(t, k, v) return end
|
||||
|
||||
mt.count = var_count
|
||||
|
||||
for idx = 1,select('#',...) do
|
||||
local v = select(idx,...)
|
||||
local cnt = mt.count(v)
|
||||
if cnt then
|
||||
MemSize = MemSize + cnt
|
||||
rawset(Data, idx, v)
|
||||
end
|
||||
end
|
||||
|
||||
new_t.add = function(v)
|
||||
MemSize = MemSize + mt.count(v)
|
||||
local i = #Data + 1
|
||||
table.insert(Data, i, v)
|
||||
end
|
||||
|
||||
new_t.set = function(i,v)
|
||||
i = math.min(#Data, i)
|
||||
MemSize = MemSize - mt.count(rawget(Data, i))
|
||||
MemSize = MemSize + mt.count(v)
|
||||
rawset(Data, i, v)
|
||||
end
|
||||
|
||||
new_t.get = function(i)
|
||||
return Data[i]
|
||||
end
|
||||
|
||||
new_t.insert = function(i, v)
|
||||
MemSize = MemSize + mt.count(v)
|
||||
i = math.min(#Data, i)
|
||||
table.insert(Data, i, v)
|
||||
end
|
||||
|
||||
new_t.remove = function(i)
|
||||
local v = table.remove(Data, i)
|
||||
MemSize = MemSize - mt.count(v)
|
||||
return v
|
||||
end
|
||||
|
||||
new_t.sort = function(reverse)
|
||||
if reverse then
|
||||
table.sort(Data, function(a,b) return a > b end)
|
||||
else
|
||||
table.sort(Data)
|
||||
end
|
||||
end
|
||||
|
||||
new_t.memsize = function(t)
|
||||
return MemSize
|
||||
end
|
||||
|
||||
new_t.size = function(t)
|
||||
return #Data
|
||||
end
|
||||
|
||||
new_t.next = function(t)
|
||||
local i = 0
|
||||
local n = #Data
|
||||
return function ()
|
||||
i = i + 1
|
||||
if i <= n then return i, Data[i] end
|
||||
end
|
||||
end
|
||||
|
||||
new_t.__dump = function()
|
||||
-- remove the not serializable meta data
|
||||
return {Type = "Array", MemSize = MemSize, Data = Data}
|
||||
end
|
||||
|
||||
new_t.__load = function(memsize, data)
|
||||
MemSize = memsize
|
||||
Data = data
|
||||
end
|
||||
|
||||
return setmetatable(new_t, mt)
|
||||
end
|
||||
|
||||
|
||||
function safer_lua.Set(...)
|
||||
|
||||
local new_t = {}
|
||||
local mt = {}
|
||||
|
||||
local MemSize = 0
|
||||
local Size = 0
|
||||
local Data = {}
|
||||
|
||||
mt.__newindex = function(t, k, v) return end
|
||||
|
||||
mt.count = var_count
|
||||
|
||||
for idx = 1,select('#',...) do
|
||||
local v = select(idx,...)
|
||||
local cnt = mt.count(v)
|
||||
if cnt then
|
||||
MemSize = MemSize + cnt
|
||||
Size = Size + 1
|
||||
rawset(Data, v, true)
|
||||
end
|
||||
end
|
||||
|
||||
new_t.add = function(k)
|
||||
MemSize = MemSize + mt.count(k)
|
||||
rawset(Data, k, true)
|
||||
Size = Size + 1
|
||||
end
|
||||
|
||||
new_t.del = function(k)
|
||||
MemSize = MemSize - mt.count(k)
|
||||
rawset(Data, k, nil)
|
||||
Size = Size - 1
|
||||
end
|
||||
|
||||
new_t.has = function(k)
|
||||
return rawget(Data, k) == true
|
||||
end
|
||||
|
||||
new_t.memsize = function(t)
|
||||
return MemSize
|
||||
end
|
||||
|
||||
new_t.size = function(t)
|
||||
return Size
|
||||
end
|
||||
|
||||
new_t.next = function(t)
|
||||
local i = 0
|
||||
local n = nil
|
||||
return function ()
|
||||
i = i + 1
|
||||
n = next(Data, n)
|
||||
if n then return i, n end
|
||||
end
|
||||
end
|
||||
|
||||
new_t.__dump = function()
|
||||
-- remove the not serializable meta data
|
||||
return {Type = "Set", Size = Size, MemSize = MemSize, Data = Data}
|
||||
end
|
||||
|
||||
new_t.__load = function(size, memsize, data)
|
||||
Size = size
|
||||
MemSize = memsize
|
||||
Data = data
|
||||
end
|
||||
|
||||
return setmetatable(new_t, mt)
|
||||
end
|
||||
|
||||
|
||||
-- remove the not serializable meta data
|
||||
function safer_lua.datastruct_to_table(ds)
|
||||
return ds.__dump()
|
||||
end
|
||||
|
||||
-- add the not serializable meta data again
|
||||
function safer_lua.table_to_datastruct(tbl)
|
||||
if tbl.Type == "Store" then
|
||||
local s = safer_lua.Store()
|
||||
s.__load(tbl.Size, tbl.MemSize, tbl.Data)
|
||||
return s
|
||||
elseif tbl.Type == "Set" then
|
||||
local s = safer_lua.Set()
|
||||
s.__load(tbl.Size, tbl.MemSize, tbl.Data)
|
||||
return s
|
||||
elseif tbl.Type == "Array" then
|
||||
local a = safer_lua.Array()
|
||||
a.__load(tbl.MemSize, tbl.Data)
|
||||
return a
|
||||
end
|
||||
end
|
|
@ -0,0 +1,4 @@
|
|||
SaferLua [safer_lua], a subset of the language Lua for safe and secure Lua sandboxes
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
--[[
|
||||
|
||||
SaferLua [safer_lua]
|
||||
====================
|
||||
|
||||
Copyright (C) 2018 Joachim Stolberg
|
||||
|
||||
LGPLv2.1+
|
||||
See LICENSE.txt for more information
|
||||
|
||||
environ.lua:
|
||||
|
||||
]]--
|
||||
|
||||
safer_lua.MaxCodeSize = 5000 -- size if source code in bytes
|
||||
safer_lua.MaxTableSize = 1000 -- sum over all table sizes
|
||||
safer_lua.MaxExeTime = 5000 -- max. execution time in us
|
||||
|
||||
local function memsize()
|
||||
return safer_lua.MaxTableSize
|
||||
end
|
||||
|
||||
local function range(from, to)
|
||||
return function(expired_at,last)
|
||||
assert(expired_at > minetest.get_us_time(), "Runtime limit exceeded")
|
||||
if last >= to then
|
||||
return nil
|
||||
else
|
||||
return last+1
|
||||
end
|
||||
end, minetest.get_us_time() + safer_lua.MaxExeTime, from-1
|
||||
end
|
||||
|
||||
local BASE_ENV = {
|
||||
Array = safer_lua.Array,
|
||||
Store = safer_lua.Store,
|
||||
Set = safer_lua.Set,
|
||||
memsize = memsize,
|
||||
range = range,
|
||||
math = {
|
||||
floor = math.floor,
|
||||
abs = math.abs,
|
||||
max = math.max,
|
||||
min = math.min,
|
||||
random = math.random,
|
||||
},
|
||||
string = {
|
||||
byte = string.byte,
|
||||
char = string.char,
|
||||
find = string.find,
|
||||
format = string.format,
|
||||
gmatch = string.gmatch,
|
||||
gsub = string.gsub,
|
||||
len = string.len,
|
||||
lower = string.lower,
|
||||
match = string.match,
|
||||
rep = string.rep,
|
||||
sub = string.sub,
|
||||
upper = string.upper,
|
||||
split = string.split,
|
||||
trim = string.trim,
|
||||
},
|
||||
tonumber = tonumber,
|
||||
tostring = tostring,
|
||||
unpack = unpack,
|
||||
type = type,
|
||||
ticks = 0,
|
||||
}
|
||||
|
||||
local function map(dest, source)
|
||||
for k,v in pairs(source) do
|
||||
dest[k] = v
|
||||
end
|
||||
return dest
|
||||
end
|
||||
|
||||
local function calc_used_mem_size(env)
|
||||
local size = 0
|
||||
for key,val in pairs(env) do
|
||||
if type(val) == "table" and val.size ~= nil then
|
||||
size = size + val.size() or 0
|
||||
end
|
||||
end
|
||||
return size
|
||||
end
|
||||
|
||||
function safer_lua.config(max_code_size, max_table_size)
|
||||
safer_lua.MaxCodeSize = max_code_size
|
||||
safer_lua.MaxTableSize = max_table_size
|
||||
end
|
||||
|
||||
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
|
||||
return "Error: Stack overflow due to recursive function calls!"
|
||||
end
|
||||
return format_error_str(err, label)
|
||||
end
|
||||
|
||||
local function compile(pos, text, label, err_clbk)
|
||||
if safer_lua:check(pos, text, label, err_clbk) == 0 then
|
||||
text = text:gsub("%$", "S:")
|
||||
local code, err = loadstring(text)
|
||||
if not code then
|
||||
err_clbk(pos, format_error(err, label))
|
||||
else
|
||||
return code
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Standard init/loop controller
|
||||
-------------------------------------------------------------------------------
|
||||
function safer_lua.init(pos, init, loop, environ, err_clbk)
|
||||
if (#init + #loop) > safer_lua.MaxCodeSize then
|
||||
err_clbk(pos, "Error: Code size limit exceeded")
|
||||
return
|
||||
end
|
||||
local code = compile(pos, init, "init", err_clbk, 0)
|
||||
if code then
|
||||
local env = table.copy(BASE_ENV)
|
||||
env.S = {}
|
||||
env.S = map(env.S, environ)
|
||||
setfenv(code, env)
|
||||
local res, err = xpcall(code, debug.traceback)
|
||||
if not res then
|
||||
err_clbk(pos, format_error(err, "init"))
|
||||
else
|
||||
env = getfenv(code)
|
||||
code = compile(pos, loop, "loop", err_clbk)
|
||||
if code then
|
||||
setfenv(code, env)
|
||||
return code
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function safer_lua.run_loop(pos, elapsed, code, err_clbk)
|
||||
local env = getfenv(code)
|
||||
env.elapsed = elapsed
|
||||
if elapsed < 0 then -- event?
|
||||
env.event = true
|
||||
else
|
||||
env.event = false
|
||||
env.ticks = env.ticks + 1
|
||||
end
|
||||
local res, err = xpcall(code, debug.traceback)
|
||||
if calc_used_mem_size(env) > safer_lua.MaxTableSize then
|
||||
err_clbk(pos, "Error: Data memory limit exceeded")
|
||||
return false
|
||||
end
|
||||
if not res then
|
||||
err_clbk(pos, format_error(err, "loop"))
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Endless/Coroutine controller
|
||||
-------------------------------------------------------------------------------
|
||||
local function thread(pos, code, err_clbk)
|
||||
while true do
|
||||
local res, err = xpcall(code, debug.traceback)
|
||||
if not res then
|
||||
err_clbk(pos, format_error(err, "loop"))
|
||||
return false
|
||||
end
|
||||
local env = getfenv(code)
|
||||
if calc_used_mem_size(env) > safer_lua.MaxTableSize then
|
||||
err_clbk(pos, "Error: Memory limit exceeded")
|
||||
return false
|
||||
end
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
|
||||
function safer_lua.co_create(pos, init, loop, environ, err_clbk)
|
||||
local code = safer_lua.init(pos, init, loop, environ, err_clbk)
|
||||
return coroutine.create(thread), code
|
||||
end
|
||||
|
||||
function safer_lua.co_resume(pos, co, code, err_clbk)
|
||||
local res, err = coroutine.resume(co, pos, code, err_clbk)
|
||||
if not res then
|
||||
err_clbk(pos, format_error(err, "loop"))
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
--[[
|
||||
|
||||
SaferLua [safer_lua]
|
||||
====================
|
||||
|
||||
Copyright (C) 2018 Joachim Stolberg
|
||||
|
||||
LGPLv2.1+
|
||||
See LICENSE.txt for more information
|
||||
|
||||
environ.lua:
|
||||
|
||||
]]--
|
||||
|
||||
safer_lua = {}
|
||||
|
||||
dofile(minetest.get_modpath("safer_lua") .. "/data_struct.lua")
|
||||
dofile(minetest.get_modpath("safer_lua") .. "/scanner.lua")
|
||||
dofile(minetest.get_modpath("safer_lua") .. "/environ.lua")
|
|
@ -0,0 +1,19 @@
|
|||
SaferLua [safer_lua] v0.01
|
||||
==========================
|
||||
|
||||
A subset of the language Lua for safe and secure Lua sandboxes with:
|
||||
- limited code length
|
||||
- limited execution time
|
||||
- limited memory use
|
||||
- limited posibilities to call functions
|
||||
|
||||
### License
|
||||
Copyright (C) 2018 Joachim Stolberg
|
||||
Code: Licensed under the GNU LGPL version 2.1 or later. See LICENSE.txt
|
||||
|
||||
|
||||
### Dependencies
|
||||
none
|
||||
|
||||
### History
|
||||
- 2018-06-24 v0.01 * first draft
|
|
@ -0,0 +1,141 @@
|
|||
--[[
|
||||
|
||||
SaferLua [safer_lua]
|
||||
====================
|
||||
|
||||
Copyright (C) 2018 Joachim Stolberg
|
||||
|
||||
LGPLv2.1+
|
||||
See LICENSE.txt for more information
|
||||
|
||||
scanner.lua:
|
||||
|
||||
]]--
|
||||
|
||||
local function trim(s)
|
||||
return (s:gsub("^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
|
||||
function safer_lua:word(ch, pttrn)
|
||||
local word = ""
|
||||
while ch:match(pttrn) do
|
||||
word = word .. ch
|
||||
self.pos = self.pos + 1
|
||||
ch = self.line:sub(self.pos, self.pos)
|
||||
end
|
||||
return word
|
||||
end
|
||||
|
||||
function safer_lua:string(pttrn)
|
||||
self.pos = self.pos + 1
|
||||
local ch = self.line:sub(self.pos, self.pos)
|
||||
while not ch:match(pttrn) and self.pos < #self.line do
|
||||
if ch == "\\" then
|
||||
self.pos = self.pos + 1
|
||||
end
|
||||
self.pos = self.pos + 1
|
||||
ch = self.line:sub(self.pos, self.pos)
|
||||
end
|
||||
self.pos = self.pos + 1
|
||||
-- result is not needed
|
||||
end
|
||||
|
||||
local function lines(str)
|
||||
local t = {}
|
||||
local function helper(line)
|
||||
table.insert(t, line)
|
||||
return ""
|
||||
end
|
||||
helper((str:gsub("(.-)\r?\n", helper)))
|
||||
return t
|
||||
end
|
||||
|
||||
function safer_lua:scanner(text)
|
||||
local lToken = {}
|
||||
for idx, line in ipairs(lines(text)) do
|
||||
self.line = line
|
||||
self.pos = 1
|
||||
self.line = trim(self.line)
|
||||
self.line = self.line:split("--", true)[1]
|
||||
table.insert(lToken, idx) -- line number
|
||||
if self.line then
|
||||
-- devide line in tokens
|
||||
while true do
|
||||
if self.pos > #self.line then break end
|
||||
local ch = self.line:sub(self.pos, self.pos)
|
||||
if ch:match("[%u%l_]") then -- identifier?
|
||||
table.insert(lToken, self:word(ch, "[%w_]"))
|
||||
elseif ch:match("[%d]") then -- number?
|
||||
table.insert(lToken, self:word(ch, "[%d%xx]"))
|
||||
elseif ch:match("'") then -- string?
|
||||
self:string("'")
|
||||
elseif ch:match('"') then -- string?
|
||||
self:string('"')
|
||||
elseif ch:match("[%s]") then -- Space?
|
||||
self.pos = self.pos + 1
|
||||
elseif ch:match("[:{}]") then -- critical tokens?
|
||||
table.insert(lToken,ch)
|
||||
self.pos = self.pos + 1
|
||||
else
|
||||
self.pos = self.pos + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return lToken
|
||||
end
|
||||
|
||||
local InvalidKeywords = {
|
||||
["while"] = true,
|
||||
["repeat"] = true,
|
||||
["until"] = true,
|
||||
["for"] = true,
|
||||
["range"] = true,
|
||||
--["function"] = true,
|
||||
["_G"] = true,
|
||||
["__load"] = true,
|
||||
["__dump"] = true,
|
||||
}
|
||||
|
||||
local InvalidChars = {
|
||||
[":"] = true,
|
||||
["{"] = true,
|
||||
["["] = true,
|
||||
["]"] = true,
|
||||
["}"] = true,
|
||||
}
|
||||
|
||||
function safer_lua:check(pos, text, label, err_clbk)
|
||||
local lToken = self:scanner(text)
|
||||
local lineno = 0
|
||||
local errno = 0
|
||||
for idx,token in ipairs(lToken) do
|
||||
if type(token) == "number" then
|
||||
lineno = token
|
||||
elseif InvalidKeywords[token] then
|
||||
if token == "for" then
|
||||
-- invalid for statement?
|
||||
if lToken[idx + 3] == "in" and lToken[idx + 5] == "next" then
|
||||
--
|
||||
elseif lToken[idx + 2] == "in" and lToken[idx + 3] == "range" then
|
||||
--
|
||||
else
|
||||
err_clbk(pos, label..":"..lineno..": Invalid use of 'for'")
|
||||
errno = errno + 1
|
||||
end
|
||||
elseif token == "range" then
|
||||
if lToken[idx - 1] ~= "in" then
|
||||
err_clbk(pos, label..":"..lineno..": Invalid use of 'range'")
|
||||
errno = errno + 1
|
||||
end
|
||||
else
|
||||
err_clbk(pos, label..":"..lineno..": Invalid keyword '"..token.."'")
|
||||
errno = errno + 1
|
||||
end
|
||||
elseif InvalidChars[token] then
|
||||
err_clbk(pos, label..":"..lineno..": Invalid character '"..token.."'")
|
||||
errno = errno + 1
|
||||
end
|
||||
end
|
||||
return errno
|
||||
end
|
|
@ -0,0 +1,203 @@
|
|||
core = {}
|
||||
|
||||
function core.global_exists(name)
|
||||
return false
|
||||
end
|
||||
|
||||
dofile('/home/joachim/minetest4/builtin/common/vector.lua')
|
||||
dofile('/home/joachim/minetest4/builtin/common/misc_helpers.lua')
|
||||
|
||||
safer_lua = {}
|
||||
safer_lua.MaxTableSize = 1000 -- number of table entries considering string lenghts
|
||||
|
||||
dofile('/home/joachim/minetest4/mods/techpack/safer_lua/data_struct.lua')
|
||||
|
||||
|
||||
print("Array")
|
||||
local a = safer_lua.Array(1,2,3,4) --> {1,2,3,4}
|
||||
a.add(6) --> {1,2,3,4,6}
|
||||
a.set(2, 8) --> {1,8,3,4,6}
|
||||
a.insert(5,7) --> {1,8,3,4,7,6}
|
||||
print(dump(a.__dump()))
|
||||
a.remove(3) --> {1,8,4,7,6}
|
||||
a.insert(1, "hello") --> {"hello",1,8,4,7,6}
|
||||
print(a.size()) --> function returns 10
|
||||
print(dump(a.__dump()))
|
||||
|
||||
|
||||
print("Store")
|
||||
local s = safer_lua.Store() --> {}
|
||||
s.set("val", 12) --> {val = 12}
|
||||
s.get("val") --> returns 12
|
||||
s.set(0, "hello") --> {val = 12, [0] = "hello"}
|
||||
print(dump(s.__dump()))
|
||||
s.del("val") --> {[0] = "hello"}
|
||||
print(s.size()) --> function returns 6
|
||||
|
||||
|
||||
print("Set")
|
||||
s = safer_lua.Set("Tom", "Lucy", "Joe") --> {Tom = true, Lucy = true, Joe = true}
|
||||
s.add("Susi") --> {Tom = true, Lucy = true, Joe = true, Susi = true}
|
||||
s.del("Tom") --> {Lucy = true, Joe = true, Susi = true}
|
||||
print(dump(s.__dump()))
|
||||
print(s.has("Joe")) --> function returns `true`
|
||||
print(s.has("Mike")) --> function returns `false`
|
||||
print(s.size()) --> function returns 11
|
||||
|
||||
print("S1")
|
||||
local s1 = safer_lua.Store()
|
||||
assert(s1.size() == 0)
|
||||
|
||||
s1.a = 3
|
||||
s1[1] = 4
|
||||
assert(s1.size() == 0)
|
||||
|
||||
s1.set("b", "Hallo")
|
||||
assert(s1.size() == 1)
|
||||
assert(s1.memsize() == 6)
|
||||
|
||||
s1.set("b", "Hall")
|
||||
assert(s1.size() == 1)
|
||||
assert(s1.memsize() == 5)
|
||||
|
||||
|
||||
assert(s1.get("b") == "Hall")
|
||||
assert(s1.size() == 1)
|
||||
|
||||
s1.set("1234", "12345678")
|
||||
assert(s1.size() == 2)
|
||||
assert(s1.memsize() == 17)
|
||||
|
||||
s1.del("1234")
|
||||
print(s1.size())
|
||||
assert(s1.size() == 1)
|
||||
assert(s1.memsize() == 5)
|
||||
assert(s1.get("1234") == nil)
|
||||
|
||||
print("S2")
|
||||
local s2 = safer_lua.Store()
|
||||
assert(s2.size() == 0)
|
||||
assert(s2.memsize() == 0)
|
||||
s2.set("b", "Joe")
|
||||
assert(s2.size() == 1)
|
||||
assert(s2.memsize() == 4)
|
||||
|
||||
assert(s2.b == nil)
|
||||
assert(s2.get('b') == "Joe")
|
||||
s2.c = "XXX!"
|
||||
assert(s2.c == nil)
|
||||
|
||||
s1.set("c", s2)
|
||||
print(dump(s1.get("c")))
|
||||
assert(s1.size() == 2)
|
||||
print(s1.memsize())
|
||||
assert(s1.memsize() == 10)
|
||||
assert(s2.size() == 1)
|
||||
assert(s2.memsize() == 4)
|
||||
|
||||
|
||||
print("A1")
|
||||
local a1 = safer_lua.Array(1,2,3,4)
|
||||
assert(a1.size() == 4)
|
||||
print(dump(a1))
|
||||
a1.set(2, "Hallo")
|
||||
assert(a1.size() == 4)
|
||||
assert(a1.memsize() == 8)
|
||||
a1.insert(1, 0)
|
||||
assert(a1.size() == 5)
|
||||
assert(a1.memsize() == 9)
|
||||
a1.remove(3)
|
||||
assert(a1.size() == 4)
|
||||
assert(a1.memsize() == 4)
|
||||
|
||||
print(a1.MemSize)
|
||||
print(a1.Size)
|
||||
|
||||
|
||||
print("Set")
|
||||
local s3 = safer_lua.Set("Joe", "Bob", "Tom")
|
||||
print(s3.size())
|
||||
s3.add("Susi")
|
||||
local t = s3.__dump()
|
||||
print(dump(t))
|
||||
s3.del("Tom")
|
||||
assert(s3.has("Susi") == true)
|
||||
assert(s3.has("Mike") == false)
|
||||
print(s3.size())
|
||||
assert(s3.size() == 3)
|
||||
assert(s3.memsize() == 10)
|
||||
|
||||
|
||||
local s4 = safer_lua.Set()
|
||||
s4.__load(3, 12, {Joe=true, Bob=true, Tom=true})
|
||||
assert(s4.has("Joe") == true)
|
||||
assert(s4.has("Mike") == false)
|
||||
print(s4.size())
|
||||
assert(s4.size() == 3)
|
||||
assert(s4.memsize() == 12)
|
||||
|
||||
|
||||
local tbl = safer_lua.datastruct_to_table(s3)
|
||||
s3 = safer_lua.table_to_datastruct(tbl)
|
||||
assert(s3.has("Susi") == true)
|
||||
assert(s3.has("Mike") == false)
|
||||
print(s3.size())
|
||||
assert(s3.size() == 3)
|
||||
assert(s3.memsize() == 10)
|
||||
|
||||
|
||||
tbl = safer_lua.datastruct_to_table(a1)
|
||||
a1 = safer_lua.table_to_datastruct(tbl)
|
||||
assert(a1.size() == 4)
|
||||
assert(a1.memsize() == 4)
|
||||
assert(a1.get(4) == 4)
|
||||
|
||||
|
||||
tbl = safer_lua.datastruct_to_table(s2)
|
||||
s2 = safer_lua.table_to_datastruct(tbl)
|
||||
assert(s2.size() == 1)
|
||||
assert(s2.memsize() == 4)
|
||||
assert(s2.get("b") == "Joe")
|
||||
|
||||
|
||||
print("next over Array")
|
||||
local a2 = safer_lua.Array(10, 20, 30)
|
||||
for i,v in a2.next() do
|
||||
print(i,v)
|
||||
end
|
||||
|
||||
print("next over Set")
|
||||
local s6 = safer_lua.Set("Joe", "Bob", "Tom")
|
||||
for i,v in s6.next() do
|
||||
print(i,v)
|
||||
end
|
||||
|
||||
print("next over Store")
|
||||
local s7 = safer_lua.Store() --> {}
|
||||
s7.set("a", 12)
|
||||
s7.set("b", 13)
|
||||
s7.set("c", 14)
|
||||
for k,v in s7.next() do
|
||||
print(k,v)
|
||||
end
|
||||
|
||||
print("sort")
|
||||
a3 = safer_lua.Array("Joe", "Bob", "Tom")
|
||||
a3.sort(true)
|
||||
print(dump(a3.__dump()))
|
||||
a3.sort()
|
||||
print(dump(a3.__dump()))
|
||||
|
||||
s7 = safer_lua.Store()
|
||||
s7.set("Joe", 1000)
|
||||
s7.set("Susi", 800)
|
||||
s7.set("Tom", 60)
|
||||
s7.set("Mike", 900)
|
||||
s7.set("Bob", 1100)
|
||||
local k = s7.keys()
|
||||
print(dump(k.__dump()))
|
||||
local k = s7.keys("up")
|
||||
print(dump(k.__dump()))
|
||||
local k = s7.keys("down")
|
||||
print(dump(k.__dump()))
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
core = {}
|
||||
|
||||
function core.global_exists(name)
|
||||
return false
|
||||
end
|
||||
|
||||
dofile('/home/joachim/minetest4/builtin/common/vector.lua')
|
||||
dofile('/home/joachim/minetest4/builtin/common/misc_helpers.lua')
|
||||
|
||||
safer_lua = {}
|
||||
dofile('/home/joachim/minetest4/mods/techpack/safer_lua/data_struct.lua')
|
||||
dofile('/home/joachim/minetest4/mods/techpack/safer_lua/scanner.lua')
|
||||
dofile('/home/joachim/minetest4/mods/techpack/safer_lua/environ.lua')
|
||||
|
||||
--local Cache = {}
|
||||
--local key = minetest.pos_to_hash(pos)
|
||||
--code = Cache[key]
|
||||
|
||||
local function foo(self, val)
|
||||
print("Hallo", val)
|
||||
end
|
||||
|
||||
local function error(pos, s)
|
||||
print("[Test] "..(s or "").." at pos "..dump(pos))
|
||||
end
|
||||
|
||||
|
||||
local init = "fo"
|
||||
local loop = [[
|
||||
--$loopcycle(5)
|
||||
--$events(true)
|
||||
$foo("hallo")
|
||||
$foo("hallo")
|
||||
$foo(math.floor(5.5))
|
||||
$foo("Joe")
|
||||
a = Store()
|
||||
a.set("a", 123)
|
||||
a.set("b", 456)
|
||||
$foo(a.get("a"))
|
||||
$foo(ticks)
|
||||
if ticks == 10 then $foo("Fehler") end
|
||||
for k,v in a.next() do
|
||||
$foo(k)
|
||||
end
|
||||
--for k = 1,4,1 do
|
||||
-- next = 3
|
||||
-- $foo(k)
|
||||
--end
|
||||
--$foo(meta.pos)
|
||||
]]
|
||||
|
||||
local init2 = [[
|
||||
meme = "\n\n\"" while true do end meme = ""
|
||||
]]
|
||||
|
||||
local env = {foo = foo}
|
||||
env.meta = {pos=1, owner="Joe", number="0123"}
|
||||
|
||||
local code = safer_lua.init(0, init, "", loop, env, error)
|
||||
if code then
|
||||
print(safer_lua.run_loop(0, 0, code, error))
|
||||
safer_lua.run_loop(0, 1, code, error)
|
||||
safer_lua.run_loop(0, 2, code, error)
|
||||
end
|
||||
|
||||
print("\n<<< Coroutine >>>\n")
|
||||
local function outp(self, val)
|
||||
print(val)
|
||||
end
|
||||
|
||||
local function step(self, num)
|
||||
for i=1,num do
|
||||
print(i)
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local thread = [[
|
||||
$outp("hallo")
|
||||
$step(2)
|
||||
$outp(meta.pos)
|
||||
]]
|
||||
|
||||
env = {step = step, outp = outp}
|
||||
|
||||
env.meta = {pos=1, owner="Joe", number="0123", error=error}
|
||||
local co, code = safer_lua.co_create(0, init, thread, env, error)
|
||||
|
||||
if code then
|
||||
for i=1,20 do
|
||||
if safer_lua.co_resume(0, co, code, error) == false then break end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,41 @@
|
|||
core = {}
|
||||
|
||||
function core.global_exists(name)
|
||||
return false
|
||||
end
|
||||
|
||||
safer_lua = {}
|
||||
|
||||
dofile('/home/joachim/minetest4/builtin/common/vector.lua')
|
||||
dofile('/home/joachim/minetest4/builtin/common/misc_helpers.lua')
|
||||
dofile('/home/joachim/minetest4/mods/techpack/safer_lua/scanner.lua')
|
||||
|
||||
code = [[
|
||||
-- GOOD
|
||||
a = 1
|
||||
a = a + 1
|
||||
print(a)
|
||||
foo(a)
|
||||
|
||||
-- BAD
|
||||
_G.print(()
|
||||
t = {}
|
||||
for i = 1,1000 do
|
||||
]]
|
||||
|
||||
code2 = [[
|
||||
meme = "\"" while true do end meme = ""
|
||||
]]
|
||||
|
||||
code3 = "s = [[abc]]"
|
||||
local function error(pos, s)
|
||||
print("[Robbi] "..s)
|
||||
end
|
||||
|
||||
safer_lua:check(nil, code, "Code", error)
|
||||
|
||||
lToken = safer_lua:scanner(code2)
|
||||
safer_lua:check(nil, code2, "Code", error)
|
||||
|
||||
lToken = safer_lua:scanner(code3)
|
||||
safer_lua:check(nil, code3, "Code", error)
|
|
@ -0,0 +1,59 @@
|
|||
core = {}
|
||||
|
||||
function core.global_exists(name)
|
||||
return false
|
||||
end
|
||||
|
||||
dofile('/home/joachim/minetest/builtin/common/vector.lua')
|
||||
dofile('/home/joachim/minetest/builtin/common/misc_helpers.lua')
|
||||
|
||||
safer_lua = {}
|
||||
safer_lua.MaxTableSize = 1000 -- number of table entries considering string lenghts
|
||||
|
||||
dofile('/home/joachim/minetest4/mods/techpack/safer_lua/data_struct.lua')
|
||||
|
||||
local s1 = safer_lua.Store()
|
||||
local s2 = safer_lua.Store()
|
||||
|
||||
print("S1")
|
||||
assert(s1.size() == 0)
|
||||
|
||||
s1.a = 3
|
||||
assert(s1.size() == 0)
|
||||
|
||||
s1.set("b", "Hallo")
|
||||
assert(s1.size() == 1)
|
||||
|
||||
assert(s1.get("b") == "Hallo")
|
||||
assert(s1.size() == 1)
|
||||
|
||||
print("S2")
|
||||
assert(s2.size() == 0)
|
||||
s2.set("b", "Joe")
|
||||
assert(s2.size() == 1)
|
||||
|
||||
assert(s2.b == nil)
|
||||
s2.c = "XXX!"
|
||||
assert(s2.c == nil)
|
||||
|
||||
s1.set("c", s2)
|
||||
print(s1.get("c"))
|
||||
|
||||
|
||||
local s3 = safer_lua.Store("a", 4, "b", 5, "c")
|
||||
assert(s3.get("a") == 4)
|
||||
assert(s3.get("b") == 5)
|
||||
assert(s3.get("c") == nil)
|
||||
|
||||
s = safer_lua.Store("a", 4, "b", 5) --> {a = 4, b = 5}
|
||||
--s = safer_lua.Store() --> {a = 4, b = 5}
|
||||
s.set("val", 12) --> {a = 4, b = 5, val = 12}
|
||||
s.get("val") --> returns 12
|
||||
s.set(0, "hello") --> {a = 4, b = 5, val = 12, [0] = "hello"}
|
||||
s.del("val") --> {a = 4, b = 5, [0] = "hello"}
|
||||
print(s.size()) --> function returns 3
|
||||
print(s.memsize()) --> function returns 8
|
||||
|
||||
|
||||
|
||||
print("Ready.")
|
Loading…
Reference in New Issue