vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2 Name: AceHook-2.0
3 Revision: $Rev: 7464 $
4 Author(s): cladhaire (cladhaire@gmail.com)
5 ckknight (ckknight@gmail.com)
6 Inspired By: AceHook 1.x by Turan (<email here>)
7 Website: http://www.wowace.com/
8 Documentation: http://wiki.wowace.com/index.php/AceHook-2.0
9 SVN: http://svn.wowace.com/root/trunk/Ace2/AceHook-2.0
10 Description: Mixin to allow for safe hooking of functions, methods, and scripts.
11 Dependencies: AceLibrary, AceOO-2.0
12 ]]
13  
14 local MAJOR_VERSION = "AceHook-2.0"
15 local MINOR_VERSION = "$Revision: 7464 $"
16  
17 -- This ensures the code is only executed if the libary doesn't already exist, or is a newer version
18 if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
19 if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
20  
21 if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end
22  
23 --[[---------------------------------------------------------------------------------
24 Create the library object
25 ----------------------------------------------------------------------------------]]
26  
27 local AceOO = AceLibrary:GetInstance("AceOO-2.0")
28 local AceHook = AceOO.Mixin {
29 "Hook",
30 "Unhook",
31 "UnhookAll",
32 "HookReport",
33 "IsHooked",
34 "HookScript",
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 _G = getfenv(0)
57  
58 --[[---------------------------------------------------------------------------------
59 Private definitions (Not exposed)
60 ----------------------------------------------------------------------------------]]
61  
62 --[[----------------------------------------------------------------------
63 _debug - Internal Method
64 -------------------------------------------------------------------------]]
65 local function print(text)
66 DEFAULT_CHAT_FRAME:AddMessage(text)
67 end
68  
69 local function _debug(self, msg)
70 local name = self.hooks.name
71 if name then
72 print(string.format("[%s]: %s", name, msg))
73 else
74 print(msg)
75 end
76 end
77  
78 local donothing
79  
80 local new, del
81 do
82 local list = setmetatable({}, {__mode = "k"})
83 function new()
84 local t = next(list)
85 if not t then
86 return {}
87 end
88 list[t] = nil
89 return t
90 end
91  
92 function del(t)
93 setmetatable(t, nil)
94 table.setn(t, 0)
95 for k in pairs(t) do
96 t[k] = nil
97 end
98 list[t] = true
99 end
100 end
101  
102 --[[----------------------------------------------------------------------
103 AceHook:_getFunctionHook- internal method
104 -------------------------------------------------------------------------]]
105  
106 local function _getFunctionHook(self, func, handler)
107 local orig = self.hooks[func].orig
108 if type(handler) == "string" then
109 -- The handler is a method, need to self it
110 return function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
111 if self.hooks and self.hooks[func] and self.hooks[func].active then
112 return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
113 elseif orig then
114 return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
115 end
116 end
117 else
118 -- The handler is a function, just call it
119 return function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
120 if self.hooks and self.hooks[func] and self.hooks[func].active then
121 return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
122 elseif orig then
123 return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
124 end
125 end
126 end
127 end
128  
129 --[[----------------------------------------------------------------------
130 AceHook:_getMethodHook - Internal Method
131 -------------------------------------------------------------------------]]
132 local function _getMethodHook(self, object, method, handler, script)
133 local orig = self.hooks[object][method].orig
134 if type(handler) == "string" then
135 -- The handler is a method, need to self it
136 if script then
137 return function()
138 if self.hooks and self.hooks[object] and self.hooks[object][method] and self.hooks[object][method].active then
139 return self[handler](self, object)
140 elseif orig then
141 return orig()
142 end
143 end
144 else
145 return function(obj,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
146 if self.hooks and self.hooks[obj] and self.hooks[obj][method] and self.hooks[obj][method].active then
147 return self[handler](self, obj, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
148 elseif orig then
149 return orig(object, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
150 end
151 end
152 end
153 else
154 -- The handler is a function, just call it
155 if script then
156 return function()
157 if self.hooks and self.hooks[object] and self.hooks[object][method] and self.hooks[object][method].active then
158 return handler(object)
159 elseif orig then
160 return orig()
161 end
162 end
163 else
164 return function(obj,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
165 if self.hooks and self.hooks[obj] and self.hooks[obj][method] and self.hooks[obj][method].active then
166 return handler(obj,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
167 elseif orig then
168 return orig(object, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
169 end
170 end
171 end
172 end
173 end
174  
175 --[[----------------------------------------------------------------------
176 AceHook:HookFunc - internal method.
177 o You can only hook each function once from each source.
178 o If there is an inactive hook for this func/handler pair, we reactivate it
179 o If there is an inactive hook for another handler, we error out.
180 o Looks for handler as a method of the calling class, error if not available
181 o If handler is a function, it just uses it directly through the wrapper
182 -------------------------------------------------------------------------]]
183 local function _hookFunc(self, func, handler)
184 local f = _G[func]
185  
186 if not f or type(f) ~= "function" then
187 _debug(self, string.format("Attempt to hook a non-existant function %q", func),3)
188 return
189 end
190  
191 if not handler then handler = func end
192  
193 if self.hooks[func] then
194 -- We have an active hook from this source. Don't multi-hook
195 if self.hooks[func].active then
196 _debug(self, string.format("%q already has an active hook from this source.", func))
197 return
198 end
199 -- The hook is inactive, so reactivate it
200 if self.hooks[func].handler == handler then
201 self.hooks[func].active = true
202 return
203 else
204 AceHook:error("There is a stale hook for %q can't hook or reactivate.", func)
205 end
206 end
207  
208 if type(handler) == "string" then
209 if type(self[handler]) ~= "function" then
210 AceHook:error("Could not find the the handler %q when hooking function %q", handler, func)
211 end
212 elseif type(handler) ~= "function" then
213 AceHook:error("Could not find the handler you supplied when hooking %q", func)
214 end
215  
216 local t = new()
217 self.hooks[func] = t
218 t.orig = f
219 t.active = true
220 t.handler = handler
221 t.func = _getFunctionHook(self, func, handler)
222  
223 _G[func] = t.func
224 end
225  
226 --[[----------------------------------------------------------------------
227 AceHook:UnhookFunc - internal method
228 o If you attempt to unhook a function that has never been hooked, or to unhook in a
229 system that has never had a hook before, the system will error with a stack trace
230 o If we own the global function, then put the original back in its place and remove
231 all references to the Hooks[func] structure.
232 o If we don't own the global function (we've been hooked) we deactivate the hook,
233 forcing the handler to passthrough.
234 -------------------------------------------------------------------------]]
235 local function _unhookFunc(self, func)
236 if not self.hooks[func] then
237 _debug(self, string.format("Tried to unhook %q which is not currently hooked.", func))
238 return
239 end
240 if self.hooks[func].active then
241 -- See if we own the global function
242 if _G[func] == self.hooks[func].func then
243 _G[func] = self.hooks[func].orig
244 del(self.hooks[func])
245 self.hooks[func] = nil
246 -- Magically all-done
247 else
248 self.hooks[func].active = nil
249 end
250 end
251 end
252  
253 --[[----------------------------------------------------------------------
254 AceHook:HookMeth - Takes an optional fourth argument
255 o script - Signifies whether this is a script hook or not
256 -------------------------------------------------------------------------]]
257  
258 local function _hookMeth(self, obj, method, handler, script)
259 if not handler then handler = method end
260 if (not obj or type(obj) ~= "table") then
261 AceHook:error("The object you supplied could not be found, or isn't a table.")
262 end
263  
264 if self.hooks[obj] and self.hooks[obj][method] then
265 -- We have an active hook from this source. Don't multi-hook
266 if self.hooks[obj][method].active then
267 _debug(self, string.format("%q already has an active hook from this source.", method))
268 return
269 end
270 -- The hook is inactive, so reactivate it.
271 if self.hooks[obj][method].handler == handler then
272 self.hooks[obj][method].active = true
273 return
274 else
275 AceHook:error("There is a stale hook for %q can't hook or reactivate.", method)
276 end
277 end
278 -- We're clear to try the hook, let's make some checks first
279 if type(handler) == "string" then
280 if type(self[handler]) ~= "function" then
281 AceHook:error("Could not find the handler %q you supplied when hooking method %q", handler, method)
282 end
283 elseif type(handler) ~= "function" then
284 AceHook:error("Could not find the handler you supplied when hooking method %q", method)
285 end
286 -- Handler has been found, so now try to find the method we're trying to hook
287 local orig
288 -- Script
289 if script then
290 if not obj.GetScript then
291 AceHook:error("The object you supplied does not have a GetScript method.")
292 end
293 if not obj:HasScript(method) then
294 AceHook:error("The object you supplied doesn't allow the %q method.", method)
295 end
296 -- Sometimes there is not a original function for a script.
297 orig = obj:GetScript(method)
298 if not orig then
299 if not donothing then
300 function donothing() end
301 end
302 orig = donothing
303 end
304 -- Method
305 else
306 orig = obj[method]
307 end
308 if not orig then
309 AceHook:error("Could not find the method or script %q you are trying to hook.", method)
310 end
311 if not self.hooks[obj] then
312 self.hooks[obj] = new()
313 end
314 local t = new()
315 self.hooks[obj][method] = t
316 t.orig = orig
317 t.active = true
318 t.handler = handler
319 t.script = script
320 t.func = _getMethodHook(self, obj, method, handler, script)
321  
322 if script then
323 obj:SetScript(method, t.func)
324 else
325 obj[method] = t.func
326 end
327 end
328  
329 --[[----------------------------------------------------------------------
330 AceHook:UnhookMeth - Internal method
331 o If you attempt to unhook a method that has never been hooked, or to unhook in a
332 system that has never had a hook before, the system will error with a stack trace
333 o If we own the global method, then put the original back in its place and remove
334 all references to the Hooks[obj][method] structure.
335 o If we don't own the global method (we've been hooked) we deactivate the hook,
336 forcing the handler to passthrough.
337 -------------------------------------------------------------------------]]
338 local function _unhookMeth(self, obj, method)
339 if not self.hooks[obj] or not self.hooks[obj][method] then
340 _debug(self, string.format("Attempt to unhook a method %q that is not currently hooked.", method))
341 return
342 end
343 if self.hooks[obj][method].active then
344 -- If this is a script
345 if self.hooks[obj][method].script then
346 if obj:GetScript(method) == self.hooks[obj][method].func then
347 -- We own the script. Kill it.
348 obj:SetScript(method, self.hooks[obj][method].orig)
349 del(self.hooks[obj][method])
350 self.hooks[obj][method] = nil
351 else
352 self.hooks[obj][method].active = nil
353 end
354 else
355 if obj[method] == self.hooks[obj][method].func then
356 -- We own the method. Kill it.
357 obj[method] = self.hooks[obj][method].orig
358 del(self.hooks[obj][method])
359 self.hooks[obj][method] = nil
360 else
361 self.hooks[obj][method].active = nil
362 end
363 end
364 end
365 if not next(self.hooks[obj]) then
366 -- Spank the table
367 del(self.hooks[obj])
368 self.hooks[obj] = nil
369 end
370 end
371  
372 function AceHook:OnInstanceInit(object)
373 if not object.hooks then
374 object.hooks = new()
375 end
376  
377 local name
378  
379 if type(rawget(object, 'GetLibraryVersion')) == "function" then
380 name = object:GetLibraryVersion()
381 end
382 if not name and type(object.GetName) == "function" then
383 name = object:GetName()
384 end
385 if not name and type(object.name) == "string" then
386 name = object.name
387 end
388 if not name then
389 for k,v in pairs(_G) do
390 if v == object then
391 name = tostring(k)
392 break
393 end
394 end
395 end
396  
397 object.hooks.name = name
398 end
399  
400 AceHook.OnManualEmbed = AceHook.OnInstanceInit
401  
402 --[[----------------------------------------------------------------------
403 AceHook:Hook
404 self:Hook("functionName", ["handlerName" | handler])
405 self:Hook(ObjectName, "Method", ["Handler" | handler])
406 -------------------------------------------------------------------------]]
407 function AceHook:Hook(arg1, arg2, arg3)
408 if type(arg1)== "string" then
409 if protFuncs[arg1] then
410 if self.hooks.name then
411 AceHook:error("%s tried to hook %q, which is a Blizzard protected function.", self.hooks.name, arg1)
412 else
413 _debug(self, string.format("An Addon tried to hook %q, which is a Blizzard protected function.", arg1))
414 end
415 else
416 _hookFunc(self, arg1, arg2)
417 end
418 else
419 _hookMeth(self, arg1, arg2, arg3)
420 end
421 end
422  
423 function AceHook:HookScript(arg1, arg2, arg3)
424 _hookMeth(self, arg1, arg2, arg3, true)
425 end
426  
427 --[[----------------------------------------------------------------------
428 AceHook:IsHooked()
429 self:Hook("functionName")
430 self:Hook(ObjectName, "Method")
431  
432 Returns whether or not the given function is hooked in the current
433 namespace. A hooked, but inactive function is considered NOT
434 hooked in this context.
435 -------------------------------------------------------------------------]]
436 function AceHook:IsHooked(obj, method)
437 if method and obj then
438 if self.hooks and self.hooks[obj] and self.hooks[obj][method] and self.hooks[obj][method].active then
439 return true, self.hooks[obj][method].handler
440 end
441 else
442 if self.hooks and self.hooks[obj] and self.hooks[obj].active then
443 return true, self.hooks[obj].handler
444 end
445 end
446  
447 return false, nil
448 end
449  
450 --[[----------------------------------------------------------------------
451 AceHook:Unhook
452 self:Unhook("functionName")
453 self:Unhook(ObjectName, "Method")
454 -------------------------------------------------------------------------]]
455 function AceHook:Unhook(arg1, arg2)
456 if type(arg1) == "string" then
457 _unhookFunc(self, arg1)
458 else
459 _unhookMeth(self, arg1, arg2)
460 end
461 end
462  
463 --[[----------------------------------------------------------------------
464 AceHook:UnhookAll - Unhooks all active hooks from the calling source
465 -------------------------------------------------------------------------]]
466 function AceHook:UnhookAll()
467 for key, value in pairs(self.hooks) do
468 if type(key) == "table" then
469 for method in pairs(value) do
470 self:Unhook(key, method)
471 end
472 else
473 self:Unhook(key)
474 end
475 end
476 end
477  
478  
479 function AceHook:OnEmbedDisable(target)
480 self.UnhookAll(target)
481 end
482  
483 --[[----------------------------------------------------------------------
484 AceHook:HookReport - Lists registered hooks from this source
485 -------------------------------------------------------------------------]]
486  
487 function AceHook:HookReport()
488 _debug(self, "This is a list of all active hooks for this object:")
489 if not self.hooks then _debug(self, "No registered hooks.") return end
490  
491 for key, value in pairs(self.hooks) do
492 if type(value) == "table" then
493 for method in pairs(value) do
494 _debug(self, string.format("key: %s method: %q |cff%s|r", tostring(key), method, self.hooks[key][method].active and "00ff00Active" or "ffff00Inactive"))
495 end
496 else
497 _debug(self, string.format("key: %s value: %q |cff%s|r", tostring(key), tostring(value), self.hooks[key].active and "00ff00Active" or "ffff00Inactive"))
498 end
499 end
500 end
501  
502 --[[---------------------------------------------------------------------------------
503 Stub and Library registration
504 ----------------------------------------------------------------------------------]]
505  
506 function AceHook:activate(oldLib, oldDeactivate)
507 AceHook = AceLibrary(MAJOR_VERSION)
508  
509 AceHook.super.activate(self, oldLib, oldDeactivate)
510 end
511  
512 AceLibrary:Register(AceHook, MAJOR_VERSION, MINOR_VERSION, AceHook.activate)
513 AceHook = AceLibrary(MAJOR_VERSION)