1--- 2-- DataDumper.lua 3-- Copyright (c) 2007 Olivetti-Engineering SA 4-- 5-- Permission is hereby granted, free of charge, to any person 6-- obtaining a copy of this software and associated documentation 7-- files (the "Software"), to deal in the Software without 8-- restriction, including without limitation the rights to use, 9-- copy, modify, merge, publish, distribute, sublicense, and/or sell 10-- copies of the Software, and to permit persons to whom the 11-- Software is furnished to do so, subject to the following 12-- conditions: 13-- 14-- The above copyright notice and this permission notice shall be 15-- included in all copies or substantial portions of the Software. 16-- 17-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19-- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21-- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22-- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24-- OTHER DEALINGS IN THE SOFTWARE. 25 26local dumplua_closure = [[ 27local closures = {} 28local function closure(t) 29 closures[#closures+1] = t 30 t[1] = assert(loadstring(t[1])) 31 return t[1] 32end 33 34for _,t in pairs(closures) do 35 for i = 2,#t do 36 debug.setupvalue(t[1], i-1, t[i]) 37 end 38end 39]] 40 41local lua_reserved_keywords = { 42 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 43 'function', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', 44 'return', 'then', 'true', 'until', 'while' } 45 46local function keys(t) 47 local res = {} 48 local oktypes = { stringstring = true, numbernumber = true } 49 local function cmpfct(a,b) 50 if oktypes[type(a)..type(b)] then 51 return a < b 52 else 53 return type(a) < type(b) 54 end 55 end 56 for k in pairs(t) do 57 res[#res+1] = k 58 end 59 table.sort(res, cmpfct) 60 return res 61end 62 63local c_functions = {} 64for _,lib in pairs{'_G', 'string', 'table', 'math', 65 'io', 'os', 'coroutine', 'package', 'debug'} do 66 local t = _G[lib] or {} 67 lib = lib .. "." 68 if lib == "_G." then lib = "" end 69 for k,v in pairs(t) do 70 if type(v) == 'function' and not pcall(string.dump, v) then 71 c_functions[v] = lib..k 72 end 73 end 74end 75 76function DataDumper(value, varname, fastmode, ident) 77 local defined, dumplua = {} 78 -- Local variables for speed optimization 79 local string_format, type, string_dump, string_rep = 80 string.format, type, string.dump, string.rep 81 local tostring, pairs, table_concat = 82 tostring, pairs, table.concat 83 local keycache, strvalcache, out, closure_cnt = {}, {}, {}, 0 84 setmetatable(strvalcache, {__index = function(t,value) 85 local res = string_format('%q', value) 86 t[value] = res 87 return res 88 end}) 89 local fcts = { 90 string = function(value) return strvalcache[value] end, 91 number = function(value) return value end, 92 boolean = function(value) return tostring(value) end, 93 ['nil'] = function(value) return 'nil' end, 94 ['function'] = function(value) 95 return string_format("loadstring(%q)", string_dump(value)) 96 end, 97 userdata = function() error("Cannot dump userdata") end, 98 thread = function() error("Cannot dump threads") end, 99 } 100 local function test_defined(value, path) 101 if defined[value] then 102 if path:match("^getmetatable.*%)$") then 103 out[#out+1] = string_format("s%s, %s)\n", path:sub(2,-2), defined[value]) 104 else 105 out[#out+1] = path .. " = " .. defined[value] .. "\n" 106 end 107 return true 108 end 109 defined[value] = path 110 end 111 local function make_key(t, key) 112 local s 113 if type(key) == 'string' and key:match('^[_%a][_%w]*$') then 114 s = key .. "=" 115 else 116 s = "[" .. dumplua(key, 0) .. "]=" 117 end 118 t[key] = s 119 return s 120 end 121 for _,k in ipairs(lua_reserved_keywords) do 122 keycache[k] = '["'..k..'"] = ' 123 end 124 if fastmode then 125 fcts.table = function (value) 126 -- Table value 127 local numidx = 1 128 out[#out+1] = "{" 129 for key,val in pairs(value) do 130 if key == numidx then 131 numidx = numidx + 1 132 else 133 out[#out+1] = keycache[key] 134 end 135 local str = dumplua(val) 136 out[#out+1] = str.."," 137 end 138 if string.sub(out[#out], -1) == "," then 139 out[#out] = string.sub(out[#out], 1, -2); 140 end 141 out[#out+1] = "}" 142 return "" 143 end 144 else 145 fcts.table = function (value, ident, path) 146 if test_defined(value, path) then return "nil" end 147 -- Table value 148 local sep, str, numidx, totallen = " ", {}, 1, 0 149 local meta, metastr = (debug or getfenv()).getmetatable(value) 150 if meta then 151 ident = ident + 1 152 metastr = dumplua(meta, ident, "getmetatable("..path..")") 153 totallen = totallen + #metastr + 16 154 end 155 for _,key in pairs(keys(value)) do 156 local val = value[key] 157 local s = "" 158 local subpath = path 159 if key == numidx then 160 subpath = subpath .. "[" .. numidx .. "]" 161 numidx = numidx + 1 162 else 163 s = keycache[key] 164 if not s:match "^%[" then subpath = subpath .. "." end 165 subpath = subpath .. s:gsub("%s*=%s*$","") 166 end 167 s = s .. dumplua(val, ident+1, subpath) 168 str[#str+1] = s 169 totallen = totallen + #s + 2 170 end 171 if totallen > 80 then 172 sep = "\n" .. string_rep(" ", ident+1) 173 end 174 str = "{"..sep..table_concat(str, ","..sep).." "..sep:sub(1,-3).."}" 175 if meta then 176 sep = sep:sub(1,-3) 177 return "setmetatable("..sep..str..","..sep..metastr..sep:sub(1,-3)..")" 178 end 179 return str 180 end 181 fcts['function'] = function (value, ident, path) 182 if test_defined(value, path) then return "nil" end 183 if c_functions[value] then 184 return c_functions[value] 185 elseif debug == nil or debug.getupvalue(value, 1) == nil then 186 return string_format("loadstring(%q)", string_dump(value)) 187 end 188 closure_cnt = closure_cnt + 1 189 local res = {string.dump(value)} 190 for i = 1,math.huge do 191 local name, v = debug.getupvalue(value,i) 192 if name == nil then break end 193 res[i+1] = v 194 end 195 return "closure " .. dumplua(res, ident, "closures["..closure_cnt.."]") 196 end 197 end 198 function dumplua(value, ident, path) 199 return fcts[type(value)](value, ident, path) 200 end 201 if varname == nil then 202 varname = "return " 203 elseif varname:match("^[%a_][%w_]*$") then 204 varname = varname .. " = " 205 end 206 if fastmode then 207 setmetatable(keycache, {__index = make_key }) 208 out[1] = varname 209 table.insert(out,dumplua(value, 0)) 210 return table.concat(out) 211 else 212 setmetatable(keycache, {__index = make_key }) 213 local items = {} 214 for i=1,10 do items[i] = '' end 215 items[3] = dumplua(value, ident or 0, "t") 216 if closure_cnt > 0 then 217 items[1], items[6] = dumplua_closure:match("(.*\n)\n(.*)") 218 out[#out+1] = "" 219 end 220 if #out > 0 then 221 items[2], items[4] = "local t = ", "\n" 222 items[5] = table.concat(out) 223 items[7] = varname .. "t" 224 else 225 items[2] = varname 226 end 227 return table.concat(items) 228 end 229end 230