This AI is still in early stages of development.
Hook API
Overview
The Hook API lets you turn an existing function in to a hookable function:
var myFn = function foo(a, b) {
return a + b;
}
myFn.canHook; // false
var myHookedFn = Hook(myFn);
myFn.canHook; // false
myHookedFn.canHook; // myFn (typecasts to true)
myHookedFn.canHook.name; // "foo"
myHookedFn.canHook.length; // 2 (myFn defines 2 formal parameters)
myHookedFn.name; // "hooked"
myHookedFn.length; // 0 (doesn't define formal parameters)
It's a bit like function.wraps(), in that the original function (eg. myFn) will be wrapped in a new function (eg. myHookedFn). The main difference is that once you've made a function hookable, you can add input and output hooks to it.
Input hooks
An input hook lets you alter the parameters (params) that will be sent to the inner function. The input parameters are:
- params – an editable array of parameters that will be sent to the inner function
- args – a read-only array of parameters that were used on the outer function
- hooked – a reference to the outer function (get the inner function via
hooked.canHook)
Continuing from the last example...
myHookedFn.onInput; // 0 (typecasts to false)
// catch the fish
myHookedFn.onInput = function(params, args, hooked) {
// play with it (note: arguments is immutable)
params[0] = 10; // a => 10
// throw it back in the river
return params;
}
myHookedFn.onInput; // 1 (typecasts to true)
myFn(1, 2); // 3 (1 + 2)
myHookedFn(1, 2); // returns 12 (10 + 2)
Remember to return the params array, otherwise the inner function won't be called.
Output hooks
An output hook lets you alter the functions return value.
The parameters are:
- result – the editable result returned by the inner function
- params – a read-only array of parameters that will be sent to the inner function
- args – a read-only array of parameters that were used on the outer function
- hooked – a reference to the outer function (get the inner function via
hooked.canHook)
Continuing from the last example...
myHookedFn.onOutput; // 0
myHookedFn.onOutput = function(result, params, args, hooked) {
result -= 3;
return result;
}
myHookedFn.onOutput; // 1
myHookedFn(1, 2); // 9 ((10 + 2) - 3)
Remember to return the result, otherwise the return value sent to the calling script will be undefined.
Multiple hooks
You can add any number of input/output hooks:
- All the input hooks will run first, on a first-come first-served basis
- The inner function will then be invoked (unless one of the input hooks aborted the invocation)
- All the output hooks will run last, on a first-come first-served basis
- The final result will be passed back to the calling script
Continuing from the last example...
// add another output function, it will be invoked after previous one
myHookedFn.onOutput = function(result, params, args, hooked) {
// params[0] == 10 due to input hook
result += args[0]; // args[0] unaffected by alterations to params array
return result;
}
myHookedFn.onOutput; // 2
myHookedFn(1, 2); // 10 (((10 + 2) - 3) + 1)
myHookedFn(-4, 5); // 8 (((10 + 5) - 3) + -4)
Aborting function invocations
You can abort a function run during an input hook by returning undefined instead of the params array.
Continuing from the last example...
myHookedFn.onInput = function(params, args, hooked) {
if (args[0] < 0) {
return void Hook; // kill the function invocation
// note: output hooks will be skipped
}
}
myHookedFn(1, 2); // 10 (((10 + 2) - 3) + 1)
myHookedFn(-4, 5); // undefined (invocation was aborted)
Notes
Hook(Hook); // Error
Hook(myHookedFn) === myHookedFn
Define("foo", myFn); Hook(myFn); // Error
Define("foo", Hook(myFn)); // ±1