Skip to content

Instantly share code, notes, and snippets.

@philikon
Created May 3, 2010 01:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save philikon/387621 to your computer and use it in GitHub Desktop.
Save philikon/387621 to your computer and use it in GitHub Desktop.
/*** Monkey patch vs. evil() ***/
// Consider an object with a method:
var Obj = {
aMethod: function (arg) {
print("Hello " + arg);
}
};
Obj.aMethod("World!");
// Imagine we want to extend this method (e.g. do something before or
// after it is run, transform the arguments or return value, etc.)
var newMethod = function (arg) {
this.oldMethod(arg.toUpperCase());
};
// In the simplest case, one could patch the method in like this and
// all would be fine
Obj.oldMethod = Obj.aMethod;
Obj.aMethod = newMethod;
Obj.aMethod("World!");
// Now imagine some other code wanted to patch the (original) method
// as well, using an evil() hack. This crude replacement of parts of
// the code would work on the original method, but it's been replaced!
function evilModule () {
function alert () {
print("\7"); // bell
print.apply(this, arguments);
}
eval('Obj.aMethod = ' + Obj.aMethod.toString().replace('print', 'alert'));
};
evilModule();
// Not the desired effect (no bell) because evilModule saw our new
// method that doesn't mention "print" in its source code.
Obj.aMethod("World!");
/*** Enter monkeyPatchMethod ***/
/*
* Replace a method with another one while still making the all too
* popuplar kind of toSource + eval hack possible.
*/
function monkeyPatchMethod (obj, name, backupname, func) {
obj[backupname] = obj[name];
// Make the new method "look" like the old one.
func.toSource = function () {
return obj[backupname].toSource();
};
func.toString = function () {
return obj[backupname].toString();
};
// If anyone should try to replace the method, replace the old one.
obj.__defineGetter__(name, function () {
return func;
});
obj.__defineSetter__(name, function (value) {
obj[backupname] = value;
});
}
// Let's consider the same object again:
Obj = {
aMethod: function (arg) {
print("Hello " + arg);
}
};
// Now use monkeyPatchMethod to patch in the new method, and then do
// the evil() hack again.
monkeyPatchMethod(Obj, "aMethod", "oldMethod", newMethod);
evilModule();
// Now both patches work: the eval() one modified the original method, the
Obj.aMethod("World!");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment