Quantcast Post-hooking a function safely - WoWWiki - Your guide to the World of Warcraft
Recent changes Random page
GAMING
Gaming
 
StarCraft Wiki
Super Smash Wiki
Halopedia
Diablo Wiki
FFXIclopedia
Grand Theft Wiki
See more...

Post-hooking a function safely

From WoWWiki

Jump to: navigation, search

Contents

This HOWTO discusses parameter and return value handling in a safer-than-usual way.

This HOWTO deals with post-hooks. For details on pre-hooks, see HOWTO: Hook a function safely.

For more information on the actual hooking of functions, see HOWTO: Hook a Function.

How you usually do it

 local orig_foo = foo

function foo(a1, a2)
  local r1, r2, r3 = orig_foo(a1, a2)
  -- some code that looks at e.g. a1 or r1
  return r1, r2, r3
end

The problem with this method is that it only handles a fixed number of args and returns. If the function's API is changed in the future it could break the function completely. Fortunately we have a way to guarantee that it'll work, both today, and in the future!

Red exclamation mark iconBlizzard's APIs do change from time to time!

The safe way to do it

local orig_foo = foo

function posthook(a1, a2, r1, r2, ...)
  --do something with the args (a1,a2) or return values (r1,r2) that we're interested in
  return r1, r2, ... -- this returns all the original's returns, which were passed to us   
end

function foo(a1, a2, ...)
  return posthook(a1, a2, orig_foo(a1, a2, ...))
end

Or, if you're only interested in return values:

local orig_foo = foo

function posthook(r1, r2, ...)
  --do something with r1,r2 (the first two return values)
  return r1, r2, ... -- this returns all the original's returns, which were passed to us   
end

function foo(...)
  return posthook(orig_foo(...))
end


What this does is ensure that all args are passed to the original, even if you don't know how many args there are. We also ensure that all values are returned back. As an added bonus, the use of a local for saving the original function and making a proper tail call give us the best performance we can get, thus minimizing the cost of our hook.

Won't there be a huge performance impact?

The common pre-WoW-2.0 design, which used unpack(), created a garbage table every time the hook was called. The new design takes advantage of the new, more powerful '...' variable, removing this source of garbage. With Lua 5.1 we can ensure that all args are passed to the original, and all returns are returned out of our hook, without spending a table's worth of memory on every call.

If tainting is an issue...

 function foo(a1)
    -- do something with a1
 end
 hooksecurefunc("SomeBlizzardFunction", foo);

What's the difference here? hooksecurefunc() creates a secure hook, meaning that it will not taint code. This is critical in applications where the hooked function will need to be in a secure execution path; hooking without hooksecurefunc() in these cases will result in a taint of the execution path, and then hours of debugging because Blizzard APIs are no longer working the way they should. Unfortunately, hooksecurefunc() is much more expensive than the previously mentioned methods, so it should only be used when tainting is an issue.

Rate this article:
Share this article: