Skip to content

Commit

Permalink
Upgraded Mobdebug (0.624) to make DONE async and to add `__tostring…
Browse files Browse the repository at this point in the history
…` protection (closes #446).
  • Loading branch information
pkulchenko committed Jun 10, 2015
1 parent 2ee84a0 commit adcdb71
Showing 1 changed file with 21 additions and 20 deletions.
41 changes: 21 additions & 20 deletions lualibs/mobdebug/mobdebug.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ end)("os")

local mobdebug = {
_NAME = "mobdebug",
_VERSION = 0.62,
_VERSION = 0.624,
_COPYRIGHT = "Paul Kulchenko",
_DESCRIPTION = "Mobile Remote Debugger for the Lua programming language",
port = os and os.getenv and tonumber((os.getenv("MOBDEBUG_PORT"))) or 8172,
Expand All @@ -37,6 +37,7 @@ local setmetatable = setmetatable
local tonumber = tonumber
local unpack = table.unpack or unpack
local rawget = rawget
local gsub, sub, find = string.gsub, string.sub, string.find

-- if strict.lua is used, then need to avoid referencing some global
-- variables, as they can be undefined;
Expand All @@ -61,6 +62,7 @@ local corocreate = ngx and coroutine._create or coroutine.create
local cororesume = ngx and coroutine._resume or coroutine.resume
local coroyield = ngx and coroutine._yield or coroutine.yield
local corostatus = ngx and coroutine._status or coroutine.status
local corowrap = coroutine.wrap

if not setfenv then -- Lua 5.2+
-- based on http://lua-users.org/lists/lua-l/2010-06/msg00314.html
Expand Down Expand Up @@ -127,31 +129,33 @@ end
local function q(s) return s:gsub('([%(%)%.%%%+%-%*%?%[%^%$%]])','%%%1') end

local serpent = (function() ---- include Serpent module for serialization
local n, v = "serpent", 0.28 -- (C) 2012-15 Paul Kulchenko; MIT License
local n, v = "serpent", 0.284 -- (C) 2012-15 Paul Kulchenko; MIT License
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 badtype = {thread = true, userdata = true, cdata = true}
local getmetatable = debug and debug.getmetatable or getmetatable
local keyword, globals, G = {}, {}, (_G or _ENV)
for _,k in ipairs({'and', 'break', 'do', 'else', 'elseif', 'end', 'false',
'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat',
'return', 'then', 'true', 'until', 'while'}) do keyword[k] = true end
for k,v in pairs(G) do globals[v] = k end -- build func to name mapping
for _,g in ipairs({'coroutine', 'debug', 'io', 'math', 'string', 'table', 'os'}) do
for k,v in pairs(G[g] or {}) do globals[v] = g..'.'..k end end
for k,v in pairs(type(G[g]) == 'table' and G[g] or {}) do globals[v] = g..'.'..k end end

local function s(t, opts)
local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum
local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge
local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge)
local iname, comm = '_'..(name or ''), opts.comment and (tonumber(opts.comment) or math.huge)
local numformat = opts.numformat or "%.17g"
local seen, sref, syms, symn = {}, {'local '..iname..'={}'}, {}, 0
local function gensym(val) return '_'..(tostring(tostring(val)):gsub("[^%w]",""):gsub("(%d%w+)",
-- tostring(val) is needed because __tostring may return a non-string value
function(s) if not syms[s] then symn = symn+1; syms[s] = symn end return tostring(syms[s]) end)) end
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 numformat:format(s))
or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026
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
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']
Expand All @@ -175,7 +179,9 @@ local function s(t, opts)
if seen[t] then -- already seen this element
sref[#sref+1] = spath..space..'='..space..seen[t]
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
if mt.__serialize then t = mt.__serialize(t) else t = tostring(t) end
ttype = type(t) end -- new value falls through to be serialized
Expand Down Expand Up @@ -573,26 +579,26 @@ local function debug_hook(event, line)
-- Unfortunately, there is no reliable/quick way to figure out
-- what is the filename and what is the source code.
-- The following will work if the supplied filename uses Unix path.
if file:find("^@") then
file = file:gsub("^@", ""):gsub("\\", "/")
if find(file, "^@") then
file = gsub(gsub(file, "^@", ""), "\\", "/")
-- need this conversion to be applied to relative and absolute
-- file names as you may write "require 'Foo'" to
-- load "foo.lua" (on a case insensitive file system) and breakpoints
-- set on foo.lua will not work if not converted to the same case.
if iscasepreserving then file = string.lower(file) end
if file:find("%./") == 1 then file = file:sub(3)
else file = file:gsub("^"..q(basedir), "") end
if find(file, "%./") == 1 then file = sub(file, 3)
else file = gsub(file, "^"..q(basedir), "") end
-- some file systems allow newlines in file names; remove these.
file = file:gsub("\n", ' ')
file = gsub(file, "\n", ' ')
else
-- this is either a file name coming from loadstring("chunk", "file"),
-- or the actual source code that needs to be serialized (as it may
-- include newlines); assume it's a file name if it's all on one line.
if file:find("[\r\n]") then
if find(file, "[\r\n]") then
file = mobdebug.line(file)
else
if iscasepreserving then file = string.lower(file) end
file = file:gsub("\\", "/"):gsub(file:find("^%./") and "^%./" or "^"..q(basedir), "")
file = gsub(gsub(file, "\\", "/"), find(file, "^%./") and "^%./" or "^"..q(basedir), "")
end
end

Expand Down Expand Up @@ -916,7 +922,6 @@ local function debugger_loop(sev, svars, sfile, sline)
elseif command == "SUSPEND" then
-- do nothing; it already fulfilled its role
elseif command == "DONE" then
server:send("200 OK\n")
coroyield("done")
return -- done with all the debugging
elseif command == "STACK" then
Expand Down Expand Up @@ -945,7 +950,7 @@ local function debugger_loop(sev, svars, sfile, sline)
if stream and mode and stream == "stdout" then
-- assign "print" in the global environment
local default = mode == 'd'
genv.print = default and iobase.print or coroutine.wrap(function()
genv.print = default and iobase.print or corowrap(function()
-- wrapping into coroutine.wrap protects this function from
-- being stepped through in the debugger.
-- don't use vararg (...) as it adds a reference for its values,
Expand Down Expand Up @@ -1217,11 +1222,7 @@ local function handle(params, client, options)
end
elseif command == "done" then
client:send(string.upper(command) .. "\n")
if client:receive() ~= "200 OK" then
print("Unknown error")
os.exit(1, true)
return nil, nil, "Debugger error: unexpected response after DONE"
end
-- no response is expected
elseif command == "setb" or command == "asetb" then
_, _, _, file, line = string.find(params, "^([a-z]+)%s+(.-)%s+(%d+)%s*$")
if file and line then
Expand Down

0 comments on commit adcdb71

Please sign in to comment.