From 2190ff6bc08bd442f3122019b87e97fadee0726c Mon Sep 17 00:00:00 2001 From: number Zero Date: Tue, 1 Jan 2019 23:10:38 +0300 Subject: [PATCH] Test version --- libluabox/init.lua | 5 + mesecons_sandbox/depends.txt | 1 + mesecons_sandbox/init.lua | 5 + mesecons_sandbox/sandbox.lua | 21 +++ mesecons_sandbox/sandbox/_fini.lua | 3 + mesecons_sandbox/sandbox/_init.lua | 11 ++ mesecons_sandbox/sandbox/helpers.lua | 187 +++++++++++++++++++++++++ mesecons_sandbox/sandbox/serialize.lua | 177 +++++++++++++++++++++++ mesecons_sandbox/sandbox/test.lua | 2 + 9 files changed, 412 insertions(+) create mode 100644 libluabox/init.lua create mode 100644 mesecons_sandbox/depends.txt create mode 100644 mesecons_sandbox/init.lua create mode 100644 mesecons_sandbox/sandbox.lua create mode 100644 mesecons_sandbox/sandbox/_fini.lua create mode 100644 mesecons_sandbox/sandbox/_init.lua create mode 100644 mesecons_sandbox/sandbox/helpers.lua create mode 100644 mesecons_sandbox/sandbox/serialize.lua create mode 100644 mesecons_sandbox/sandbox/test.lua diff --git a/libluabox/init.lua b/libluabox/init.lua new file mode 100644 index 0000000..277e802 --- /dev/null +++ b/libluabox/init.lua @@ -0,0 +1,5 @@ +libluabox = {} +--local lualua = require("lualua") +local path = minetest.get_modpath("libluabox") +local lualua = package.loadlib(path .. "/lualua.so", "luaopen_lualua") +libluabox.run = lualua() diff --git a/mesecons_sandbox/depends.txt b/mesecons_sandbox/depends.txt new file mode 100644 index 0000000..cf9d15e --- /dev/null +++ b/mesecons_sandbox/depends.txt @@ -0,0 +1 @@ +libluabox diff --git a/mesecons_sandbox/init.lua b/mesecons_sandbox/init.lua new file mode 100644 index 0000000..94e9c01 --- /dev/null +++ b/mesecons_sandbox/init.lua @@ -0,0 +1,5 @@ +local BASE = minetest.get_modpath(minetest.get_current_modname()) + +mesecons_sandbox = {} + +dofile(BASE .. "/sandbox.lua") diff --git a/mesecons_sandbox/sandbox.lua b/mesecons_sandbox/sandbox.lua new file mode 100644 index 0000000..25ed610 --- /dev/null +++ b/mesecons_sandbox/sandbox.lua @@ -0,0 +1,21 @@ +local BASE = minetest.get_modpath(minetest.get_current_modname()) +local LIBS = BASE .. "/sandbox" + +local function load_lib(name) + local filename = string.format("%s/%s.lua", LIBS, name) + local file = io.open(filename, "rb") + local code = file:read("*a") + file:close() + return code +end + +local f_init = load_lib("_init") +local f_fini = load_lib("_fini") +local f_helpers = load_lib("helpers") +local f_serialize = load_lib("serialize") +local c_test = load_lib("test") + +print("Testing") +local a,b,c,d,e,f = libluabox.run(0.1, 128, f_helpers, f_serialize, f_init, c_test, f_fini) +print(a,b,c,d,e,f) +print("Tested") diff --git a/mesecons_sandbox/sandbox/_fini.lua b/mesecons_sandbox/sandbox/_fini.lua new file mode 100644 index 0000000..579e89a --- /dev/null +++ b/mesecons_sandbox/sandbox/_fini.lua @@ -0,0 +1,3 @@ +local result = serialize({...}) +local log = table.concat(log, "\n") +return result, log diff --git a/mesecons_sandbox/sandbox/_init.lua b/mesecons_sandbox/sandbox/_init.lua new file mode 100644 index 0000000..68df35f --- /dev/null +++ b/mesecons_sandbox/sandbox/_init.lua @@ -0,0 +1,11 @@ +mem = ... + +log = {} + +function print(...) + local entry = {} + for i, v in ipairs({...}) do + entry[i] = dump(v) + end + log[#log + 1] = table.concat(entry, "\t") +end diff --git a/mesecons_sandbox/sandbox/helpers.lua b/mesecons_sandbox/sandbox/helpers.lua new file mode 100644 index 0000000..60735f6 --- /dev/null +++ b/mesecons_sandbox/sandbox/helpers.lua @@ -0,0 +1,187 @@ +-------------------------------------------------------------------------------- +-- Localize functions to avoid table lookups (better performance). +local string_sub, string_find = string.sub, string.find + +-------------------------------------------------------------------------------- +local function basic_dump(o) + local tp = type(o) + if tp == "number" then + return tostring(o) + elseif tp == "string" then + return string.format("%q", o) + elseif tp == "boolean" then + return tostring(o) + elseif tp == "nil" then + return "nil" + -- Uncomment for full function dumping support. + -- Not currently enabled because bytecode isn't very human-readable and + -- dump's output is intended for humans. + --elseif tp == "function" then + -- return string.format("loadstring(%q)", string.dump(o)) + else + return string.format("<%s>", tp) + end +end + +local keywords = { + ["and"] = true, + ["break"] = true, + ["do"] = true, + ["else"] = true, + ["elseif"] = true, + ["end"] = true, + ["false"] = true, + ["for"] = true, + ["function"] = true, + ["goto"] = true, -- Lua 5.2 + ["if"] = true, + ["in"] = true, + ["local"] = true, + ["nil"] = true, + ["not"] = true, + ["or"] = true, + ["repeat"] = true, + ["return"] = true, + ["then"] = true, + ["true"] = true, + ["until"] = true, + ["while"] = true, +} +local function is_valid_identifier(str) + if not string_find("^[a-zA-Z_][a-zA-Z0-9_]*$") or keywords[str] then + return false + end + return true +end + +-------------------------------------------------------------------------------- +-- Dumps values in a line-per-value format. +-- For example, {test = {"Testing..."}} becomes: +-- _["test"] = {} +-- _["test"][1] = "Testing..." +-- This handles tables as keys and circular references properly. +-- It also handles multiple references well, writing the table only once. +-- The dumped argument is internal-only. + +local function dump2(o, name, dumped) + name = name or "_" + -- "dumped" is used to keep track of serialized tables to handle + -- multiple references and circular tables properly. + -- It only contains tables as keys. The value is the name that + -- the table has in the dump, eg: + -- {x = {"y"}} -> dumped[{"y"}] = '_["x"]' + dumped = dumped or {} + if type(o) ~= "table" then + return string.format("%s = %s\n", name, basic_dump(o)) + end + if dumped[o] then + return string.format("%s = %s\n", name, dumped[o]) + end + dumped[o] = name + -- This contains a list of strings to be concatenated later (because + -- Lua is slow at individual concatenation). + local t = {} + for k, v in pairs(o) do + local keyStr + if type(k) == "table" then + if dumped[k] then + keyStr = dumped[k] + else + -- Key tables don't have a name, so use one of + -- the form _G["table: 0xFFFFFFF"] + keyStr = string.format("_G[%q]", tostring(k)) + -- Dump key table + t[#t + 1] = dump2(k, keyStr, dumped) + end + else + keyStr = basic_dump(k) + end + local vname = string.format("%s[%s]", name, keyStr) + t[#t + 1] = dump2(v, vname, dumped) + end + return string.format("%s = {}\n%s", name, table.concat(t)) +end + +-------------------------------------------------------------------------------- +-- This dumps values in a one-statement format. +-- For example, {test = {"Testing..."}} becomes: +-- [[{ +-- test = { +-- "Testing..." +-- } +-- }]] +-- This supports tables as keys, but not circular references. +-- It performs poorly with multiple references as it writes out the full +-- table each time. +-- The indent field specifies a indentation string, it defaults to a tab. +-- Use the empty string to disable indentation. +-- The dumped and level arguments are internal-only. + +function dump(o, indent, nested, level) + local t = type(o) + if not level and t == "userdata" then + -- when userdata (e.g. player) is passed directly, print its metatable: + return "userdata metatable: " .. dump(getmetatable(o)) + end + if t ~= "table" then + return basic_dump(o) + end + -- Contains table -> true/nil of currently nested tables + nested = nested or {} + if nested[o] then + return "" + end + nested[o] = true + indent = indent or "\t" + level = level or 1 + local t = {} + local dumped_indexes = {} + for i, v in ipairs(o) do + t[#t + 1] = dump(v, indent, nested, level + 1) + dumped_indexes[i] = true + end + for k, v in pairs(o) do + if not dumped_indexes[k] then + if type(k) ~= "string" or not is_valid_identifier(k) then + k = "["..dump(k, indent, nested, level + 1).."]" + end + v = dump(v, indent, nested, level + 1) + t[#t + 1] = k.." = "..v + end + end + nested[o] = nil + if indent ~= "" then + local indent_str = "\n"..string.rep(indent, level) + local end_indent_str = "\n"..string.rep(indent, level - 1) + return string.format("{%s%s%s}", + indent_str, + table.concat(t, ","..indent_str), + end_indent_str) + end + return "{"..table.concat(t, ", ").."}" +end + +-------------------------------------------------------------------------------- +function string.split(str, delim, include_empty, max_splits, sep_is_pattern) + delim = delim or "," + max_splits = max_splits or -2 + local items = {} + local pos, len = 1, #str + local plain = not sep_is_pattern + max_splits = max_splits + 1 + repeat + local np, npe = string_find(str, delim, pos, plain) + np, npe = (np or (len+1)), (npe or (len+1)) + if (not np) or (max_splits == 1) then + np = len + 1 + npe = np + end + local s = string_sub(str, pos, np - 1) + if include_empty or (s ~= "") then + max_splits = max_splits - 1 + items[#items + 1] = s + end + pos = npe + 1 + until (max_splits == 0) or (pos > (len + 1)) + return items +end diff --git a/mesecons_sandbox/sandbox/serialize.lua b/mesecons_sandbox/sandbox/serialize.lua new file mode 100644 index 0000000..c95177e --- /dev/null +++ b/mesecons_sandbox/sandbox/serialize.lua @@ -0,0 +1,177 @@ +function serialize(x) + local local_index = 1 -- Top index of the "_" local table in the dump + -- table->nil/1/2 set of tables seen. + -- nil = not seen, 1 = seen once, 2 = seen multiple times. + local seen = {} + + -- nest_points are places where a table appears within itself, directly + -- or not. For instance, all of these chunks create nest points in + -- table x: "x = {}; x[x] = 1", "x = {}; x[1] = x", + -- "x = {}; x[1] = {y = {x}}". + -- To handle those, two tables are used by mark_nest_point: + -- * nested - Transient set of tables being currently traversed. + -- Used for detecting nested tables. + -- * nest_points - parent->{key=value, ...} table cantaining the nested + -- keys and values in the parent. They're all dumped after all the + -- other table operations have been performed. + -- + -- mark_nest_point(p, k, v) fills nest_points with information required + -- to remember that key/value (k, v) creates a nest point in table + -- parent. It also marks "parent" and the nested item(s) as occuring + -- multiple times, since several references to it will be required in + -- order to patch the nest points. + local nest_points = {} + local nested = {} + local function mark_nest_point(parent, k, v) + local nk, nv = nested[k], nested[v] + local np = nest_points[parent] + if not np then + np = {} + nest_points[parent] = np + end + np[k] = v + seen[parent] = 2 + if nk then seen[k] = 2 end + if nv then seen[v] = 2 end + end + + -- First phase, list the tables and functions which appear more than + -- once in x. + local function mark_multiple_occurences(x) + local tp = type(x) + if tp ~= "table" and tp ~= "function" then + -- No identity (comparison is done by value, not by instance) + return + end + if seen[x] == 1 then + seen[x] = 2 + elseif seen[x] ~= 2 then + seen[x] = 1 + end + + if tp == "table" then + nested[x] = true + for k, v in pairs(x) do + if nested[k] or nested[v] then + mark_nest_point(x, k, v) + else + mark_multiple_occurences(k) + mark_multiple_occurences(v) + end + end + nested[x] = nil + end + end + + local dumped = {} -- object->varname set + local local_defs = {} -- Dumped local definitions as source code lines + + -- Mutually recursive local functions: + local dump_val, dump_or_ref_val + + -- If x occurs multiple times, dump the local variable rather than + -- the value. If it's the first time it's dumped, also dump the + -- content in local_defs. + function dump_or_ref_val(x) + if seen[x] ~= 2 then + return dump_val(x) + end + local var = dumped[x] + if var then -- Already referenced + return var + end + -- First occurence, create and register reference + local val = dump_val(x) + local i = local_index + local_index = local_index + 1 + var = "_["..i.."]" + local_defs[#local_defs + 1] = var.." = "..val + dumped[x] = var + return var + end + + -- Second phase. Dump the object; subparts occuring multiple times + -- are dumped in local variables which can be referenced multiple + -- times. Care is taken to dump local vars in a sensible order. + function dump_val(x) + local tp = type(x) + if x == nil then return "nil" + elseif tp == "string" then return string.format("%q", x) + elseif tp == "boolean" then return x and "true" or "false" + elseif tp == "function" then + return string.format("loadstring(%q)", string.dump(x)) + elseif tp == "number" then + -- Serialize integers with string.format to prevent + -- scientific notation, which doesn't preserve + -- precision and breaks things like node position + -- hashes. Serialize floats normally. + if math.floor(x) == x then + return string.format("%d", x) + else + return tostring(x) + end + elseif tp == "table" then + local vals = {} + local idx_dumped = {} + local np = nest_points[x] + for i, v in ipairs(x) do + if not np or not np[i] then + vals[#vals + 1] = dump_or_ref_val(v) + end + idx_dumped[i] = true + end + for k, v in pairs(x) do + if (not np or not np[k]) and + not idx_dumped[k] then + vals[#vals + 1] = "["..dump_or_ref_val(k).."] = " + ..dump_or_ref_val(v) + end + end + return "{"..table.concat(vals, ", ").."}" + else + error("Can't serialize data of type "..tp) + end + end + + local function dump_nest_points() + for parent, vals in pairs(nest_points) do + for k, v in pairs(vals) do + local_defs[#local_defs + 1] = dump_or_ref_val(parent) + .."["..dump_or_ref_val(k).."] = " + ..dump_or_ref_val(v) + end + end + end + + mark_multiple_occurences(x) + local top_level = dump_or_ref_val(x) + dump_nest_points() + + if next(local_defs) then + return "local _ = {}\n" + ..table.concat(local_defs, "\n") + .."\nreturn "..top_level + else + return "return "..top_level + end +end + +-- Deserialization +function parse(str) + if type(str) ~= "string" then + error("Cannot deserialize type "..type(str)..". Argument must be a string.") + end + if str:byte(1) == 0x1B then + return nil, "Bytecode prohibited" + end + local f, err = loadstring(str) + if not f then return nil, err end + setfenv(f, {}) + + local good, data = pcall(f) + if good then + return data + else + return nil, data + end +end diff --git a/mesecons_sandbox/sandbox/test.lua b/mesecons_sandbox/sandbox/test.lua new file mode 100644 index 0000000..b1dd74a --- /dev/null +++ b/mesecons_sandbox/sandbox/test.lua @@ -0,0 +1,2 @@ +print("Hello, world!") +print("Something else...")