Blender
Blender

Reputation: 298226

Monkey-patching a named function that's called immediately

A script I'm loading is misbehaving. It's got a structure like this:

function bad_function() {
    ...
}

/* randomly generated stuff */

bad_function();

How do I modify the behavior of bad_function if it's called immediately at the end of the script? I thought of creating a silently read-only property on window before loading the script, but this throws a TypeError: Identifier 'bad_function' has already been declared exception when the above script is executed:

Object.defineProperty(window, 'bad_function', {
    value: function() {
        /* my monkey-patched code */
    }
});

How do I monkey-patch this function?

Upvotes: 3

Views: 1253

Answers (2)

Blender
Blender

Reputation: 298226

While this isn't an answer to my question in general, I was able to monkey patch my specific function by patching a global function like encodeURIComponent that was called inside of my function, performing the necessary changes, and throwing an exception to prevent the rest of the original function from running.

var old_encodeURIComponent = window.encodeURIComponent;

window.encodeURIComponent = function() {
    // If this function is used in multiple places, look for variables in
    // the current scope to determine if this is the right spot to start 
    // monkey patching.
    if (typeof a === 'undefined' || typeof b === 'undefined') {
        return old_encodeURIComponent.apply(this, arguments);
    }

    // You now have access to the variables in the scope of your target 
    // function. If your monkey patching is just a simple tweak, you're all set

    // Otherwise, do what you need to do and throw an exception to stop the
    // rest of the code from running

    throw 'goodbye';
};

Upvotes: 1

Rhumborl
Rhumborl

Reputation: 16609

It's not nice, but all I can think to do is to load the script via ajax instead of putting it in a <script> tag, manipulate the result string to call your own version of the function (or "rename" bad_function so it doesn't override your version), then put that in a <script> tag and append it to your page:

An example using jQuery for simplicity:

function good_function() {
    alert("I am good");
}

$.ajax({
    url: '/echo/html/',
    type: 'POST',
    data: {
        html: "function bad_function() { alert('hahaha'); } bad_function();",
        delay: 0
    },
    success: function(data) {
        console.log(data);

        // comment this line to see the original
        data = data.replace('bad_function();', 'good_function();')

        var newScript = $('<script type="text/javascript" />').text(data);
        $('body').append(newScript);
    }
});

Working JSFiddle

Upvotes: 0

Related Questions