Test version
This commit is contained in:
parent
737f366741
commit
2190ff6bc0
|
@ -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()
|
|
@ -0,0 +1 @@
|
||||||
|
libluabox
|
|
@ -0,0 +1,5 @@
|
||||||
|
local BASE = minetest.get_modpath(minetest.get_current_modname())
|
||||||
|
|
||||||
|
mesecons_sandbox = {}
|
||||||
|
|
||||||
|
dofile(BASE .. "/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")
|
|
@ -0,0 +1,3 @@
|
||||||
|
local result = serialize({...})
|
||||||
|
local log = table.concat(log, "\n")
|
||||||
|
return result, log
|
|
@ -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
|
|
@ -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 "<circular reference>"
|
||||||
|
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
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
||||||
|
print("Hello, world!")
|
||||||
|
print("Something else...")
|
Loading…
Reference in New Issue