views:

245

answers:

7

Hi all, I've got a weird question in that I need to inject some javascript into another javascript function. I am using a framework which is locked so I can not change the existing function.

What I've got is something like this

function doSomething(){ ... }

...*

I can manipulate the *(above) however I can not change the doSomething function... Instead I need to somehow inject a few lines of code into the end of the doSomething code.

The reason I need to do this is that the custom framework calls doSomething() and this results in an ID being returned from the server that I need to extract. This ID is only referenced inside the doSomething function so I can not catch it unless I inject code to that function (unless I've missed something).

Is there a way to do this?

A: 

Functions are first class values in Javascript, which means that you can store them in variables (and in fact, declaring a named function is essentially assigning an anonymous function to a variable).

You should be able to do something like this:

function doSomething() { ... }

var oldVersionOfFunc = doSomething;
doSomething = function() {

    var retVal = oldVersionOfFunc.apply(oldVersionOfFunc, args);

    // your added code here

    return retVal;
}

(But, I could be wrong. My javascript's a little rusty. ;))

Amber
If I got the question right, that won't help here, since the problem is to extract the value of a local variable that's only referenced inside `doSomething`. And the new version of `doSomething` can't reference variables inside the old one.
Pär Wieslander
+1  A: 

Alias it.

var oldDoSomething = doSomething;
doSomething = function() {
  // Do what you need to do.
  return oldDoSomething.apply(oldDoSomething, arguments);
}
August Lilleaas
he needs access to a variable that is scoped to the original function
Matt
A: 

I can not change the doSomething function... Instead I need to somehow inject a few lines of code into the end of the doSomething code

Injecting a few lines into the end of the doSomething code sounds like changing the doSomething function, to me. I think, unfortunately, that you’re screwed.

(I’m a bit hazy on all this, but I think functions are how people who worry about such things implement information hiding in JavaScript, precisely because you can’t access their scope from outside them.)

Paul D. Waite
A: 
function doSomething() { ... }
var oldVersionOfFunc = doSomething;
doSomething = function() {
    var retVal = oldVersionOfFunc.apply(oldVersionOfFunc, args);
    // your added code here
    return retVal;
}

This seems to provoke an error (Too much recursion)

function doSomething(){ document.write('Test'); return 45; } 
var code = 'myDoSomething = ' + doSomething + '; function doSomething() { var id = myDoSomething(); document.write("Test 2"); return id; }';
eval(code)
doSomething();

This works for me, even if it is ugly.

Anonymous
+1  A: 

I'm not sure what you mean by "locked" - JavaScript is interpreted by your browser. Even if it has been minified or encoded, you can still copy the source of that function from source of the page of script. At that point, you reassign the original function to one of your own design, which contains the copied code plus your own.

var id;
var myFunction = function() {
  // code copied from their source, which contains the variable needed

  // insert  your own code, such as for copying the needed value
  id = theirIDvalue;

  // return the value as originally designed by the function
  return theirValue;
};

theirObject.theirFunction = myFunction;
Matt
+2  A: 

Thanks for all your feedback. Each answer gave me a clue and as such I've come up with the following solution.

<body>
<script type="text/javascript">
    var val;
    function doSomething(item1, item2) {
        var id = 3;
    }
    function merge() {
        var oScript = document.createElement("script");
        var oldDoSomething = doSomething.toString();
        oScript.language = "javascript";
        oScript.type = "text/javascript";
        var args = oldDoSomething.substring(oldDoSomething.indexOf("(") + 1, oldDoSomething.indexOf(")"));
        var scr = oldDoSomething.substring(oldDoSomething.indexOf("{") + 1, oldDoSomething.lastIndexOf("}") - 1);
        var newScript = "function doSomething(" + args + "){" + scr + " val = id; }";
        oScript.text = newScript;
        document.getElementsByTagName('BODY').item(0).appendChild(oScript);
    }

    merge();

</script>
<input type="button" onclick="doSomething();alert(val);" value="xx" />

Grunt
+1  A: 

Altering the function by working with the source code strings can be quite simple. To do it for a specific instance, try:

eval(doSomething.toString().replace(/}\s*$/, ' return id; $&');

Now doSomething returns the ID. I'm not normally a fan of eval, but normal aspect oriented programming techniques don't apply here, due to the requirement for accessing a local variable.

If doSomething already returns a value, try:

eval(doSomething.toString().replace(/}\s*$/, ' window.someID = id; $&');

To turn this into a function, we need to make the code evaluate in global scope:

function insertCode(func, replacer, pattern) {
    var newFunc = func.toString().replace(pattern, replacer);
    with (window) {
        eval(newFunc);
    }
}
function after(func, code) {
    return insertCode(func, function (old) { code + old }, /}\s*$/ );
}
...
after(doSomething, 'return id;');

If you want to rewrite methods and anonymous functions bound to variables, change insertCodeBefore to:

function insertCode(funcName, replacer, pattern) {
    var newFunc = eval('window.' + funcName).toString().replace(pattern, replacer);
    eval('window.' + funcName + '=' + newFunc);
}
...
function Foo() {}
Foo.prototype.bar = function () { var secret=0x09F91102; }
...
after('doSomething', 'return id;');
after('Foo.prototype.bar', 'return secret;');

Note the first argument to the functions are now strings. We can define other functions:

function before(func, code) {
    return insertCode(func, function (old) {old + code}, /^function\s+\w+\([^)]*\)\s+{/);
}
function around(func, beforeCode, afterCode) {
    before(func, beforeCode);
    after(func, afterCode);
}
outis