From 6ab8bfb5c53c05ac7812efc2c8ef46f675211526 Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Fri, 26 Jul 2019 09:36:06 +0800 Subject: reduce concat --- xmake/core/base/serialize.lua | 56 +++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/xmake/core/base/serialize.lua b/xmake/core/base/serialize.lua index 61e8c3adf..8da0546c8 100644 --- a/xmake/core/base/serialize.lua +++ b/xmake/core/base/serialize.lua @@ -59,11 +59,14 @@ function serialize._maketable(object, opt, level) local maxn = 0 for k, v in pairs(object) do if type(k) == "number" then - numidxcount = numidxcount + 1 - if k < 1 or not math.isint(k) then - isarr = false - elseif k > maxn then - maxn = k + -- only checks when it may be an array + if isarr then + numidxcount = numidxcount + 1 + if k < 1 or not math.isint(k) then + isarr = false + elseif k > maxn then + maxn = k + end end elseif type(k) == "string" then isarr = false @@ -89,7 +92,7 @@ function serialize._maketable(object, opt, level) end -- make head - local headstr = opt.indent and "{\n" or "{" + local headstr = opt.indent and ("{\n" .. indent .. opt.indent) or "{" -- make tail local tailstr @@ -100,44 +103,33 @@ function serialize._maketable(object, opt, level) end -- make body - local s = {} - if opt.indent then - indent = string.rep(opt.indent, level + 1) - end - + local bodystrs = {} if isarr then - local nilval - if maxn ~= numidxcount then - nilval = indent .. "nil" - end for i = 1, maxn do - local val = serialized[i] - if val == nil then - s[i] = nilval - else - s[i] = indent .. val - end + bodystrs[i] = serialized[i] or "nil" end else local con = opt.indent and " = " or "=" for k, v in pairs(serialized) do - if type(k) == "string" and not k:match("^[%a_][%w_]*$") then - k = string.format("[%q]", k) - elseif type(k) == "number" then + if type(k) == "string" then + if not k:match("^[%a_][%w_]*$") then + k = string.format("[%q]", k) + end + else -- type(k) == "number" local nval, err = serialize._makenumber(k, opt, childlevel) if err ~= nil then return nil, err end k = string.format("[%s]", nval) end - table.insert(s, indent .. k .. con .. v) + table.insert(bodystrs, k .. con .. v) end end - if #s == 0 then + if #bodystrs == 0 then return opt.indent and "{ }" or "{}" end - return headstr .. table.concat(s, opt.indent and ",\n" or ",") .. tailstr + return headstr .. table.concat(bodystrs, opt.indent and (",\n" .. indent .. opt.indent) or ",") .. tailstr end function serialize._makefunction(func, opt, level) @@ -196,17 +188,24 @@ function serialize.save(object, opt) opt = { strip = false, binary = false, indent = true } end + -- init indent, from nil, boolean, number or string to false or string if not opt.indent then + -- no indent opt.indent = false - elseif type(opt.indent) == "boolean" then + elseif type(opt.indent) == "boolean" then -- true + -- 4 spaces opt.indent = " " elseif type(opt.indent) == "number" then if opt.indent < 0 then opt.indent = false + elseif opt.indent > 20 then + return nil, "invalid opt.indent, too large" else + -- opt.indent spaces opt.indent = string.rep(" ", opt.indent) end elseif type(opt.indent) == "string" then + -- only whitespaces allowed if not opt.indent:match("^%s+$") then return nil, "invalid opt.indent, only whitespaces are accepted" end @@ -216,7 +215,6 @@ function serialize.save(object, opt) -- make string local ok, result, errors = pcall(serialize._make, object, opt, 0) - if not ok then if result:find("stack overflow", 1, true) then errors = "cannot serialize: reference loop found" -- cgit v1.3.1 From e9902d526b04319bb73dddabab17c5e339dba99a Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Fri, 26 Jul 2019 09:49:50 +0800 Subject: add unit test for math --- tests/modules/math/test.lua | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/modules/math/test.lua diff --git a/tests/modules/math/test.lua b/tests/modules/math/test.lua new file mode 100644 index 000000000..3d05adb61 --- /dev/null +++ b/tests/modules/math/test.lua @@ -0,0 +1,34 @@ +function test_isinf(t) + t:will_raise(function() math.isinf(nil) end) + t:will_raise(function() math.isinf(true) end) + + t:require_not(math.isinf(0)) + t:require_not(math.isinf(math.nan)) + t:are_same(math.isinf(math.huge), 1) + t:are_same(math.isinf(-math.huge), -1) +end + +function test_isnan(t) + t:will_raise(function() math.isinf(nil) end) + t:will_raise(function() math.isinf(true) end) + + t:require_not(math.isnan(0)) + t:require(math.isnan(math.nan)) + t:require_not(math.isnan(math.huge)) + t:require_not(math.isnan(-math.huge)) +end + +function test_isint(t) + t:will_raise(function() math.isint(nil) end) + t:will_raise(function() math.isint(true) end) + + t:require(math.isint(0)) + t:require(math.isint(-10)) + t:require(math.isint(123456)) + t:require_not(math.isint(123456.1)) + t:require_not(math.isint(-9.99)) + t:require_not(math.isint(math.nan)) + t:require_not(math.isint(math.huge)) + t:require_not(math.isint(-math.huge)) +end + -- cgit v1.3.1 From 71d253bc95fe107ee401f37a8d6405fac96651fa Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Fri, 26 Jul 2019 11:05:33 +0800 Subject: clean up code --- xmake/core/base/io.lua | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/xmake/core/base/io.lua b/xmake/core/base/io.lua index a7e18f38e..428fdc8c7 100644 --- a/xmake/core/base/io.lua +++ b/xmake/core/base/io.lua @@ -65,15 +65,19 @@ end -- save object function _file:save(object) local str, errors = string.serialize(object, false) - if str then + if not errors then self:write(str) + return str end return str, errors end -- load object function _file:load() - local data = self:read("*all") + local data, err = self:read("*all") + if err then + return nil, err + end if data and type(data) == "string" then return data:deserialize() end @@ -214,30 +218,22 @@ function io.save(filepath, object, opt) -- init option opt = opt or {} - -- ensure directory - local dir = path.directory(filepath) - if not os.isdir(dir) then - os.mkdir(dir) - end - -- open the file - local file = io.open(filepath, "wb", opt) - if not file then + local file, err = io.open(filepath, "wb", opt) + if err then -- error - return false, string.format("open %s failed!", filepath) + return false, err end -- save object to file local ok, errors = file:save(object) + -- close file + file:close() if not ok then -- error - file:close() return false, string.format("save %s failed, %s!", filepath, errors) end - -- close file - file:close() - -- ok return true end @@ -252,10 +248,10 @@ function io.load(filepath, opt) opt = opt or {} -- open the file - local file = io.open(filepath, "rb", opt) - if not file then + local file, err = io.open(filepath, "rb", opt) + if err then -- error - return nil, string.format("open %s failed!", filepath) + return nil, err end -- load object -- cgit v1.3.1 From 39bd37f2a46ed9b5e6d02bcf68ecd5597ee85dfa Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Fri, 26 Jul 2019 11:16:20 +0800 Subject: improve error message --- xmake/core/base/serialize.lua | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/xmake/core/base/serialize.lua b/xmake/core/base/serialize.lua index 8da0546c8..8bfc265c6 100644 --- a/xmake/core/base/serialize.lua +++ b/xmake/core/base/serialize.lua @@ -257,31 +257,33 @@ function serialize._load(str) str = "return " .. str end - local script, errors = loadstring(str) + -- load string + local script, errors = loadstring(str, "=(deserializing data)") if script then - -- load object local ok, object = pcall(script) if ok then result = object - elseif object then - -- error - errors = object else - local data - if binary then - data = "" - elseif #str > 20 then - data = str:sub(8, 17) .. "..." - else - data = str:sub(8) - end -- error - errors = string.format("cannot deserialize string: %s", data) + errors = tostring(object) end end - return result, errors + if errors then + local data + if binary then + data = "" + elseif #str > 30 then + data = string.format("%q... ", str:sub(8, 27)) + else + data = string.format("%q", str:sub(8)) + end + -- error + return nil, string.format("cannot deserialize %s: %s", data, errors) + end + + return result end -- deserialize string to object -- cgit v1.3.1 From 1a1e0c7200d23f53bd1219341bc1dada7b4054a9 Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Fri, 26 Jul 2019 11:24:14 +0800 Subject: fix load nil --- xmake/core/sandbox/modules/io.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xmake/core/sandbox/modules/io.lua b/xmake/core/sandbox/modules/io.lua index c4c1a63c8..e9206bbaa 100644 --- a/xmake/core/sandbox/modules/io.lua +++ b/xmake/core/sandbox/modules/io.lua @@ -172,7 +172,7 @@ function sandbox_io.load(filepath, opt) -- done local result, errors = io.load(filepath, opt) - if not result then + if errors ~= nil then raise(errors) end -- cgit v1.3.1 From 55b29ab3799917fa04b55ac5c8273eadb6045b39 Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Fri, 26 Jul 2019 11:52:14 +0800 Subject: use a seperate nev to load seriailzed data --- xmake/core/base/io.lua | 6 ++--- xmake/core/base/serialize.lua | 58 ++++++++++++++++++------------------------- 2 files changed, 27 insertions(+), 37 deletions(-) diff --git a/xmake/core/base/io.lua b/xmake/core/base/io.lua index 428fdc8c7..d234d52b3 100644 --- a/xmake/core/base/io.lua +++ b/xmake/core/base/io.lua @@ -63,8 +63,8 @@ function _file:printf(...) end -- save object -function _file:save(object) - local str, errors = string.serialize(object, false) +function _file:save(object, opt) + local str, errors = string.serialize(object, opt) if not errors then self:write(str) return str @@ -226,7 +226,7 @@ function io.save(filepath, object, opt) end -- save object to file - local ok, errors = file:save(object) + local ok, errors = file:save(object, opt) -- close file file:close() if not ok then diff --git a/xmake/core/base/serialize.lua b/xmake/core/base/serialize.lua index 8bfc265c6..252a223cc 100644 --- a/xmake/core/base/serialize.lua +++ b/xmake/core/base/serialize.lua @@ -21,6 +21,7 @@ -- define module: serialize local serialize = serialize or {} +local _ENV = serialize._ENV or {} -- load modules local math = require("base/math") @@ -28,24 +29,15 @@ local math = require("base/math") -- save original interfaces serialize._dump = serialize._dump or string._dump or string.dump -function serialize._makenumber(num, opt, level) - if math.isnan(num) then - return "math.nan" - end - local inf = math.isinf(num) - if inf == 1 then - return "math.huge" - elseif inf == -1 then - return "-math.huge" - end - return tostring(num) -end +-- init env +_ENV.nan = math.nan +_ENV.inf = math.huge function serialize._makestring(str, opt, level) return string.format("%q", str) end -function serialize._makekeyword(val, opt, level) +function serialize._makedefault(val, opt, level) return tostring(val) end @@ -116,7 +108,7 @@ function serialize._maketable(object, opt, level) k = string.format("[%q]", k) end else -- type(k) == "number" - local nval, err = serialize._makenumber(k, opt, childlevel) + local nval, err = serialize._makedefault(k, opt, childlevel) if err ~= nil then return nil, err end @@ -138,20 +130,15 @@ function serialize._makefunction(func, opt, level) if not ok then return nil, string.format("%s: <%s>", funccode, func) end + return string.format("func%q", funccode) +end - local chunkname = nil - local sep = "," - if opt.strip then - chunkname = "\"=(deserialized code)\"" - end - if opt.indent then - sep = ", " - end - if chunkname then - return string.format("loadstring(%q%s%s)", funccode, sep, chunkname) - else - return string.format("loadstring(%q)", funccode) - end +-- load function +function _ENV.func(funccode) + -- type guard + assert(type(funccode) == "string", "func should called with a string") + -- load func + return loadstring(funccode, "=(deserialized code)") end -- make string with the level @@ -160,10 +147,8 @@ function serialize._make(object, opt, level) -- call make* by type if type(object) == "string" then return serialize._makestring(object, opt, level) - elseif type(object) == "boolean" or type(object) == "nil" then - return serialize._makekeyword(object, opt, level) - elseif type(object) == "number" then - return serialize._makenumber(object, opt, level) + elseif type(object) == "boolean" or type(object) == "nil" or type(object) == "number" then + return serialize._makedefault(object, opt, level) elseif type(object) == "table" then return serialize._maketable(object, opt, level) elseif type(object) == "function" then @@ -184,10 +169,14 @@ function serialize.save(object, opt) -- init options if opt == true then opt = { strip = true, binary = false, indent = false } - elseif opt == false or opt == nil then - opt = { strip = false, binary = false, indent = true } + elseif not opt then + opt = {} end + if opt.strip == nil then opt.strip = false end + if opt.binary == nil then opt.binary = false end + if opt.indent == nil then opt.indent = true end + -- init indent, from nil, boolean, number or string to false or string if not opt.indent then -- no indent @@ -258,7 +247,7 @@ function serialize._load(str) end -- load string - local script, errors = loadstring(str, "=(deserializing data)") + local script, errors = load(str, "=(deserializing data)", binary and "b" or "t", _ENV) if script then -- load object local ok, object = pcall(script) @@ -306,4 +295,5 @@ function serialize.load(str) end -- return module: serialize +serialize._ENV = _ENV return serialize -- cgit v1.3.1 From d0216ee6b330bfa8fe894210f8d6bc1517d7e42d Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Fri, 26 Jul 2019 13:27:28 +0800 Subject: add stub --- xmake/core/base/serialize.lua | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/xmake/core/base/serialize.lua b/xmake/core/base/serialize.lua index 252a223cc..8e57661ce 100644 --- a/xmake/core/base/serialize.lua +++ b/xmake/core/base/serialize.lua @@ -25,6 +25,7 @@ local _ENV = serialize._ENV or {} -- load modules local math = require("base/math") +local table = require("base/table") -- save original interfaces serialize._dump = serialize._dump or string._dump or string.dump @@ -33,6 +34,36 @@ serialize._dump = serialize._dump or string._dump or string.dump _ENV.nan = math.nan _ENV.inf = math.huge +function serialize._createstub(resolver, ...) + _ENV.has_stub = true + local params = table.pack(...) + return function(root, env) + return resolver(root, env, table.unpack(params, 1, params.n)) + end +end + +function serialize._resolvestub(object, root, env) + if type(object) == "function" then + local ok, result, errors = pcall(object, root, env) + if ok and errors == nil then + return result + end + return nil, errors or result or "unspecified error" + end + if type(object) ~= "table" then + return object + end + + for k, v in pairs(object) do + local result, errors = serialize._resolvestub(v, root, env) + if errors ~= nil then + return nil, errors + end + object[k] = result + end + return object +end + function serialize._makestring(str, opt, level) return string.format("%q", str) end @@ -133,12 +164,16 @@ function serialize._makefunction(func, opt, level) return string.format("func%q", funccode) end +function serialize._resolvefunction(root, env, funccode) + return load(funccode, "=(deserialized code)", "b", env) +end + -- load function function _ENV.func(funccode) -- type guard assert(type(funccode) == "string", "func should called with a string") -- load func - return loadstring(funccode, "=(deserialized code)") + return serialize._createstub(serialize._resolvefunction, funccode) end -- make string with the level @@ -253,6 +288,11 @@ function serialize._load(str) local ok, object = pcall(script) if ok then result = object + if _ENV.has_stub then + _ENV.has_stub = false + local env = debug.getfenv(debug.getinfo(3, "f").func) + result, errors = serialize._resolvestub(result, result, env) + end else -- error errors = tostring(object) -- cgit v1.3.1 From 447d9b62f61a3bd20349783835e01fa73d8d5747 Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Fri, 26 Jul 2019 14:05:49 +0800 Subject: support ref loop --- tests/modules/string/serialize/test.lua | 15 ++++++ xmake/core/base/serialize.lua | 82 ++++++++++++++++++++++++++------- 2 files changed, 81 insertions(+), 16 deletions(-) diff --git a/tests/modules/string/serialize/test.lua b/tests/modules/string/serialize/test.lua index 567bee87f..ab8986867 100644 --- a/tests/modules/string/serialize/test.lua +++ b/tests/modules/string/serialize/test.lua @@ -43,3 +43,18 @@ function test_function(t) t:are_equal(roundtrip({function() return {{1, 2, 3, nil, 4}} end})[1](), {{1, 2, 3, nil, 4}}) t:are_equal(roundtrip({{function() return {{1, 2, 3, nil, 4}} end}})[1][1](), {{1, 2, 3, nil, 4}}) end + +function test_refloop(t) + local l1 = {} + l1.l = l1 + local r1 = roundtrip(l1) + t:are_same(r1.l, r1) + + local l2 = {{1}, {2}, {3}} + l2[1].l = { root = l2, a = l2[1], b = l2[2], c = l2[3] } + local r2 = roundtrip(l2) + t:are_same(r2[1].l.root, r2) + t:are_same(r2[1].l.a, r2[1]) + t:are_same(r2[1].l.b, r2[2]) + t:are_same(r2[1].l.c, r2[3]) +end diff --git a/xmake/core/base/serialize.lua b/xmake/core/base/serialize.lua index 8e57661ce..c178fec9e 100644 --- a/xmake/core/base/serialize.lua +++ b/xmake/core/base/serialize.lua @@ -64,15 +64,20 @@ function serialize._resolvestub(object, root, env) return object end -function serialize._makestring(str, opt, level) +function serialize._makestring(str, opt) return string.format("%q", str) end -function serialize._makedefault(val, opt, level) +function serialize._makedefault(val, opt) return tostring(val) end -function serialize._maketable(object, opt, level) +function serialize._maketable(object, opt, level, path, reftab) + + level = level or 0 + reftab = reftab or {} + path = path or {} + reftab[object] = table.copy(path) -- serialize child items local childlevel = level + 1 @@ -81,6 +86,7 @@ function serialize._maketable(object, opt, level) local isarr = true local maxn = 0 for k, v in pairs(object) do + -- check key if type(k) == "number" then -- only checks when it may be an array if isarr then @@ -96,7 +102,20 @@ function serialize._maketable(object, opt, level) else return nil, string.format("cannot serialize table with key of %s: <%s>", type(k), k) end - local sval, err = serialize._make(v, opt, childlevel) + + -- serialize value + local sval, err + if type(v) == "table" then + if reftab[v] then + sval, err = serialize._makeref(reftab[v], opt) + else + table.insert(path, k) + sval, err = serialize._maketable(v, opt, childlevel, path, reftab) + table.remove(path) + end + else + sval, err = serialize._make(v, opt) + end if err ~= nil then return nil, err end @@ -134,6 +153,7 @@ function serialize._maketable(object, opt, level) else local con = opt.indent and " = " or "=" for k, v in pairs(serialized) do + -- serialize key if type(k) == "string" then if not k:match("^[%a_][%w_]*$") then k = string.format("[%q]", k) @@ -145,6 +165,7 @@ function serialize._maketable(object, opt, level) end k = string.format("[%s]", nval) end + -- concat k = v table.insert(bodystrs, k .. con .. v) end end @@ -155,7 +176,7 @@ function serialize._maketable(object, opt, level) return headstr .. table.concat(bodystrs, opt.indent and (",\n" .. indent .. opt.indent) or ",") .. tailstr end -function serialize._makefunction(func, opt, level) +function serialize._makefunction(func, opt) local ok, funccode = pcall(serialize._dump, func, opt.strip) if not ok then @@ -176,18 +197,51 @@ function _ENV.func(funccode) return serialize._createstub(serialize._resolvefunction, funccode) end +function serialize._makeref(path, opt) + + -- root reference + if path[1] == nil then + return "ref()" + end + + local ppath = {} + for i, v in ipairs(path) do + ppath[i] = serialize._make(v, opt) + end + + return "ref(" .. table.concat(ppath, opt.indent and ", " or ",") .. ")" +end + +function serialize._resolveref(root, env, ...) + local pos = root + for i, v in ipairs({...}) do + if type(pos) ~= "table" then + return nil, "unable to resolve path: ." .. table.concat(path, ".", 1, i - 1) .. " is " .. tostring(pos) + end + pos = pos[v] + end + return pos +end + +-- reference +function _ENV.ref(...) + -- load func + return serialize._createstub(serialize._resolveref, ...) +end + + -- make string with the level -function serialize._make(object, opt, level) +function serialize._make(object, opt) -- call make* by type if type(object) == "string" then - return serialize._makestring(object, opt, level) + return serialize._makestring(object, opt) elseif type(object) == "boolean" or type(object) == "nil" or type(object) == "number" then - return serialize._makedefault(object, opt, level) + return serialize._makedefault(object, opt) elseif type(object) == "table" then - return serialize._maketable(object, opt, level) + return serialize._maketable(object, opt) elseif type(object) == "function" then - return serialize._makefunction(object, opt, level) + return serialize._makefunction(object, opt) else return nil, string.format("cannot serialize %s: <%s>", type(object), object) end @@ -238,13 +292,9 @@ function serialize.save(object, opt) end -- make string - local ok, result, errors = pcall(serialize._make, object, opt, 0) + local ok, result, errors = pcall(serialize._make, object, opt) if not ok then - if result:find("stack overflow", 1, true) then - errors = "cannot serialize: reference loop found" - else - errors = "cannot serialize: " .. result - end + errors = "cannot serialize: " .. result end -- ok? -- cgit v1.3.1 From 4eccb9fa09f347f1b5cf64cb085a28599b2e010d Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Fri, 26 Jul 2019 14:30:42 +0800 Subject: seperate env for each call --- xmake/core/base/serialize.lua | 73 ++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/xmake/core/base/serialize.lua b/xmake/core/base/serialize.lua index c178fec9e..d6bd3885e 100644 --- a/xmake/core/base/serialize.lua +++ b/xmake/core/base/serialize.lua @@ -21,7 +21,6 @@ -- define module: serialize local serialize = serialize or {} -local _ENV = serialize._ENV or {} -- load modules local math = require("base/math") @@ -30,21 +29,17 @@ local table = require("base/table") -- save original interfaces serialize._dump = serialize._dump or string._dump or string.dump --- init env -_ENV.nan = math.nan -_ENV.inf = math.huge - -function serialize._createstub(resolver, ...) - _ENV.has_stub = true +function serialize._createstub(resolver, env, ...) + env.has_stub = true local params = table.pack(...) - return function(root, env) - return resolver(root, env, table.unpack(params, 1, params.n)) + return function(root, fenv) + return resolver(root, fenv, table.unpack(params, 1, params.n)) end end -function serialize._resolvestub(object, root, env) +function serialize._resolvestub(object, root, fenv) if type(object) == "function" then - local ok, result, errors = pcall(object, root, env) + local ok, result, errors = pcall(object, root, fenv) if ok and errors == nil then return result end @@ -177,7 +172,6 @@ function serialize._maketable(object, opt, level, path, reftab) end function serialize._makefunction(func, opt) - local ok, funccode = pcall(serialize._dump, func, opt.strip) if not ok then return nil, string.format("%s: <%s>", funccode, func) @@ -186,17 +180,14 @@ function serialize._makefunction(func, opt) end function serialize._resolvefunction(root, env, funccode) + -- check + if type(funccode) ~= "string" then + return nil, "func should called with a string" + end + -- resolve return load(funccode, "=(deserialized code)", "b", env) end --- load function -function _ENV.func(funccode) - -- type guard - assert(type(funccode) == "string", "func should called with a string") - -- load func - return serialize._createstub(serialize._resolvefunction, funccode) -end - function serialize._makeref(path, opt) -- root reference @@ -215,6 +206,9 @@ end function serialize._resolveref(root, env, ...) local pos = root for i, v in ipairs({...}) do + if type(v) ~= "string" and type(v) ~= "number" then + return nil, "path segments in ref should be string or number" + end if type(pos) ~= "table" then return nil, "unable to resolve path: ." .. table.concat(path, ".", 1, i - 1) .. " is " .. tostring(pos) end @@ -223,13 +217,6 @@ function serialize._resolveref(root, env, ...) return pos end --- reference -function _ENV.ref(...) - -- load func - return serialize._createstub(serialize._resolveref, ...) -end - - -- make string with the level function serialize._make(object, opt) @@ -321,6 +308,27 @@ function serialize.save(object, opt) return (#dump < #result) and dump or result end +function serialize._createenv() + + -- init env + local env = { nan = math.nan, inf = math.huge } + + -- resolve reference + function env.ref(...) + -- load ref + return serialize._createstub(serialize._resolveref, env, ...) + end + + -- load function + function env.func(funccode) + -- load func + return serialize._createstub(serialize._resolvefunction, env, funccode) + end + + -- return new env + return env +end + -- load table from string in table function serialize._load(str) @@ -332,16 +340,16 @@ function serialize._load(str) end -- load string - local script, errors = load(str, "=(deserializing data)", binary and "b" or "t", _ENV) + local env = serialize._createenv() + local script, errors = load(str, "=(deserializing data)", binary and "b" or "t", env) if script then -- load object local ok, object = pcall(script) if ok then result = object - if _ENV.has_stub then - _ENV.has_stub = false - local env = debug.getfenv(debug.getinfo(3, "f").func) - result, errors = serialize._resolvestub(result, result, env) + if env.has_stub then + local fenv = debug.getfenv(debug.getinfo(3, "f").func) + result, errors = serialize._resolvestub(result, result, fenv) end else -- error @@ -385,5 +393,4 @@ function serialize.load(str) end -- return module: serialize -serialize._ENV = _ENV return serialize -- cgit v1.3.1 From cff42b3aeb348d0a193c24f244ddec003aded06f Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Fri, 26 Jul 2019 14:42:05 +0800 Subject: restore upvalue --- xmake/core/base/serialize.lua | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/xmake/core/base/serialize.lua b/xmake/core/base/serialize.lua index d6bd3885e..6e652384a 100644 --- a/xmake/core/base/serialize.lua +++ b/xmake/core/base/serialize.lua @@ -184,8 +184,22 @@ function serialize._resolvefunction(root, env, funccode) if type(funccode) ~= "string" then return nil, "func should called with a string" end - -- resolve - return load(funccode, "=(deserialized code)", "b", env) + + -- resolve funccode + local func, err = load(funccode, "=(deserialized code)", "b", env) + if err ~= nil then + return nil, err + end + + -- try restore upvalues + for i = 1, math.huge do + local upname = debug.getupvalue(func, i) + if upname == nil or upname == "" then + break + end + debug.setupvalue(func, i, env[upname]) + end + return func end function serialize._makeref(path, opt) @@ -320,9 +334,9 @@ function serialize._createenv() end -- load function - function env.func(funccode) + function env.func(...) -- load func - return serialize._createstub(serialize._resolvefunction, env, funccode) + return serialize._createstub(serialize._resolvefunction, env, ...) end -- return new env -- cgit v1.3.1 From d3667d71c86861ffb910af8ba8ecd930f75bf9e7 Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Fri, 26 Jul 2019 16:25:52 +0800 Subject: fix up value --- tests/modules/string/serialize/test.lua | 3 +++ tests/test_utils/test_assert.lua | 2 +- xmake/core/base/serialize.lua | 20 +++++++++++--------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/modules/string/serialize/test.lua b/tests/modules/string/serialize/test.lua index ab8986867..66b216566 100644 --- a/tests/modules/string/serialize/test.lua +++ b/tests/modules/string/serialize/test.lua @@ -42,6 +42,9 @@ function test_function(t) t:are_equal(roundtrip(function() return {{1, 2, 3, nil, 4}} end)(), {{1, 2, 3, nil, 4}}) t:are_equal(roundtrip({function() return {{1, 2, 3, nil, 4}} end})[1](), {{1, 2, 3, nil, 4}}) t:are_equal(roundtrip({{function() return {{1, 2, 3, nil, 4}} end}})[1][1](), {{1, 2, 3, nil, 4}}) + + t:will_raise(function() string.serialize({io.open}):deserialize()[1]("test.lua") end, "attempt to call global 'setmetatable' (a nil value)") + t:will_raise(function() string.serialize({io.open}):deserialize()[1]("not/a/file") end, "failed to open file: not/a/file") end function test_refloop(t) diff --git a/tests/test_utils/test_assert.lua b/tests/test_utils/test_assert.lua index a964fcfb3..1cf0e9386 100644 --- a/tests/test_utils/test_assert.lua +++ b/tests/test_utils/test_assert.lua @@ -57,7 +57,7 @@ function test_assert:will_raise(func, message_pattern) local funcs = { func, unpack(self._will_raise_stack) } if ok then self:print_error("expected raise but finished successfully", funcs) - elseif message_pattern and not string.find(error, message_pattern) then + elseif message_pattern and not error:find(message_pattern, 1, true) and not error:find(message_pattern) then self:print_error(format("expected raise with message ${green}%s${reset} but got ${red}%s${reset}", message_pattern, error), funcs) end end diff --git a/xmake/core/base/serialize.lua b/xmake/core/base/serialize.lua index 6e652384a..f5e078da0 100644 --- a/xmake/core/base/serialize.lua +++ b/xmake/core/base/serialize.lua @@ -50,7 +50,7 @@ function serialize._resolvestub(object, root, fenv) end for k, v in pairs(object) do - local result, errors = serialize._resolvestub(v, root, env) + local result, errors = serialize._resolvestub(v, root, fenv) if errors ~= nil then return nil, errors end @@ -179,25 +179,27 @@ function serialize._makefunction(func, opt) return string.format("func%q", funccode) end -function serialize._resolvefunction(root, env, funccode) +function serialize._resolvefunction(root, fenv, funccode) -- check if type(funccode) ~= "string" then return nil, "func should called with a string" end -- resolve funccode - local func, err = load(funccode, "=(deserialized code)", "b", env) + local func, err = load(funccode, "=(deserialized code)", "b", fenv) if err ~= nil then return nil, err end -- try restore upvalues - for i = 1, math.huge do - local upname = debug.getupvalue(func, i) - if upname == nil or upname == "" then - break + if fenv then + for i = 1, math.huge do + local upname = debug.getupvalue(func, i) + if upname == nil or upname == "" then + break + end + debug.setupvalue(func, i, fenv[upname]) end - debug.setupvalue(func, i, env[upname]) end return func end @@ -217,7 +219,7 @@ function serialize._makeref(path, opt) return "ref(" .. table.concat(ppath, opt.indent and ", " or ",") .. ")" end -function serialize._resolveref(root, env, ...) +function serialize._resolveref(root, fenv, ...) local pos = root for i, v in ipairs({...}) do if type(v) ~= "string" and type(v) ~= "number" then -- cgit v1.3.1 From 3be7a9e64378782f90854f3b00f5af8de409ae10 Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Fri, 26 Jul 2019 16:37:45 +0800 Subject: add unit test of serialize function --- tests/modules/string/serialize/test.lua | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/modules/string/serialize/test.lua b/tests/modules/string/serialize/test.lua index 66b216566..0d72e721e 100644 --- a/tests/modules/string/serialize/test.lua +++ b/tests/modules/string/serialize/test.lua @@ -43,8 +43,24 @@ function test_function(t) t:are_equal(roundtrip({function() return {{1, 2, 3, nil, 4}} end})[1](), {{1, 2, 3, nil, 4}}) t:are_equal(roundtrip({{function() return {{1, 2, 3, nil, 4}} end}})[1][1](), {{1, 2, 3, nil, 4}}) - t:will_raise(function() string.serialize({io.open}):deserialize()[1]("test.lua") end, "attempt to call global 'setmetatable' (a nil value)") - t:will_raise(function() string.serialize({io.open}):deserialize()[1]("not/a/file") end, "failed to open file: not/a/file") + -- x in fenv + x = {} + -- return x in fenv + function f() return x end + -- fenv will restore + t:are_same(roundtrip(f)(), x) + + y = {} + -- y in fenv + local g_y = y + -- y in upvalue + local y = {} + -- return y in upvalue + function g() return y end + -- upvalue will not restore if striped + t:are_same(roundtrip(g)(), nil) + -- upvalue will be restored by fenv, so y in fenv is returned + t:are_same(string.serialize(g):deserialize()(), g_y) end function test_refloop(t) -- cgit v1.3.1 From ab8e62f99a161981fbb485aaa895e429b8c31144 Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Fri, 26 Jul 2019 19:40:51 +0800 Subject: improve test --- tests/modules/string/serialize/test.lua | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/tests/modules/string/serialize/test.lua b/tests/modules/string/serialize/test.lua index 0d72e721e..1fcb62f66 100644 --- a/tests/modules/string/serialize/test.lua +++ b/tests/modules/string/serialize/test.lua @@ -1,10 +1,22 @@ +function roundtripimpl(value, opt) + local s, serr = string.serialize(value, opt) + if serr then + raise(serr) + end + local v, verr = s:deserialize() + if verr then + raise(verr) + end + return v +end + function roundtrip(round0) - local round1 = string.serialize(round0, false):deserialize() - local round2 = string.serialize(round1, true):deserialize() - local round3 = string.serialize(round2, {binary=true}):deserialize() - local round4 = string.serialize(round3, {indent=16}):deserialize() - local round5 = string.serialize(round4, {indent=" \r\n\t"}):deserialize() + local round1 = roundtripimpl(round0, false) + local round2 = roundtripimpl(round1, true) + local round3 = roundtripimpl(round2, {binary=true}) + local round4 = roundtripimpl(round3, {indent=16}) + local round5 = roundtripimpl(round4, {indent=" \r\n\t"}) return round5 end @@ -60,7 +72,7 @@ function test_function(t) -- upvalue will not restore if striped t:are_same(roundtrip(g)(), nil) -- upvalue will be restored by fenv, so y in fenv is returned - t:are_same(string.serialize(g):deserialize()(), g_y) + t:are_same(roundtripimpl(g)(), g_y) end function test_refloop(t) -- cgit v1.3.1 From ef1b33a90b2fcb87c070de07d22ed89d830fcbd0 Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Fri, 26 Jul 2019 20:37:21 +0800 Subject: fix keyword as key --- tests/modules/string/serialize/test.lua | 2 + xmake/core/base/serialize.lua | 106 ++++++++++++++++++-------------- 2 files changed, 63 insertions(+), 45 deletions(-) diff --git a/tests/modules/string/serialize/test.lua b/tests/modules/string/serialize/test.lua index 1fcb62f66..3c21d4410 100644 --- a/tests/modules/string/serialize/test.lua +++ b/tests/modules/string/serialize/test.lua @@ -41,6 +41,8 @@ end function test_table(t) t:are_equal(roundtrip({}), {}) + t:are_equal(roundtrip({{},{1}}), {{},{1}}) + t:are_equal(roundtrip({["true"] = true}), {["true"] = true}) t:are_equal(roundtrip({1, 2, 3}), {1, 2, 3}) t:are_equal(roundtrip({1, "", 3}), {1, "", 3}) t:are_equal(roundtrip({{1, 2, 3, nil, 4}}), {{1, 2, 3, nil, 4}}) diff --git a/xmake/core/base/serialize.lua b/xmake/core/base/serialize.lua index f5e078da0..59feae24a 100644 --- a/xmake/core/base/serialize.lua +++ b/xmake/core/base/serialize.lua @@ -25,40 +25,14 @@ local serialize = serialize or {} -- load modules local math = require("base/math") local table = require("base/table") +local hashset = require("base/hashset") + +-- reserved keywords in lua +local keywords = hashset.of("and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while") -- save original interfaces serialize._dump = serialize._dump or string._dump or string.dump -function serialize._createstub(resolver, env, ...) - env.has_stub = true - local params = table.pack(...) - return function(root, fenv) - return resolver(root, fenv, table.unpack(params, 1, params.n)) - end -end - -function serialize._resolvestub(object, root, fenv) - if type(object) == "function" then - local ok, result, errors = pcall(object, root, fenv) - if ok and errors == nil then - return result - end - return nil, errors or result or "unspecified error" - end - if type(object) ~= "table" then - return object - end - - for k, v in pairs(object) do - local result, errors = serialize._resolvestub(v, root, fenv) - if errors ~= nil then - return nil, errors - end - object[k] = result - end - return object -end - function serialize._makestring(str, opt) return string.format("%q", str) end @@ -117,6 +91,11 @@ function serialize._maketable(object, opt, level, path, reftab) serialized[k] = sval end + -- empty table + if isarr and numidxcount == 0 then + return opt.indent and "{ }" or "{}" + end + -- too sparse if numidxcount * 2 < maxn then isarr = false @@ -128,17 +107,6 @@ function serialize._maketable(object, opt, level, path, reftab) indent = string.rep(opt.indent, level) end - -- make head - local headstr = opt.indent and ("{\n" .. indent .. opt.indent) or "{" - - -- make tail - local tailstr - if opt.indent then - tailstr = "\n" .. indent .. "}" - else - tailstr = "}" - end - -- make body local bodystrs = {} if isarr then @@ -150,11 +118,11 @@ function serialize._maketable(object, opt, level, path, reftab) for k, v in pairs(serialized) do -- serialize key if type(k) == "string" then - if not k:match("^[%a_][%w_]*$") then + if keywords:has(k) or not k:match("^[%a_][%w_]*$") then k = string.format("[%q]", k) end else -- type(k) == "number" - local nval, err = serialize._makedefault(k, opt, childlevel) + local nval, err = serialize._makedefault(k, opt) if err ~= nil then return nil, err end @@ -165,9 +133,18 @@ function serialize._maketable(object, opt, level, path, reftab) end end - if #bodystrs == 0 then - return opt.indent and "{ }" or "{}" + -- make head + local headstr = opt.indent and ("{\n" .. indent .. opt.indent) or "{" + + -- make tail + local tailstr + if opt.indent then + tailstr = "\n" .. indent .. "}" + else + tailstr = "}" end + + -- concat together return headstr .. table.concat(bodystrs, opt.indent and (",\n" .. indent .. opt.indent) or ",") .. tailstr end @@ -324,6 +301,45 @@ function serialize.save(object, opt) return (#dump < #result) and dump or result end +-- called by functions in deserialize environment +-- create a function (called stub) to finish deserialization +function serialize._createstub(resolver, env, ...) + env.has_stub = true + local params = table.pack(...) + return function(root, fenv) + return resolver(root, fenv, table.unpack(params, 1, params.n)) + end +end + +-- after deserialization by load() +-- use this routine to call all stubs in deserialzed data +-- +-- @param object object to search stubs +-- root root object +-- fenv fenv of deserialzer caller +function serialize._resolvestub(object, root, fenv) + if type(object) == "function" then + local ok, result, errors = pcall(object, root, fenv) + if ok and errors == nil then + return result + end + return nil, errors or result or "unspecified error" + end + if type(object) ~= "table" then + return object + end + + for k, v in pairs(object) do + local result, errors = serialize._resolvestub(v, root, fenv) + if errors ~= nil then + return nil, errors + end + object[k] = result + end + return object +end + +-- create a env for deserialze load() call function serialize._createenv() -- init env -- cgit v1.3.1 From bfdfbe9b260824a9254a7be7bb7ffe9f0e9cf3d5 Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Fri, 26 Jul 2019 20:55:55 +0800 Subject: optimize _maketable --- xmake/core/base/serialize.lua | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/xmake/core/base/serialize.lua b/xmake/core/base/serialize.lua index 59feae24a..1a9294a09 100644 --- a/xmake/core/base/serialize.lua +++ b/xmake/core/base/serialize.lua @@ -101,12 +101,6 @@ function serialize._maketable(object, opt, level, path, reftab) isarr = false end - -- make indent - local indent = "" - if opt.indent then - indent = string.rep(opt.indent, level) - end - -- make body local bodystrs = {} if isarr then @@ -114,38 +108,40 @@ function serialize._maketable(object, opt, level, path, reftab) bodystrs[i] = serialized[i] or "nil" end else - local con = opt.indent and " = " or "=" + local dformat = opt.indent and "%s = %s" or "%s=%s" + local sformat = opt.indent and "[%q] = %s" or "[%q]=%s" + local nformat = opt.indent and "[%s] = %s" or "[%s]=%s" for k, v in pairs(serialized) do + local format -- serialize key if type(k) == "string" then if keywords:has(k) or not k:match("^[%a_][%w_]*$") then - k = string.format("[%q]", k) + format = sformat + else + format = dformat end else -- type(k) == "number" - local nval, err = serialize._makedefault(k, opt) - if err ~= nil then - return nil, err - end - k = string.format("[%s]", nval) + format = nformat end -- concat k = v - table.insert(bodystrs, k .. con .. v) + table.insert(bodystrs, string.format(format, k, v)) end end - -- make head - local headstr = opt.indent and ("{\n" .. indent .. opt.indent) or "{" - - -- make tail - local tailstr + -- make head and tail + local headstr, bodysep, tailstr if opt.indent then - tailstr = "\n" .. indent .. "}" + local indent = "\n" .. string.rep(opt.indent, level) + tailstr = indent .. "}" + indent = indent .. opt.indent + headstr = "{" .. indent + bodysep = "," .. indent else - tailstr = "}" + headstr, bodysep, tailstr = "{", ",", "}" end -- concat together - return headstr .. table.concat(bodystrs, opt.indent and (",\n" .. indent .. opt.indent) or ",") .. tailstr + return headstr .. table.concat(bodystrs, bodysep) .. tailstr end function serialize._makefunction(func, opt) -- cgit v1.3.1 From 5f72124406a72da23a816b73c5fa0bd5ec404e57 Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Sat, 27 Jul 2019 16:27:30 +0800 Subject: fix serialize --- xmake/core/base/serialize.lua | 58 +++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/xmake/core/base/serialize.lua b/xmake/core/base/serialize.lua index 1a9294a09..f41b1de2f 100644 --- a/xmake/core/base/serialize.lua +++ b/xmake/core/base/serialize.lua @@ -223,6 +223,35 @@ function serialize._make(object, opt) end end +function serialize._generateindent(indent) + + -- init indent, from nil, boolean, number or string to false or string + if not indent then + -- no indent + return false + elseif type(indent) == "boolean" then -- true + -- 4 spaces + return " " + elseif type(indent) == "number" then + if indent < 0 then + return false + elseif indent > 20 then + return nil, "invalid opt.indent, too large" + else + -- indent spaces + return string.rep(" ", indent) + end + elseif type(indent) == "string" then + -- only whitespaces allowed + if not (indent:trim() == "") then + return nil, "invalid opt.indent, only whitespaces are accepted" + end + return indent + else + return nil, "invalid opt.indent, should be boolean, number or string" + end +end + -- serialize to string from the given object -- -- @param opt serialize options @@ -242,30 +271,11 @@ function serialize.save(object, opt) if opt.binary == nil then opt.binary = false end if opt.indent == nil then opt.indent = true end - -- init indent, from nil, boolean, number or string to false or string - if not opt.indent then - -- no indent - opt.indent = false - elseif type(opt.indent) == "boolean" then -- true - -- 4 spaces - opt.indent = " " - elseif type(opt.indent) == "number" then - if opt.indent < 0 then - opt.indent = false - elseif opt.indent > 20 then - return nil, "invalid opt.indent, too large" - else - -- opt.indent spaces - opt.indent = string.rep(" ", opt.indent) - end - elseif type(opt.indent) == "string" then - -- only whitespaces allowed - if not opt.indent:match("^%s+$") then - return nil, "invalid opt.indent, only whitespaces are accepted" - end - else - return nil, "invalid opt.indent, should be boolean, number or string" + local indent, ierrors = serialize._generateindent(opt.indent) + if ierrors then + return nil, ierrors end + opt.indent = indent -- make string local ok, result, errors = pcall(serialize._make, object, opt) @@ -283,7 +293,7 @@ function serialize.save(object, opt) end -- binary mode - local func, lerr = loadstring("return " .. result) + local func, lerr = loadstring("return " .. result, "=") if lerr ~= nil then return nil, lerr end -- cgit v1.3.1 From 72f398835b6846c62a0d8a2a3d442689bf232df4 Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Sat, 27 Jul 2019 17:08:06 +0800 Subject: use table as stub --- xmake/core/base/dump.lua | 4 ++-- xmake/core/base/serialize.lua | 45 +++++++++++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/xmake/core/base/dump.lua b/xmake/core/base/dump.lua index ce3b87b91..cac43ebf3 100644 --- a/xmake/core/base/dump.lua +++ b/xmake/core/base/dump.lua @@ -248,7 +248,7 @@ end function dump._print_udata(value, first_indent, remain_indent, printed_set) local first_level - local metatable = getmetatable(value) + local metatable = debug.getmetatable(value) printed_set, first_level = dump._get_printed_set(printed_set, metatable) io.write(first_indent) @@ -277,7 +277,7 @@ function dump._print_table(value, first_indent, remain_indent, printed_set) local first_level printed_set, first_level = dump._get_printed_set(printed_set, value) io.write(first_indent) - local metatable = getmetatable(value) + local metatable = debug.getmetatable(value) local tostringmethod = metatable and rawget(metatable, "__tostring") if not first_level and tostringmethod then local ok, strrep = pcall(tostringmethod, value, value) diff --git a/xmake/core/base/serialize.lua b/xmake/core/base/serialize.lua index f41b1de2f..9faf1079c 100644 --- a/xmake/core/base/serialize.lua +++ b/xmake/core/base/serialize.lua @@ -20,7 +20,11 @@ -- -- define module: serialize -local serialize = serialize or {} +local serialize = serialize or {} +local stub = serialize._stub or {} +stub.isstub = setmetatable({}, { __tostring = function() return "stub indentifier" end }) +stub.__index = stub +serialize._stub = stub -- load modules local math = require("base/math") @@ -194,12 +198,15 @@ end function serialize._resolveref(root, fenv, ...) local pos = root - for i, v in ipairs({...}) do + local path = table.pack(...) + for i = 1, path.n do + local v = path[i] if type(v) ~= "string" and type(v) ~= "number" then return nil, "path segments in ref should be string or number" end if type(pos) ~= "table" then - return nil, "unable to resolve path: ." .. table.concat(path, ".", 1, i - 1) .. " is " .. tostring(pos) + table.insert(path, 1, "") + return nil, "unable to resolve path: " .. table.concat(path, ".", 1, i) .. " is " .. tostring(pos) end pos = pos[v] end @@ -307,14 +314,23 @@ function serialize.save(object, opt) return (#dump < #result) and dump or result end +function stub:__call(root, fenv) + return self.resolver(root, fenv, table.unpack(self.params, 1, self.params.n)) +end + +function stub:__tostring() + local fparams = {} + for i = 1, self.params.n do + fparams[i] = serialize._make(self.params[i]) + end + return string.format("%s(%s)", self.name, table.concat(fparams, ", ")) +end + -- called by functions in deserialize environment -- create a function (called stub) to finish deserialization -function serialize._createstub(resolver, env, ...) +function serialize._createstub(name, resolver, env, ...) env.has_stub = true - local params = table.pack(...) - return function(root, fenv) - return resolver(root, fenv, table.unpack(params, 1, params.n)) - end + return setmetatable({ name = name, resolver = resolver, params = table.pack(...)}, stub) end -- after deserialization by load() @@ -324,16 +340,17 @@ end -- root root object -- fenv fenv of deserialzer caller function serialize._resolvestub(object, root, fenv) - if type(object) == "function" then + if type(object) ~= "table" then + return object + end + + if object.isstub == stub.isstub then local ok, result, errors = pcall(object, root, fenv) if ok and errors == nil then return result end return nil, errors or result or "unspecified error" end - if type(object) ~= "table" then - return object - end for k, v in pairs(object) do local result, errors = serialize._resolvestub(v, root, fenv) @@ -354,13 +371,13 @@ function serialize._createenv() -- resolve reference function env.ref(...) -- load ref - return serialize._createstub(serialize._resolveref, env, ...) + return serialize._createstub("ref", serialize._resolveref, env, ...) end -- load function function env.func(...) -- load func - return serialize._createstub(serialize._resolvefunction, env, ...) + return serialize._createstub("func", serialize._resolvefunction, env, ...) end -- return new env -- cgit v1.3.1 From 60a3dbc00210e298f3d9722243bdd4b070735f49 Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Sat, 27 Jul 2019 17:32:21 +0800 Subject: change order --- xmake/core/base/serialize.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xmake/core/base/serialize.lua b/xmake/core/base/serialize.lua index 9faf1079c..6a7bafc08 100644 --- a/xmake/core/base/serialize.lua +++ b/xmake/core/base/serialize.lua @@ -22,8 +22,6 @@ -- define module: serialize local serialize = serialize or {} local stub = serialize._stub or {} -stub.isstub = setmetatable({}, { __tostring = function() return "stub indentifier" end }) -stub.__index = stub serialize._stub = stub -- load modules @@ -314,6 +312,10 @@ function serialize.save(object, opt) return (#dump < #result) and dump or result end +-- init stub metatable +stub.isstub = setmetatable({}, { __tostring = function() return "stub indentifier" end }) +stub.__index = stub + function stub:__call(root, fenv) return self.resolver(root, fenv, table.unpack(self.params, 1, self.params.n)) end -- cgit v1.3.1 From 7da50f74093bac43174a6ff236ccd72680a5ec81 Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Sat, 27 Jul 2019 18:39:41 +0800 Subject: fix indent --- xmake/core/base/serialize.lua | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/xmake/core/base/serialize.lua b/xmake/core/base/serialize.lua index 6a7bafc08..16159bb93 100644 --- a/xmake/core/base/serialize.lua +++ b/xmake/core/base/serialize.lua @@ -95,7 +95,7 @@ function serialize._maketable(object, opt, level, path, reftab) -- empty table if isarr and numidxcount == 0 then - return opt.indent and "{ }" or "{}" + return opt.indentstr and "{ }" or "{}" end -- too sparse @@ -110,9 +110,9 @@ function serialize._maketable(object, opt, level, path, reftab) bodystrs[i] = serialized[i] or "nil" end else - local dformat = opt.indent and "%s = %s" or "%s=%s" - local sformat = opt.indent and "[%q] = %s" or "[%q]=%s" - local nformat = opt.indent and "[%s] = %s" or "[%s]=%s" + local dformat = opt.indentstr and "%s = %s" or "%s=%s" + local sformat = opt.indentstr and "[%q] = %s" or "[%q]=%s" + local nformat = opt.indentstr and "[%s] = %s" or "[%s]=%s" for k, v in pairs(serialized) do local format -- serialize key @@ -132,10 +132,10 @@ function serialize._maketable(object, opt, level, path, reftab) -- make head and tail local headstr, bodysep, tailstr - if opt.indent then - local indent = "\n" .. string.rep(opt.indent, level) + if opt.indentstr then + local indent = "\n" .. string.rep(opt.indentstr, level) tailstr = indent .. "}" - indent = indent .. opt.indent + indent = indent .. opt.indentstr headstr = "{" .. indent bodysep = "," .. indent else @@ -191,7 +191,7 @@ function serialize._makeref(path, opt) ppath[i] = serialize._make(v, opt) end - return "ref(" .. table.concat(ppath, opt.indent and ", " or ",") .. ")" + return "ref(" .. table.concat(ppath, opt.indentstr and ", " or ",") .. ")" end function serialize._resolveref(root, fenv, ...) @@ -228,18 +228,18 @@ function serialize._make(object, opt) end end -function serialize._generateindent(indent) +function serialize._generateindentstr(indent) -- init indent, from nil, boolean, number or string to false or string if not indent then -- no indent - return false - elseif type(indent) == "boolean" then -- true + return nil + elseif indent == true then -- 4 spaces return " " elseif type(indent) == "number" then if indent < 0 then - return false + return nil elseif indent > 20 then return nil, "invalid opt.indent, too large" else @@ -276,11 +276,11 @@ function serialize.save(object, opt) if opt.binary == nil then opt.binary = false end if opt.indent == nil then opt.indent = true end - local indent, ierrors = serialize._generateindent(opt.indent) + local indent, ierrors = serialize._generateindentstr(opt.indent) if ierrors then return nil, ierrors end - opt.indent = indent + opt.indentstr = indent -- make string local ok, result, errors = pcall(serialize._make, object, opt) -- cgit v1.3.1 From fb9a434ada47e47e543f62e353c7375f93f82c39 Mon Sep 17 00:00:00 2001 From: OpportunityLiu Date: Sun, 28 Jul 2019 14:47:58 +0800 Subject: use cache for keywords --- xmake/core/base/serialize.lua | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/xmake/core/base/serialize.lua b/xmake/core/base/serialize.lua index 16159bb93..de80350cd 100644 --- a/xmake/core/base/serialize.lua +++ b/xmake/core/base/serialize.lua @@ -30,7 +30,14 @@ local table = require("base/table") local hashset = require("base/hashset") -- reserved keywords in lua -local keywords = hashset.of("and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while") +function serialize._keywords() + local keywords = serialize._KEYWORDS + if not keywords then + keywords = hashset.of("and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while") + serialize._KEYWORDS = keywords + end + return keywords +end -- save original interfaces serialize._dump = serialize._dump or string._dump or string.dump @@ -113,6 +120,7 @@ function serialize._maketable(object, opt, level, path, reftab) local dformat = opt.indentstr and "%s = %s" or "%s=%s" local sformat = opt.indentstr and "[%q] = %s" or "[%q]=%s" local nformat = opt.indentstr and "[%s] = %s" or "[%s]=%s" + local keywords = serialize._keywords() for k, v in pairs(serialized) do local format -- serialize key -- cgit v1.3.1