PageRenderTime 44ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/AddOns/Blizzard_DebugTools/Dump.lua

http://github.com/tekkub/wow-ui-source
Lua | 408 lines | 336 code | 46 blank | 26 comment | 70 complexity | f7dad74f694373153e09ab1e9766dda1 MD5 | raw file
  1. ------------------------------------------------------------------------------
  2. -- Dump.lua
  3. --
  4. -- Contributed by Iriel, Esamynn and Kirov from DevTools v1.11
  5. -- /dump Implementation
  6. --
  7. -- Globals: DevTools, SLASH_DEVTOOLSDUMP1, DevTools_Dump, DevTools_RunDump
  8. -- Globals: DEVTOOLS_MAX_ENTRY_CUTOFF, DEVTOOLS_LONG_STRING_CUTOFF
  9. -- Globals: DEVTOOLS_DEPTH_CUTOFF, DEVTOOLS_INDENT
  10. -- Globals: DEVTOOLS_USE_TABLE_CACHE, DEVTOOLS_USE_FUNCTION_CACHE
  11. -- Globals: DEVTOOLS_USE_USERDATA_CACHE
  12. ---------------------------------------------------------------------------
  13. local DT = {};
  14. DEVTOOLS_MAX_ENTRY_CUTOFF = 30; -- Maximum table entries shown
  15. DEVTOOLS_LONG_STRING_CUTOFF = 200; -- Maximum string size shown
  16. DEVTOOLS_DEPTH_CUTOFF = 10; -- Maximum table depth
  17. DEVTOOLS_USE_TABLE_CACHE = true; -- Look up table names
  18. DEVTOOLS_USE_FUNCTION_CACHE = true;-- Look up function names
  19. DEVTOOLS_USE_USERDATA_CACHE = true;-- Look up userdata names
  20. DEVTOOLS_INDENT=' '; -- Indentation string
  21. local DEVTOOLS_TYPE_COLOR="|cff88ff88";
  22. local DEVTOOLS_TABLEREF_COLOR="|cffffcc00";
  23. local DEVTOOLS_CUTOFF_COLOR="|cffff0000";
  24. local DEVTOOLS_TABLEKEY_COLOR="|cff88ccff";
  25. local FORMATS = {};
  26. -- prefix type suffix
  27. FORMATS["opaqueTypeVal"] = "%s" .. DEVTOOLS_TYPE_COLOR .. "<%s>|r%s";
  28. -- prefix type name suffix
  29. FORMATS["opaqueTypeValName"] = "%s" .. DEVTOOLS_TYPE_COLOR .. "<%s %s>|r%s";
  30. -- type
  31. FORMATS["opaqueTypeKey"] = "<%s>";
  32. -- type name
  33. FORMATS["opaqueTypeKeyName"] = "<%s %s>";
  34. -- value
  35. FORMATS["bracketTableKey"] = "[%s]";
  36. -- prefix value
  37. FORMATS["tableKeyAssignPrefix"] = DEVTOOLS_TABLEKEY_COLOR .. "%s%s|r=";
  38. -- prefix cutoff
  39. FORMATS["tableEntriesSkipped"] = "%s" .. DEVTOOLS_CUTOFF_COLOR .. "<skipped %s>|r";
  40. -- prefix suffix
  41. FORMATS["tableTooDeep"] = "%s" .. DEVTOOLS_CUTOFF_COLOR .. "<table (too deep)>|r%s";
  42. -- prefix value suffix
  43. FORMATS["simpleValue"] = "%s%s%s";
  44. -- prefix tablename suffix
  45. FORMATS["tableReference"] = "%s" .. DEVTOOLS_TABLEREF_COLOR .. "%s|r%s";
  46. -- Grab a copy various oft-used functions
  47. local rawget = rawget;
  48. local type = type;
  49. local string_len = string.len;
  50. local string_sub = string.sub;
  51. local string_gsub = string.gsub;
  52. local string_format = string.format;
  53. local string_match = string.match;
  54. local function WriteMessage(msg)
  55. DEFAULT_CHAT_FRAME:AddMessage(msg);
  56. end
  57. local function prepSimple(val, context)
  58. local valType = type(val);
  59. if (valType == "nil") then
  60. return "nil";
  61. elseif (valType == "number") then
  62. return val;
  63. elseif (valType == "boolean") then
  64. if (val) then
  65. return "true";
  66. else
  67. return "false";
  68. end
  69. elseif (valType == "string") then
  70. local l = string_len(val);
  71. if ((l > DEVTOOLS_LONG_STRING_CUTOFF) and
  72. (DEVTOOLS_LONG_STRING_CUTOFF > 0)) then
  73. local more = l - DEVTOOLS_LONG_STRING_CUTOFF;
  74. val = string_sub(val, 1, DEVTOOLS_LONG_STRING_CUTOFF);
  75. return string_gsub(string_format("%q...+%s",val,more),"[|]", "||");
  76. else
  77. return string_gsub(string_format("%q",val),"[|]", "||");
  78. end
  79. elseif (valType == "function") then
  80. local fName = context:GetFunctionName(val);
  81. if (fName) then
  82. return string_format(FORMATS.opaqueTypeKeyName, valType, fName);
  83. else
  84. return string_format(FORMATS.opaqueTypeKey, valType);
  85. end
  86. return string_format(FORMATS.opaqueTypeKey, valType);
  87. elseif (valType == "userdata") then
  88. local uName = context:GetUserdataName(val);
  89. if (uName) then
  90. return string_format(FORMATS.opaqueTypeKeyName, valType, uName);
  91. else
  92. return string_format(FORMATS.opaqueTypeKey, valType);
  93. end
  94. elseif (valType == 'table') then
  95. local tName = context:GetTableName(val);
  96. if (tName) then
  97. return string_format(FORMATS.opaqueTypeKeyName, valType, tName);
  98. else
  99. return string_format(FORMATS.opaqueTypeKey, valType);
  100. end
  101. end
  102. error("Bad type '" .. valType .. "' to prepSimple");
  103. end
  104. local function prepSimpleKey(val, context)
  105. local valType = type(val);
  106. if (valType == "string") then
  107. local l = string_len(val);
  108. if ((l <= DEVTOOLS_LONG_STRING_CUTOFF) or
  109. (DEVTOOLS_LONG_STRING_CUTOFF <= 0)) then
  110. if (string_match(val, "^[a-zA-Z_][a-zA-Z0-9_]*$")) then
  111. return val;
  112. end
  113. end
  114. end
  115. return string_format(FORMATS.bracketTableKey, prepSimple(val, context));
  116. end
  117. local function DevTools_InitFunctionCache(context)
  118. local ret = {};
  119. for _,k in ipairs(DT.functionSymbols) do
  120. local v = getglobal(k);
  121. if (type(v) == 'function') then
  122. ret[v] = '[' .. k .. ']';
  123. end
  124. end
  125. for k,v in pairs(getfenv(0)) do
  126. if (type(v) == 'function') then
  127. if (not ret[v]) then
  128. ret[v] = '[' .. k .. ']';
  129. end
  130. end
  131. end
  132. return ret;
  133. end
  134. local function DevTools_InitUserdataCache(context)
  135. local ret = {};
  136. for _,k in ipairs(DT.userdataSymbols) do
  137. local v = getglobal(k);
  138. if (type(v) == 'table') then
  139. local u = rawget(v,0);
  140. if (type(u) == 'userdata') then
  141. ret[u] = k .. '[0]';
  142. end
  143. end
  144. end
  145. for k,v in pairs(getfenv(0)) do
  146. if (type(v) == 'table') then
  147. local u = rawget(v, 0);
  148. if (type(u) == 'userdata') then
  149. if (not ret[u]) then
  150. ret[u] = k .. '[0]';
  151. end
  152. end
  153. end
  154. end
  155. return ret;
  156. end
  157. local function DevTools_Cache_Nil(self, value, newName)
  158. return nil;
  159. end
  160. local function DevTools_Cache_Function(self, value, newName)
  161. if (not self.fCache) then
  162. self.fCache = DevTools_InitFunctionCache(self);
  163. end
  164. local name = self.fCache[value];
  165. if ((not name) and newName) then
  166. self.fCache[value] = newName;
  167. end
  168. return name;
  169. end
  170. local function DevTools_Cache_Userdata(self, value, newName)
  171. if (not self.uCache) then
  172. self.uCache = DevTools_InitUserdataCache(self);
  173. end
  174. local name = self.uCache[value];
  175. if ((not name) and newName) then
  176. self.uCache[value] = newName;
  177. end
  178. return name;
  179. end
  180. local function DevTools_Cache_Table(self, value, newName)
  181. if (not self.tCache) then
  182. self.tCache = {};
  183. end
  184. local name = self.tCache[value];
  185. if ((not name) and newName) then
  186. self.tCache[value] = newName;
  187. end
  188. return name;
  189. end
  190. local function DevTools_Write(self, msg)
  191. DEFAULT_CHAT_FRAME:AddMessage(msg);
  192. end
  193. local DevTools_DumpValue;
  194. local function DevTools_DumpTableContents(val, prefix, firstPrefix, context)
  195. local showCount = 0;
  196. local oldDepth = context.depth;
  197. local oldKey = context.key;
  198. -- Use this to set the cache name
  199. context:GetTableName(val, oldKey or 'value');
  200. local iter = pairs(val);
  201. local nextK, nextV = iter(val, nil);
  202. while (nextK) do
  203. local k,v = nextK, nextV;
  204. nextK, nextV = iter(val, k);
  205. showCount = showCount + 1;
  206. if ((showCount <= DEVTOOLS_MAX_ENTRY_CUTOFF) or
  207. (DEVTOOLS_MAX_ENTRY_CUTOFF <= 0)) then
  208. local prepKey = prepSimpleKey(k, context);
  209. if (oldKey == nil) then
  210. context.key = prepKey;
  211. elseif (string_sub(prepKey, 1, 1) == "[") then
  212. context.key = oldKey .. prepKey
  213. else
  214. context.key = oldKey .. "." .. prepKey
  215. end
  216. context.depth = oldDepth + 1;
  217. local rp = string_format(FORMATS.tableKeyAssignPrefix, firstPrefix,
  218. prepKey);
  219. firstPrefix = prefix;
  220. DevTools_DumpValue(v, prefix, rp,
  221. (nextK and ",") or '',
  222. context);
  223. end
  224. end
  225. local cutoff = showCount - DEVTOOLS_MAX_ENTRY_CUTOFF;
  226. if ((cutoff > 0) and (DEVTOOLS_MAX_ENTRY_CUTOFF > 0)) then
  227. context:Write(string_format(FORMATS.tableEntriesSkipped,firstPrefix,
  228. cutoff));
  229. end
  230. context.key = oldKey;
  231. context.depth = oldDepth;
  232. return (showCount > 0)
  233. end
  234. -- Return the specified value
  235. function DevTools_DumpValue(val, prefix, firstPrefix, suffix, context)
  236. local valType = type(val);
  237. if (valType == "userdata") then
  238. local uName = context:GetUserdataName(val, 'value');
  239. if (uName) then
  240. context:Write(string_format(FORMATS.opaqueTypeValName,
  241. firstPrefix, valType, uName, suffix));
  242. else
  243. context:Write(string_format(FORMATS.opaqueTypeVal,
  244. firstPrefix, valType, suffix));
  245. end
  246. return;
  247. elseif (valType == "function") then
  248. local fName = context:GetFunctionName(val, 'value');
  249. if (fName) then
  250. context:Write(string_format(FORMATS.opaqueTypeValName,
  251. firstPrefix, valType, fName, suffix));
  252. else
  253. context:Write(string_format(FORMATS.opaqueTypeVal,
  254. firstPrefix, valType, suffix));
  255. end
  256. return;
  257. elseif (valType ~= "table") then
  258. context:Write(string_format(FORMATS.simpleValue,
  259. firstPrefix,prepSimple(val, context),
  260. suffix));
  261. return;
  262. end
  263. local cacheName = context:GetTableName(val);
  264. if (cacheName) then
  265. context:Write(string_format(FORMATS.tableReference,
  266. firstPrefix, cacheName, suffix));
  267. return;
  268. end
  269. if ((context.depth >= DEVTOOLS_DEPTH_CUTOFF) and
  270. (DEVTOOLS_DEPTH_CUTOFF > 0)) then
  271. context:Write(string_format(FORMATS.tableTooDeep,
  272. firstPrefix, suffix));
  273. return;
  274. end
  275. firstPrefix = firstPrefix .. "{";
  276. local oldPrefix = prefix;
  277. prefix = prefix .. DEVTOOLS_INDENT;
  278. context:Write(firstPrefix);
  279. firstPrefix = prefix;
  280. local anyContents = DevTools_DumpTableContents(val, prefix, firstPrefix,
  281. context);
  282. context:Write(oldPrefix .. "}" .. suffix);
  283. end
  284. local function Pick_Cache_Function(func, setting)
  285. if (setting) then
  286. return func;
  287. else
  288. return DevTools_Cache_Nil;
  289. end
  290. end
  291. function DevTools_RunDump(value, context)
  292. local prefix = "";
  293. local firstPrefix = prefix;
  294. local valType = type(value);
  295. if (type(value) == 'table') then
  296. local any =
  297. DevTools_DumpTableContents(value, prefix, firstPrefix, context);
  298. if (context.Result) then
  299. return context:Result();
  300. end
  301. if (not any) then
  302. context:Write("empty result");
  303. end
  304. return;
  305. end
  306. DevTools_DumpValue(value, '', '', '', context);
  307. if (context.Result) then
  308. return context:Result();
  309. end
  310. end
  311. -- Dump the specified list of value
  312. function DevTools_Dump(value, startKey)
  313. local context = {
  314. depth = 0,
  315. key = startKey,
  316. };
  317. context.GetTableName = Pick_Cache_Function(DevTools_Cache_Table,
  318. DEVTOOLS_USE_TABLE_CACHE);
  319. context.GetFunctionName = Pick_Cache_Function(DevTools_Cache_Function,
  320. DEVTOOLS_USE_FUNCTION_CACHE);
  321. context.GetUserdataName = Pick_Cache_Function(DevTools_Cache_Userdata,
  322. DEVTOOLS_USE_USERDATA_CACHE);
  323. context.Write = DevTools_Write;
  324. DevTools_RunDump(value, context);
  325. end
  326. function DevTools_DumpCommand(msg, editBox)
  327. forceinsecure();
  328. if (string_match(msg,"^[A-Za-z_][A-Za-z0-9_]*$")) then
  329. WriteMessage("Dump: " .. msg);
  330. local val = _G[msg];
  331. local tmp = {};
  332. if (val == nil) then
  333. local key = string_format(FORMATS.tableKeyAssignPrefix,
  334. '', prepSimpleKey(msg, {}));
  335. WriteMessage(key .. "nil,");
  336. else
  337. tmp[msg] = val;
  338. end
  339. DevTools_Dump(tmp);
  340. return;
  341. end
  342. WriteMessage("Dump: value=" .. msg);
  343. local func,err = loadstring("return " .. msg);
  344. if (not func) then
  345. WriteMessage("Dump: ERROR: " .. err);
  346. else
  347. DevTools_Dump({ func() }, "value");
  348. end
  349. end
  350. DT.functionSymbols = {};
  351. DT.userdataSymbols = {};
  352. local funcSyms = DT.functionSymbols;
  353. local userSyms = DT.userdataSymbols;
  354. for k,v in pairs(getfenv(0)) do
  355. if (type(v) == 'function') then
  356. table.insert(funcSyms, k);
  357. elseif (type(v) == 'table') then
  358. if (type(rawget(v,0)) == 'userdata') then
  359. table.insert(userSyms, k);
  360. end
  361. end
  362. end