Created
May 3, 2010 01:19
-
-
Save philikon/387621 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*** 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