rpc.lua (13529B)
1 local serpent 2 do 3 --[[ 4 Serpent source is released under the MIT License 5 6 Copyright (c) 2012-2018 Paul Kulchenko (paul@kulchenko.com) 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy 9 of this software and associated documentation files (the "Software"), to deal 10 in the Software without restriction, including without limitation the rights 11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 copies of the Software, and to permit persons to whom the Software is 13 furnished to do so, subject to the following conditions: 14 15 The above copyright notice and this permission notice shall be included in 16 all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 THE SOFTWARE. 25 ]] 26 local n, v = "serpent", "0.303" -- (C) 2012-18 Paul Kulchenko; MIT License 27 local c, d = "Paul Kulchenko", "Lua serializer and pretty printer" 28 local snum = { 29 [tostring(1 / 0)] = '1/0 --[[math.huge]]', 30 [tostring(-1 / 0)] = '-1/0 --[[-math.huge]]', 31 [tostring(0 / 0)] = '0/0' 32 } 33 local badtype = { thread = true, userdata = true, cdata = true } 34 local getmetatable = debug and debug.getmetatable or getmetatable 35 local pairs = function(t) return next, t end -- avoid using __pairs in Lua 5.2+ 36 local keyword, globals, G = {}, {}, (_G or _ENV) 37 for _, k in ipairs({ 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 38 'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', 39 'return', 'then', 'true', 'until', 'while' }) do keyword[k] = true end 40 for k, v in pairs(G) do globals[v] = k end -- build func to name mapping 41 for _, g in ipairs({ 'coroutine', 'debug', 'io', 'math', 'string', 'table', 'os' }) do 42 for k, v in pairs(type(G[g]) == 'table' and G[g] or {}) do globals[v] = g .. '.' .. k end 43 end 44 45 local function s(t, opts) 46 local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum 47 local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge 48 local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge) 49 local maxlen, metatostring = tonumber(opts.maxlength), opts.metatostring 50 local iname, comm = '_' .. (name or ''), opts.comment and (tonumber(opts.comment) or math.huge) 51 local numformat = opts.numformat or "%.17g" 52 local seen, sref, syms, symn = {}, { 'local ' .. iname .. '={}' }, {}, 0 53 local function gensym(val) 54 return '_' .. (tostring(tostring(val)):gsub("[^%w]", ""):gsub("(%d%w+)", 55 -- tostring(val) is needed because __tostring may return a non-string value 56 function(s) 57 if not syms[s] then 58 symn = symn + 1; syms[s] = symn 59 end 60 return tostring(syms[s]) 61 end)) 62 end 63 local function safestr(s) 64 return type(s) == "number" and (huge and snum[tostring(s)] or numformat:format(s)) 65 or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026 66 or ("%q"):format(s):gsub("\010", "n"):gsub("\026", "\\026") 67 end 68 -- handle radix changes in some locales 69 if opts.fixradix and (".1f"):format(1.2) ~= "1.2" then 70 local origsafestr = safestr 71 safestr = function(s) 72 return type(s) == "number" 73 and (nohuge and snum[tostring(s)] or numformat:format(s):gsub(",", ".")) or origsafestr(s) 74 end 75 end 76 local function comment(s, l) 77 return comm and (l or 0) < comm and ' --[[' .. select(2, pcall(tostring, s)) .. ']]' or 78 '' 79 end 80 local function globerr(s, l) 81 return globals[s] and globals[s] .. comment(s, l) or not fatal 82 and safestr(select(2, pcall(tostring, s))) or error("Can't serialize " .. tostring(s)) 83 end 84 local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r'] 85 local n = name == nil and '' or name 86 local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n] 87 local safe = plain and n or '[' .. safestr(n) .. ']' 88 return (path or '') .. (plain and path and '.' or '') .. safe, safe 89 end 90 local alphanumsort = type(opts.sortkeys) == 'function' and opts.sortkeys or 91 function(k, o, n) -- k=keys, o=originaltable, n=padding 92 local maxn, to = tonumber(n) or 12, { number = 'a', string = 'b' } 93 local function padnum(d) return ("%0" .. tostring(maxn) .. "d"):format(tonumber(d)) end 94 table.sort(k, function(a, b) 95 -- sort numeric keys first: k[key] is not nil for numerical keys 96 return (k[a] ~= nil and 0 or to[type(a)] or 'z') .. (tostring(a):gsub("%d+", padnum)) 97 < (k[b] ~= nil and 0 or to[type(b)] or 'z') .. (tostring(b):gsub("%d+", padnum)) 98 end) 99 end 100 local function val2str(t, name, indent, insref, path, plainindex, level) 101 local ttype, level, mt = type(t), (level or 0), getmetatable(t) 102 local spath, sname = safename(path, name) 103 local tag = plainindex and 104 ((type(name) == "number") and '' or name .. space .. '=' .. space) or 105 (name ~= nil and sname .. space .. '=' .. space or '') 106 if seen[t] then -- already seen this element 107 sref[#sref + 1] = spath .. space .. '=' .. space .. seen[t] 108 return tag .. 'nil' .. comment('ref', level) 109 end 110 -- protect from those cases where __tostring may fail 111 if type(mt) == 'table' and metatostring ~= false then 112 local to, tr = pcall(function() return mt.__tostring(t) end) 113 local so, sr = pcall(function() return mt.__serialize(t) end) 114 if (to or so) then -- knows how to serialize itself 115 seen[t] = insref or spath 116 t = so and sr or tr 117 ttype = type(t) 118 end -- new value falls through to be serialized 119 end 120 if ttype == "table" then 121 if level >= maxl then return tag .. '{}' .. comment('maxlvl', level) end 122 seen[t] = insref or spath 123 if next(t) == nil then return tag .. '{}' .. comment(t, level) end -- table empty 124 if maxlen and maxlen < 0 then return tag .. '{}' .. comment('maxlen', level) end 125 local maxn, o, out = math.min(#t, maxnum or #t), {}, {} 126 for key = 1, maxn do o[key] = key end 127 if not maxnum or #o < maxnum then 128 local n = #o -- n = n + 1; o[n] is much faster than o[#o+1] on large tables 129 for key in pairs(t) do 130 if o[key] ~= key then 131 n = n + 1; o[n] = key 132 end 133 end 134 end 135 if maxnum and #o > maxnum then o[maxnum + 1] = nil end 136 if opts.sortkeys and #o > maxn then alphanumsort(o, t, opts.sortkeys) end 137 local sparse = sparse and #o > maxn -- disable sparsness if only numeric keys (shorter output) 138 for n, key in ipairs(o) do 139 local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse 140 if opts.valignore and opts.valignore[value] -- skip ignored values; do nothing 141 or opts.keyallow and not opts.keyallow[key] 142 or opts.keyignore and opts.keyignore[key] 143 or opts.valtypeignore and opts.valtypeignore[type(value)] -- skipping ignored value types 144 or sparse and value == nil then -- skipping nils; do nothing 145 elseif ktype == 'table' or ktype == 'function' or badtype[ktype] then 146 if not seen[key] and not globals[key] then 147 sref[#sref + 1] = 'placeholder' 148 local sname = safename(iname, gensym(key)) -- iname is table for local variables 149 sref[#sref] = val2str(key, sname, indent, sname, iname, true) 150 end 151 sref[#sref + 1] = 'placeholder' 152 local path = seen[t] .. '[' .. tostring(seen[key] or globals[key] or gensym(key)) .. ']' 153 sref[#sref] = path .. 154 space .. '=' .. space .. tostring(seen[value] or val2str(value, nil, indent, path)) 155 else 156 out[#out + 1] = val2str(value, key, indent, nil, seen[t], plainindex, level + 1) 157 if maxlen then 158 maxlen = maxlen - #out[#out] 159 if maxlen < 0 then break end 160 end 161 end 162 end 163 local prefix = string.rep(indent or '', level) 164 local head = indent and '{\n' .. prefix .. indent or '{' 165 local body = table.concat(out, ',' .. (indent and '\n' .. prefix .. indent or space)) 166 local tail = indent and "\n" .. prefix .. '}' or '}' 167 return (custom and custom(tag, head, body, tail, level) or tag .. head .. body .. tail) .. comment(t, level) 168 elseif badtype[ttype] then 169 seen[t] = insref or spath 170 return tag .. globerr(t, level) 171 elseif ttype == 'function' then 172 seen[t] = insref or spath 173 if opts.nocode then return tag .. "function() --[[..skipped..]] end" .. comment(t, level) end 174 local ok, res = pcall(string.dump, t) 175 local func = ok and "((loadstring or load)(" .. safestr(res) .. ",'@serialized'))" .. comment(t, level) 176 return tag .. (func or globerr(t, level)) 177 else 178 return tag .. safestr(t) 179 end -- handle all other types 180 end 181 local sepr = indent and "\n" or ";" .. space 182 local body = val2str(t, name, indent) -- this call also populates sref 183 local tail = #sref > 1 and table.concat(sref, sepr) .. sepr or '' 184 local warn = opts.comment and #sref > 1 and space .. "--[[incomplete output with shared/self-references skipped]]" or 185 '' 186 return not name and body .. warn or "do local " .. body .. sepr .. tail .. "return " .. name .. sepr .. "end" 187 end 188 189 local function deserialize(data, opts) 190 local env = (opts and opts.safe == false) and G 191 or setmetatable({}, { 192 __index = function(t, k) return t end, 193 __call = function(t, ...) error("cannot call functions") end 194 }) 195 local f, res = (loadstring or load)('return ' .. data, nil, nil, env) 196 if not f then f, res = (loadstring or load)(data, nil, nil, env) end 197 if not f then return f, res end 198 if setfenv then setfenv(f, env) end 199 return pcall(f) 200 end 201 202 local function merge(a, b) 203 if b then for k, v in pairs(b) do a[k] = v end end; return a; 204 end 205 206 serpent = { 207 _NAME = n, 208 _COPYRIGHT = c, 209 _DESCRIPTION = d, 210 _VERSION = v, 211 serialize = s, 212 load = deserialize, 213 dump = function(a, opts) return s(a, merge({ name = '_', compact = true, sparse = true }, opts)) end, 214 line = function(a, opts) return s(a, merge({ sortkeys = true, comment = true }, opts)) end, 215 block = function(a, opts) return s(a, merge({ indent = ' ', sortkeys = true, comment = true }, opts)) end 216 } 217 end 218 219 local rpc = {} 220 221 function rpc.respond(command, data) 222 io.write(serpent.dump({ 223 ty = type(data), 224 data = data, 225 command = command 226 }, { metatostring = false })) 227 io.write('\n') 228 io.flush() 229 end 230 231 function rpc.globals() 232 rpc.respond('globals', _G) 233 end 234 235 local load_fn = _VERSION == 'Lua 5.1' and loadstring or load 236 237 function rpc.exec(code) 238 code = load_fn('return ' .. code)() 239 240 local fn = load_fn(code, 'repl') 241 242 if not fn then 243 fn = assert(load_fn('return (' .. code .. ')', 'repl')) 244 end 245 246 local function cleanup() 247 if debug and debug.sethook then 248 debug.sethook() 249 end 250 251 if io and io.open and rpc.cancel_file then 252 local f = io.open(rpc.cancel_file, 'w') 253 254 if f then 255 f:close() 256 end 257 end 258 end 259 260 if debug and debug.sethook and io and io.open and rpc.cancel_file then 261 local function cancel() 262 local f = assert(io.open(rpc.cancel_file, 'r')) 263 264 local data = f:read('*a') 265 266 f:close() 267 268 if data:find('stop') then 269 cleanup() 270 error('cancelled') 271 end 272 end 273 274 -- we can't do every line like in MluaExecutor due to the FS call 275 debug.sethook(cancel, "", 500000) 276 end 277 278 local success, res = pcall(fn) 279 280 cleanup() 281 282 if success then 283 rpc.respond('exec', res) 284 else 285 rpc.respond('error', res) 286 end 287 end 288 289 function rpc.prepare(file) 290 -- LuaJIT can't stop due to JIT 291 if not io or jit then 292 rpc.respond('cancel', false) 293 return 294 end 295 296 rpc.cancel_file = file 297 298 rpc.respond('cancel', true) 299 end 300 301 for line in io.stdin:lines() do 302 local cmd, arg = line:match('(.-):(.*)') 303 if cmd == nil then 304 cmd = line 305 end 306 307 local success, err = pcall(rpc[cmd], arg) 308 309 if not success then 310 rpc.respond('error', err) 311 end 312 end