Improved handling of failures in __tostring (pkulchenko/ZeroBraneStudio#446).

This commit is contained in:
Paul Kulchenko 2015-05-06 23:29:48 -07:00
parent d625641f65
commit 664b6eebea
2 changed files with 10 additions and 3 deletions

View File

@ -1,4 +1,4 @@
local n, v = "serpent", 0.28 -- (C) 2012-15 Paul Kulchenko; MIT License local n, v = "serpent", 0.281 -- (C) 2012-15 Paul Kulchenko; MIT License
local c, d = "Paul Kulchenko", "Lua serializer and pretty printer" local c, d = "Paul Kulchenko", "Lua serializer and pretty printer"
local snum = {[tostring(1/0)]='1/0 --[[math.huge]]',[tostring(-1/0)]='-1/0 --[[-math.huge]]',[tostring(0/0)]='0/0'} local snum = {[tostring(1/0)]='1/0 --[[math.huge]]',[tostring(-1/0)]='-1/0 --[[-math.huge]]',[tostring(0/0)]='0/0'}
local badtype = {thread = true, userdata = true, cdata = true} local badtype = {thread = true, userdata = true, cdata = true}
@ -22,7 +22,7 @@ local function s(t, opts)
local function safestr(s) return type(s) == "number" and tostring(huge and snum[tostring(s)] or s) local function safestr(s) return type(s) == "number" and tostring(huge and snum[tostring(s)] or s)
or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026 or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026
or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end
local function comment(s,l) return comm and (l or 0) < comm and ' --[['..tostring(s)..']]' or '' end local function comment(s,l) return comm and (l or 0) < comm and ' --[['..select(2, pcall(tostring, s))..']]' or '' end
local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal
and safestr(select(2, pcall(tostring, s))) or error("Can't serialize "..tostring(s)) end and safestr(select(2, pcall(tostring, s))) or error("Can't serialize "..tostring(s)) end
local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r'] local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r']
@ -46,7 +46,9 @@ local function s(t, opts)
if seen[t] then -- already seen this element if seen[t] then -- already seen this element
sref[#sref+1] = spath..space..'='..space..seen[t] sref[#sref+1] = spath..space..'='..space..seen[t]
return tag..'nil'..comment('ref', level) end return tag..'nil'..comment('ref', level) end
if type(mt) == 'table' and (mt.__serialize or mt.__tostring) then -- knows how to serialize itself -- protect from those cases where __tostring may fail
if type(mt) == 'table' and pcall(function() return mt.__tostring and mt.__tostring(t) end)
and (mt.__serialize or mt.__tostring) then -- knows how to serialize itself
seen[t] = insref or spath seen[t] = insref or spath
if mt.__serialize then t = mt.__serialize(t) else t = tostring(t) end if mt.__serialize then t = mt.__serialize(t) else t = tostring(t) end
ttype = type(t) end -- new value falls through to be serialized ttype = type(t) end -- new value falls through to be serialized

View File

@ -303,6 +303,11 @@ do
assert(_a[0] == 0, "table with array and hash parts has the right order of elements 4/4: failed") assert(_a[0] == 0, "table with array and hash parts has the right order of elements 4/4: failed")
end end
do
local a = {setmetatable({abc = 123}, {__tostring = function() error("meta!") end})}
assert(pcall(serpent.line, a) == true, "`__tostring` method that returns an error is still serialized correctly.")
end
-- based on https://gist.github.com/mpeterv/8360307 -- based on https://gist.github.com/mpeterv/8360307
local function random_var(is_key, deep) local function random_var(is_key, deep)
local key = math.random(1000) local key = math.random(1000)