vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2 Name: AceHook-2.1
3 Revision: $Rev: 15814 $
4 Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
5 Inspired By: Ace 1.x by Turan (turan@gryphon.com)
6 Website: http://www.wowace.com/
7 Documentation: http://www.wowace.com/index.php/AceHook-2.1
8 SVN: http://svn.wowace.com/root/trunk/Ace2/AceHook-2.1
9 Description: Mixin to allow for safe hooking of functions, methods, and scripts.
10 Dependencies: AceLibrary, AceOO-2.0
11 ]]
12  
13 local MAJOR_VERSION = "AceHook-2.1"
14 local MINOR_VERSION = "$Revision: 15814 $"
15  
16 -- This ensures the code is only executed if the libary doesn't already exist, or is a newer version
17 if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
18 if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
19  
20 if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
21  
22 --[[---------------------------------------------------------------------------------
23 Create the library object
24 ----------------------------------------------------------------------------------]]
25  
26 local AceOO = AceLibrary:GetInstance("AceOO-2.0")
27 local AceHook = AceOO.Mixin {
28 "Hook",
29 "HookScript",
30 "SecureHook",
31 "Unhook",
32 "UnhookAll",
33 "HookReport",
34 "IsHooked",
35 }
36  
37 --[[---------------------------------------------------------------------------------
38 Library Definitions
39 ----------------------------------------------------------------------------------]]
40  
41 local protFuncs = {
42 CameraOrSelectOrMoveStart = true, CameraOrSelectOrMoveStop = true,
43 TurnOrActionStart = true, TurnOrActionStop = true,
44 PitchUpStart = true, PitchUpStop = true,
45 PitchDownStart = true, PitchDownStop = true,
46 MoveBackwardStart = true, MoveBackwardStop = true,
47 MoveForwardStart = true, MoveForwardStop = true,
48 Jump = true, StrafeLeftStart = true,
49 StrafeLeftStop = true, StrafeRightStart = true,
50 StrafeRightStop = true, ToggleMouseMove = true,
51 ToggleRun = true, TurnLeftStart = true,
52 TurnLeftStop = true, TurnRightStart = true,
53 TurnRightStop = true,
54 }
55  
56 local function issecurevariable(x)
57 if protFuncs[x] then
58 return 1
59 else
60 return nil
61 end
62 end
63  
64 local _G = getfenv(0)
65  
66 local function hooksecurefunc(arg1, arg2, arg3)
67 if type(arg1) == "string" then
68 arg1, arg2, arg3 = _G, arg1, arg2
69 end
70 local orig = arg1[arg2]
71 arg1[arg2] = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
72 local x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20 = orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
73  
74 arg3(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
75  
76 return x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20
77 end
78 end
79  
80 local protectedScripts = {
81 OnClick = true,
82 }
83  
84  
85 local handlers, scripts, actives, registry
86  
87 --[[---------------------------------------------------------------------------------
88 Private definitions (Not exposed)
89 ----------------------------------------------------------------------------------]]
90  
91 local new, del
92 do
93 local list = setmetatable({}, {__mode = "k"})
94 function new()
95 local t = next(list)
96 if not t then
97 return {}
98 end
99 list[t] = nil
100 return t
101 end
102  
103 function del(t)
104 setmetatable(t, nil)
105 for k in pairs(t) do
106 t[k] = nil
107 end
108 list[t] = true
109 end
110 end
111  
112 local function createFunctionHook(self, func, handler, orig, secure)
113 if not secure then
114 if type(handler) == "string" then
115 -- The handler is a method, need to self it
116 local uid
117 uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
118 if actives[uid] then
119 return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
120 else
121 return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
122 end
123 end
124 return uid
125 else
126 -- The handler is a function, just call it
127 local uid
128 uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
129 if actives[uid] then
130 return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
131 else
132 return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
133 end
134 end
135 return uid
136 end
137 else
138 -- secure hooks don't call the original method
139 if type(handler) == "string" then
140 -- The handler is a method, need to self it
141 local uid
142 uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
143 if actives[uid] then
144 return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
145 end
146 end
147 return uid
148 else
149 -- The handler is a function, just call it
150 local uid
151 uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
152 if actives[uid] then
153 return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
154 end
155 end
156 return uid
157 end
158 end
159 end
160  
161 local function createMethodHook(self, object, method, handler, orig, secure, script)
162 if script then
163 if type(handler) == "string" then
164 local uid
165 uid = function()
166 if actives[uid] then
167 return self[handler](self, object)
168 else
169 return orig()
170 end
171 end
172 return uid
173 else
174 -- The handler is a function, just call it
175 local uid
176 uid = function()
177 if actives[uid] then
178 return handler(object)
179 else
180 return orig()
181 end
182 end
183 return uid
184 end
185 elseif not secure then
186 if type(handler) == "string" then
187 local uid
188 uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
189 if actives[uid] then
190 return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
191 else
192 return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
193 end
194 end
195 return uid
196 else
197 -- The handler is a function, just call it
198 local uid
199 uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
200 if actives[uid] then
201 return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
202 else
203 return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
204 end
205 end
206 return uid
207 end
208 else
209 -- secure hooks don't call the original method
210 if type(handler) == "string" then
211 local uid
212 uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
213 if actives[uid] then
214 return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
215 end
216 end
217 return uid
218 else
219 -- The handler is a function, just call it
220 local uid
221 uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
222 if actives[uid] then
223 return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
224 end
225 end
226 return uid
227 end
228 end
229 end
230  
231 local function hookFunction(self, func, handler, secure)
232 local orig = _G[func]
233  
234 if not orig or type(orig) ~= "function" then
235 AceHook:error("Attempt to hook a non-existant function %q", func)
236 end
237  
238 if not handler then
239 handler = func
240 end
241  
242 if registry[self][func] then
243 local uid = registry[self][func]
244  
245 if actives[uid] then
246 -- We have an active hook from this source. Don't multi-hook
247 AceHook:error("%q already has an active hook from this source.", func)
248 end
249  
250 if handlers[uid] == handler then
251 -- The hook is inactive, so reactivate it
252 actives[uid] = true
253 return
254 else
255 AceHook:error("There is a stale hook for %q can't hook or reactivate.", func)
256 end
257 end
258  
259 if type(handler) == "string" then
260 if type(self[handler]) ~= "function" then
261 AceHook:error("Could not find the the handler %q when hooking function %q", handler, func)
262 end
263 elseif type(handler) ~= "function" then
264 AceHook:error("Could not find the handler you supplied when hooking %q", func)
265 end
266  
267 local uid = createFunctionHook(self, func, handler, orig, secure)
268 registry[self][func] = uid
269 actives[uid] = true
270 handlers[uid] = handler
271  
272 if not secure then
273 _G[func] = uid
274 self.hooks[func] = orig
275 else
276 hooksecurefunc(func, uid)
277 end
278 end
279  
280 local function unhookFunction(self, func)
281 if not registry[self][func] then
282 AceHook:error("Tried to unhook %q which is not currently hooked.", func)
283 end
284  
285 local uid = registry[self][func]
286  
287 if actives[uid] then
288 -- See if we own the global function
289 if self.hooks[func] and _G[func] == uid then
290 _G[func] = self.hooks[func]
291 self.hooks[func] = nil
292 registry[self][func] = nil
293 handlers[uid] = nil
294 scripts[uid] = nil
295 actives[uid] = nil
296 -- Magically all-done
297 else
298 actives[uid] = nil
299 end
300 end
301 end
302  
303 local function hookMethod(self, obj, method, handler, script, secure)
304 if not handler then
305 handler = method
306 end
307  
308 if not obj or type(obj) ~= "table" then
309 AceHook:error("The object you supplied could not be found, or isn't a table.")
310 end
311  
312 local uid = registry[self][obj] and registry[self][obj][method]
313 if uid then
314 if actives[uid] then
315 -- We have an active hook from this source. Don't multi-hook
316 AceHook:error("%q already has an active hook from this source.", method)
317 end
318  
319 if handlers[uid] == handler then
320 -- The hook is inactive, reactivate it.
321 actives[uid] = true
322 return
323 else
324 AceHook:error("There is a stale hook for %q can't hook or reactivate.", method)
325 end
326 end
327  
328 if type(handler) == "string" then
329 if type(self[handler]) ~= "function" then
330 AceHook:error("Could not find the handler %q you supplied when hooking method %q", handler, method)
331 end
332 elseif type(handler) ~= "function" then
333 AceHook:error("Could not find the handler you supplied when hooking method %q", method)
334 end
335  
336 local orig
337 if script then
338 if not obj.GetScript then
339 AceHook:error("The object you supplied does not have a GetScript method.")
340 end
341 if not obj:HasScript(method) then
342 AceHook:error("The object you supplied doesn't allow the %q method.", method)
343 end
344  
345 orig = obj:GetScript(method)
346 if type(orig) ~= "function" then
347 -- Sometimes there is not a original function for a script.
348 orig = function() end
349 end
350 else
351 orig = obj[method]
352 end
353 if not orig then
354 AceHook:error("Could not find the method or script %q you are trying to hook.", method)
355 end
356  
357 if not registry[self][obj] then
358 registry[self][obj] = new()
359 end
360  
361 if not self.hooks[obj] then
362 self.hooks[obj] = new()
363 end
364  
365 local uid = createMethodHook(self, obj, method, handler, orig, secure, script)
366 registry[self][obj][method] = uid
367 actives[uid] = true
368 handlers[uid] = handler
369 scripts[uid] = script and true or nil
370  
371 if script then
372 obj:SetScript(method, uid)
373 self.hooks[obj][method] = orig
374 elseif not secure then
375 obj[method] = uid
376 self.hooks[obj][method] = orig
377 else
378 hooksecurefunc(obj, method, uid)
379 end
380 end
381  
382 local function unhookMethod(self, obj, method)
383 if not registry[self][obj] or not registry[self][obj][method] then
384 AceHook:error("Attempt to unhook a method %q that is not currently hooked.", method)
385 return
386 end
387  
388 local uid = registry[self][obj][method]
389  
390 if actives[uid] then
391 if scripts[uid] then -- If this is a script
392 if obj:GetScript(method) == uid then
393 -- We own the script. Revert to normal.
394 obj:SetScript(method, self.hooks[obj][method])
395 self.hooks[obj][method] = nil
396 registry[self][obj][method] = nil
397 handlers[uid] = nil
398 scripts[uid] = nil
399 actives[uid] = nil
400 else
401 actives[uid] = nil
402 end
403 else
404 if self.hooks[obj][method] and obj[method] == uid then
405 -- We own the method. Revert to normal.
406 obj[method] = self.hooks[obj][method]
407 self.hooks[obj][method] = nil
408 registry[self][obj][method] = nil
409 handlers[uid] = nil
410 actives[uid] = nil
411 else
412 actives[uid] = nil
413 end
414 end
415 end
416 if not next(self.hooks[obj]) then
417 self.hooks[obj] = del(self.hooks[obj])
418 end
419 if not next(registry[self][obj]) then
420 registry[self][obj] = del(registry[self][obj])
421 end
422 end
423  
424 -- ("function" [, handler] [, hookSecure]) or (object, "method" [, handler] [, hookSecure])
425 function AceHook:Hook(object, method, handler, hookSecure)
426 if type(object) == "string" then
427 method, handler, hookSecure = object, method, handler
428 if handler == true then
429 handler, hookSecure = nil, true
430 end
431 if not hookSecure and issecurevariable(method) then
432 AceHook:error("Attempt to hook secure function %q. Use `SecureHook' or add `true' to the argument list to override.", method)
433 end
434 AceHook:argCheck(handler, 3, "function", "string", "nil")
435 AceHook:argCheck(hookSecure, 4, "boolean", "nil")
436 hookFunction(self, method, handler, false)
437 else
438 if handler == true then
439 handler, hookSecure = nil, true
440 end
441 if not hookSecure and issecurevariable(object, method) then
442 AceHook:error("Attempt to hook secure method %q. Use `SecureHook' or add `true' to the argument list to override.", method)
443 end
444 AceHook:argCheck(object, 2, "table")
445 AceHook:argCheck(method, 3, "string")
446 AceHook:argCheck(handler, 4, "function", "string", "nil")
447 AceHook:argCheck(hookSecure, 5, "boolean", "nil")
448 hookMethod(self, object, method, handler, false, false)
449 end
450 end
451  
452 -- ("function", handler) or (object, "method", handler)
453 function AceHook:SecureHook(object, method, handler)
454 if type(object) == "string" then
455 method, handler = object, method
456 AceHook:argCheck(handler, 3, "function", "string", "nil")
457 hookFunction(self, method, handler, true)
458 else
459 AceHook:argCheck(object, 2, "table")
460 AceHook:argCheck(method, 3, "string")
461 AceHook:argCheck(handler, 4, "function", "string", "nil")
462 hookMethod(self, object, method, handler, false, true)
463 end
464 end
465  
466 function AceHook:HookScript(frame, script, handler)
467 AceHook:argCheck(frame, 2, "table")
468 if not frame[0] or type(frame.IsFrameType) ~= "function" then
469 AceHook:error("Bad argument #2 to `HookScript'. Expected frame.")
470 end
471 AceHook:argCheck(script, 3, "string")
472 AceHook:argCheck(handler, 4, "function", "string", "nil")
473 hookMethod(self, frame, script, handler, true, false)
474 end
475  
476 -- ("function") or (object, "method")
477 function AceHook:IsHooked(obj, method)
478 if type(obj) == "string" then
479 if registry[self][obj] and actives[registry[self][obj]] then
480 return true, handlers[registry[self][obj]]
481 end
482 else
483 AceHook:argCheck(obj, 2, "string", "table")
484 AceHook:argCheck(method, 3, "string")
485 if registry[self][obj] and registry[self][obj][method] and actives[registry[self][obj][method]] then
486 return true, handlers[registry[self][obj][method]]
487 end
488 end
489  
490 return false, nil
491 end
492  
493 -- ("function") or (object, "method")
494 function AceHook:Unhook(obj, method)
495 if type(obj) == "string" then
496 unhookFunction(self, obj)
497 else
498 AceHook:argCheck(obj, 2, "string", "table")
499 AceHook:argCheck(method, 3, "string")
500 unhookMethod(self, obj, method)
501 end
502 end
503  
504 function AceHook:UnhookAll()
505 for key, value in pairs(registry[self]) do
506 if type(key) == "table" then
507 for method in pairs(value) do
508 self:Unhook(key, method)
509 end
510 else
511 self:Unhook(key)
512 end
513 end
514 end
515  
516 function AceHook:HookReport()
517 DEFAULT_CHAT_FRAME:AddMessage("This is a list of all active hooks for this object:")
518 if not next(registry[self]) then
519 DEFAULT_CHAT_FRAME:AddMessage("No hooks")
520 end
521  
522 for key, value in pairs(registry[self]) do
523 if type(value) == "table" then
524 for method, uid in pairs(value) do
525 DEFAULT_CHAT_FRAME:AddMessage(string.format("object: %s method: %q |cff%s|r%s", tostring(key), method, actives[uid] and "00ff00Active" or "ffff00Inactive", not self.hooks[key][method] and " |cff7f7fff-Secure-|r" or ""))
526 end
527 else
528 DEFAULT_CHAT_FRAME:AddMessage(string.format("function: %q |cff%s|r%s", tostring(key), actives[value] and "00ff00Active" or "ffff00Inactive", not self.hooks[key] and " |cff7f7fff-Secure-|r" or ""))
529 end
530 end
531 end
532  
533 function AceHook:OnInstanceInit(object)
534 if not object.hooks then
535 object.hooks = new()
536 end
537 if not registry[object] then
538 registry[object] = new()
539 end
540 end
541  
542 AceHook.OnManualEmbed = AceHook.OnInstanceInit
543  
544 function AceHook:OnEmbedDisable(target)
545 self.UnhookAll(target)
546 end
547  
548 local function activate(self, oldLib, oldDeactivate)
549 AceHook = self
550  
551 self.handlers = oldLib and oldLib.handlers or {}
552 self.registry = oldLib and oldLib.registry or {}
553 self.scripts = oldLib and oldLib.scripts or {}
554 self.actives = oldLib and oldLib.actives or {}
555  
556 handlers = self.handlers
557 registry = self.registry
558 scripts = self.scripts
559 actives = self.actives
560  
561 AceHook.super.activate(self, oldLib, oldDeactivate)
562  
563 if oldDeactivate then
564 oldDeactivate(oldLib)
565 end
566 end
567  
568 AceLibrary:Register(AceHook, MAJOR_VERSION, MINOR_VERSION, activate)