19 local last = list.last + 1 |
19 local last = list.last + 1 |
20 list.last = last |
20 list.last = last |
21 list[last] = value |
21 list[last] = value |
22 end |
22 end |
23 |
23 |
24 function List.pop (list) |
24 function List.pop (list) |
25 local first = list.first |
25 local first = list.first |
26 if first > list.last then error("list is empty") end |
26 if first > list.last then error("list is empty") end |
27 local value = list[first] |
27 local value = list[first] |
28 list[first] = nil |
28 list[first] = nil |
29 list.first = first + 1 |
29 list.first = first + 1 |
30 return value |
30 return value |
31 end |
31 end |
32 |
32 |
33 function List.isempty (list) |
33 function List.isempty (list) |
34 return list.first > list.last |
34 return list.first > list.last |
35 end |
35 end |
36 |
36 |
37 -- Counts all references for a given object |
37 local traverse, edge; |
38 function countreferences(value) |
38 local types = {}; |
39 local count = -1 |
39 local get_metatable, get_environment, get_registry, get_locals, upvalues |
40 local f = function(from, to, how, v) |
40 |
41 if to == value then |
41 |
42 count = count + 1 |
42 -- select implementation of get_metatable depending on available API |
43 end |
43 if type( debug ) == "table" and |
44 end |
44 type( debug.getmetatable ) == "function" then |
45 traverse({edge=f}, {count, f}) |
45 |
46 return count |
46 local get_mt = debug.getmetatable |
|
47 function get_metatable( val, enabled ) |
|
48 if enabled then return get_mt( val ) end |
|
49 end |
|
50 |
|
51 elseif type( getmetatable ) == "function" then |
|
52 |
|
53 function get_metatable( val, enabled ) |
|
54 if enabled then return getmetatable( val ) end |
|
55 end |
|
56 |
|
57 else |
|
58 |
|
59 function get_metatable() end |
|
60 |
|
61 end |
|
62 |
|
63 |
|
64 -- select implementation of get_environment depending on available API |
|
65 if type( debug ) == "table" and |
|
66 type( debug.getfenv ) == "function" then |
|
67 |
|
68 local get_fe = debug.getfenv |
|
69 function get_environment( val, n, enabled ) |
|
70 if enabled and n == 1 then |
|
71 local uv = get_fe( val ) |
|
72 return uv, uv ~= nil |
|
73 end |
|
74 return nil, false |
|
75 end |
|
76 |
|
77 elseif type( debug ) == "table" and |
|
78 type( debug.getuservalue ) == "function" then |
|
79 |
|
80 local get_uv = debug.getuservalue |
|
81 if _VERSION < "Lua 5.4" then |
|
82 local get_uv_noindex = get_uv |
|
83 function get_uv( val, n ) |
|
84 if n == 1 then |
|
85 local uv = get_uv_noindex( val ) |
|
86 return uv, uv ~= nil |
|
87 end |
|
88 return nil, false |
|
89 end |
|
90 end |
|
91 function get_environment( val, n, enabled ) |
|
92 if enabled then |
|
93 -- getuservalue in Lua 5.2 throws on light userdata! |
|
94 local ok, res1, res2 = pcall( get_uv, val, n ) |
|
95 if ok then return res1, res2 end |
|
96 return nil, false |
|
97 end |
|
98 end |
|
99 |
|
100 elseif type( getfenv ) == "function" then |
|
101 |
|
102 function get_environment( val, n, enabled ) |
|
103 if enabled and n == 1 and type( val ) == "function" then |
|
104 local uv = getfenv( val ) |
|
105 return uv, uv ~= nil |
|
106 end |
|
107 return nil, false |
|
108 end |
|
109 |
|
110 else |
|
111 |
|
112 function get_environment() return nil, false end |
|
113 |
|
114 end |
|
115 |
|
116 |
|
117 -- select implementation of get_registry |
|
118 if type( debug ) == "table" and |
|
119 type( debug.getregistry ) == "function" then |
|
120 get_registry = debug.getregistry |
|
121 else |
|
122 function get_registry() end |
|
123 end |
|
124 |
|
125 |
|
126 -- select implementation of get_locals |
|
127 -- locs = { |
|
128 -- { |
|
129 -- name = "example_function"; |
|
130 -- func = <function>; |
|
131 -- [1] = { "local_name", <local value> }; |
|
132 -- [2] = ...; |
|
133 -- }; |
|
134 -- } |
|
135 if type( debug ) == "table" and |
|
136 type( debug.getinfo ) == "function" and |
|
137 type( debug.getlocal ) == "function" then |
|
138 |
|
139 local getinfo, getlocal = debug.getinfo, debug.getlocal |
|
140 |
|
141 local function getinfo_nothread( _, func, what ) |
|
142 return getinfo( func, what ) |
|
143 end |
|
144 |
|
145 local function getlocal_nothread( _, level, loc ) |
|
146 return getlocal( level, loc ) |
|
147 end |
|
148 |
|
149 function get_locals( thread, enabled ) |
|
150 if enabled then |
|
151 local locs = {} |
|
152 local start = 1 |
|
153 local gi, gl = getinfo, getlocal |
|
154 if not thread then |
|
155 gi, gl = getinfo_nothread, getlocal_nothread |
|
156 end |
|
157 local info, i = gi( thread, 0, "nf" ), 0 |
|
158 while info do |
|
159 local t = { name = info.name, func = info.func } |
|
160 local j, n,v = 1, gl( thread, i, 1 ) |
|
161 while n ~= nil do |
|
162 t[ j ] = { n, v } |
|
163 j = j + 1 |
|
164 n,v = gl( thread, i, j ) |
|
165 end |
|
166 i = i + 1 |
|
167 locs[ i ] = t |
|
168 -- We skip the currently-executing traverse() function |
|
169 -- and anything that is only referenced through it |
|
170 if info.func == traverse then start = i+1 end |
|
171 info = gi( thread, i, "nf" ) |
|
172 end |
|
173 return locs, start |
|
174 end |
|
175 end |
|
176 |
|
177 else |
|
178 |
|
179 function get_locals() end |
|
180 |
|
181 end |
|
182 |
|
183 |
|
184 -- select implementation of upvalues depending on available API |
|
185 local function dummy_iter() end |
|
186 if type( debug ) == "table" and |
|
187 type( debug.getupvalue ) == "function" then |
|
188 |
|
189 local get_up, uv_iter = debug.getupvalue |
|
190 if _VERSION == "Lua 5.1" then |
|
191 |
|
192 function uv_iter( state ) |
|
193 local name, uv = get_up( state.value, state.n ) |
|
194 state.n = state.n + 1 |
|
195 return name, uv, nil |
|
196 end |
|
197 |
|
198 else -- Lua 5.2 (and later) mixes upvalues and environments |
|
199 |
|
200 local get_upid |
|
201 if type( debug.upvalueid ) == "function" then |
|
202 get_upid = debug.upvalueid |
|
203 end |
|
204 |
|
205 function uv_iter( state ) |
|
206 local name, uv = get_up( state.value, state.n ) |
|
207 state.n = state.n + 1 |
|
208 if name == "_ENV" and not state.show_env then |
|
209 return uv_iter( state ) |
|
210 end |
|
211 local id = nil |
|
212 if get_upid ~= nil and name ~= nil then |
|
213 id = get_upid( state.value, state.n - 1 ) |
|
214 end |
|
215 return name, uv, id |
|
216 end |
|
217 end |
|
218 |
|
219 function upvalues( val, enabled, show_env ) |
|
220 if enabled then |
|
221 return uv_iter, { value = val, n = 1, show_env = show_env } |
|
222 else |
|
223 return dummy_iter |
|
224 end |
|
225 end |
|
226 |
|
227 else |
|
228 |
|
229 function upvalues() |
|
230 return dummy_iter |
|
231 end |
|
232 |
|
233 end |
|
234 |
|
235 local function handle_metatable(env, obj) |
|
236 local mtable = get_metatable(obj) |
|
237 if mtable ~= nil then |
|
238 edge(env, obj, mtable, "ismetatable", nil) |
|
239 end |
|
240 end |
|
241 |
|
242 local function handle_environment(env, obj) |
|
243 local n = 0 |
|
244 repeat |
|
245 n = n + 1 |
|
246 local fenv, has_env = get_environment(obj, n) |
|
247 if has_env and fenv ~= nil then |
|
248 edge(env, obj, fenv, "environment", nil) |
|
249 end |
|
250 until not has_env |
|
251 end |
|
252 |
|
253 local function handle_locals(env, obj) |
|
254 local stack = get_locals(obj); |
|
255 if not stack then return; end |
|
256 for _, frame in ipairs(stack) do |
|
257 for local_entry in ipairs(frame) do |
|
258 local local_name, local_value = local_entry[1], local_entry[2]; |
|
259 edge(env, nil, local_name, "isname", nil); |
|
260 edge(env, nil, local_value, "local", local_name); |
|
261 end |
|
262 end |
47 end |
263 end |
48 |
264 |
49 -- Main function |
265 -- Main function |
50 -- 'funcs' is a table that contains a funcation for every lua type and also the |
266 -- 'funcs' is a table that contains a funcation for every lua type and also the |
51 -- function edge edge (traverseedge). |
267 -- function edge edge (traverseedge). |
52 function traverse(funcs, ignoreobjs) |
268 function traverse(funcs, ignoreobjs, seenobjs) |
53 |
269 |
54 -- The keys of the marked table are the objetcts (for example, table: 00442330). |
270 -- The keys of the marked table are the objects (for example, table: 00442330). |
55 -- The value of each key is true if the object has been found and false |
271 -- The value of each key is true if the object has been found and false |
56 -- otherwise. |
272 -- otherwise. |
57 local env = {marked = {}, list=List.new(), funcs=funcs} |
273 local env = {marked = seenobjs or {}, list=List.new(), funcs=funcs} |
58 |
274 |
59 if ignoreobjs then |
275 if ignoreobjs then |
60 for i=1, #ignoreobjs do |
276 for i=1, #ignoreobjs do |
61 env.marked[ignoreobjs[i]] = true |
277 env.marked[ignoreobjs[i]] = true |
62 end |
278 end |
63 end |
279 end |
64 |
280 |
65 env.marked["traverse"] = true |
281 env.marked["traverse"] = true |
66 env.marked[traverse] = true |
282 env.marked[traverse] = true |
67 |
283 |
68 -- marks and inserts on the list |
284 -- marks and inserts on the list |
69 edge(env, nil, "_G", "isname", nil) |
285 edge(env, nil, "_G", "isname", nil) |
70 edge(env, nil, _G, "value", "_G") |
286 edge(env, nil, _G, "value", "_G") |
|
287 |
|
288 -- traverse the registry |
|
289 local registry = get_registry(); |
|
290 if registry ~= nil then |
|
291 edge(env, nil, registry, "value", "REGISTRY"); |
|
292 end |
71 |
293 |
72 -- traverses the active thread |
294 -- traverses the active thread |
73 -- inserts the local variables |
295 -- inserts the local variables |
74 -- interates over the function on the stack, starting from the one that |
296 -- interates over the function on the stack, starting from the one that |
75 -- called traverse |
297 -- called traverse |
76 |
298 handle_locals(env, nil); |
77 for i=2, math.huge do |
299 |
78 local info = debug.getinfo(i, "f") |
300 while not List.isempty(env.list) do |
79 if not info then break end |
301 |
80 for j=1, math.huge do |
|
81 local n, v = debug.getlocal(i, j) |
|
82 if not n then break end |
|
83 |
|
84 edge(env, nil, n, "isname", nil) |
|
85 edge(env, nil, v, "local", n) |
|
86 end |
|
87 end |
|
88 |
|
89 while not List.isempty(env.list) do |
|
90 |
|
91 local obj = List.pop(env.list) |
302 local obj = List.pop(env.list) |
92 local t = type(obj) |
303 local t = type(obj) |
93 _M["traverse" .. t](env, obj) |
304 types[t](env, obj) |
94 |
305 |
95 end |
306 end |
96 |
307 end |
97 end |
308 |
98 |
309 function types.table(env, obj) |
99 function traversetable(env, obj) |
|
100 |
|
101 local f = env.funcs.table |
310 local f = env.funcs.table |
102 if f then f(obj) end |
311 if f then f(obj) end |
103 |
312 |
104 for key, value in pairs(obj) do |
313 for key, value in pairs(obj) do |
105 edge(env, obj, key, "key", nil) |
314 edge(env, obj, key, "key", nil) |
106 edge(env, obj, value, "value", key) |
315 edge(env, obj, value, "value", key) |
107 end |
316 end |
108 |
317 |
109 local mtable = debug.getmetatable(obj) |
318 handle_metatable(env, obj) |
110 if mtable then edge(env, obj, mtable, "ismetatable", nil) end |
319 end |
111 |
320 |
112 end |
321 function types.string(env, obj) |
113 |
|
114 function traversestring(env, obj) |
|
115 local f = env.funcs.string |
322 local f = env.funcs.string |
116 if f then f(obj) end |
323 if f then f(obj) end |
117 |
324 end |
118 end |
325 |
119 |
326 function types.userdata(env, obj) |
120 function traverseuserdata(env, obj) |
|
121 local f = env.funcs.userdata |
327 local f = env.funcs.userdata |
122 if f then f(obj) end |
328 if f then f(obj) end |
123 |
329 |
124 local mtable = debug.getmetatable(obj) |
330 handle_metatable(env, obj) |
125 if mtable then edge(env, obj, mtable, "ismetatable", nil) end |
331 handle_environment(env, obj) |
126 |
332 end |
127 local fenv = debug.getfenv(obj) |
333 |
128 if fenv then edge(env, obj, fenv, "environment", nil) end |
334 types["function"] = function (env, obj) |
129 |
|
130 end |
|
131 |
|
132 function traversefunction(env, obj) |
|
133 local f = env.funcs.func |
335 local f = env.funcs.func |
134 if f then f(obj) end |
336 if f then f(obj) end |
135 |
337 |
136 -- gets the upvalues |
338 handle_environment(env, obj) |
137 local i = 1 |
339 |
138 while true do |
340 -- handle upvalues |
139 local n, v = debug.getupvalue(obj, i) |
341 for name, value in upvalues(obj, true, true) do |
140 if not n then break end -- when there is no upvalues |
342 edge(env, obj, name, "isname", nil) |
141 edge(env, obj, n, "isname", nil) |
343 edge(env, obj, value, "upvalue", name) |
142 edge(env, obj, v, "upvalue", n) |
344 end |
143 i = i + 1 |
345 end |
144 end |
346 |
145 |
347 function types.thread(env, t) |
146 local fenv = debug.getfenv(obj) |
|
147 edge(env, obj, fenv, "environment", nil) |
|
148 |
|
149 end |
|
150 |
|
151 function traversethread(env, t) |
|
152 local f = env.funcs.thread |
348 local f = env.funcs.thread |
153 if f then f(t) end |
349 if f then f(t) end |
154 |
350 |
155 for i=1, math.huge do |
351 handle_environment(env, t) |
156 local info = debug.getinfo(t, i, "f") |
352 handle_locals(env, t); |
157 if not info then break end |
353 |
158 for j=1, math.huge do |
|
159 local n, v = debug.getlocal(t, i , j) |
|
160 if not n then break end |
|
161 print(n, v) |
|
162 |
|
163 edge(env, nil, n, "isname", nil) |
|
164 edge(env, nil, v, "local", n) |
|
165 end |
|
166 end |
|
167 |
|
168 local fenv = debug.getfenv(t) |
|
169 edge(env, t, fenv, "environment", nil) |
|
170 |
|
171 end |
354 end |
172 |
355 |
173 |
356 |
174 -- 'how' is a string that identifies the content of 'to' and 'value': |
357 -- 'how' is a string that identifies the content of 'to' and 'value': |
175 -- if 'how' is "key", then 'to' is a key and 'name' is nil. |
358 -- if 'how' is "key", then 'to' is a key and 'name' is nil. |
176 -- if 'how' is "value", then 'to' is an object and 'name' is the name of the |
359 -- if 'how' is "value", then 'to' is an object and 'name' is the name of the |
177 -- key. |
360 -- key. |
178 function edge(env, from, to, how, name) |
361 function edge(env, from, to, how, name) |
179 |
362 |
180 local t = type(to) |
363 local t = type(to) |
181 |
364 |
182 if to and (t~="boolean") and (t~="number") and (t~="new") then |
365 if to and (t~="boolean") and (t~="number") and (t~="new") then |
183 -- If the destination object has not been found yet |
366 -- If the destination object has not been found yet |
184 if not env.marked[to] then |
367 if not env.marked[to] then |
185 env.marked[to] = true |
368 env.marked[to] = true |
186 List.push(env.list, to) -- puts on the list to be traversed |
369 List.push(env.list, to) -- puts on the list to be traversed |
187 end |
370 end |
188 |
371 |
189 local f = env.funcs.edge |
372 local f = env.funcs.edge |
190 if f then f(from, to, how, name) end |
373 if f then f(from, to, how, name) end |
191 |
374 |
192 end |
375 end |
193 end |
376 end |
194 |
377 |
195 return _M; |
378 return { |
|
379 traverse = traverse; |
|
380 }; |