31

I have a function e.g.

var test = function () {alert(1);}

How can I get the body of this function?

I assume that the only way is to parse the result of test.toString() method, but is there any other way? If parsing is the only way, what will be the regex to get to body? (help with the regex is extremly needed, because I am not familiar with them)

kalan
  • 1,752
  • 4
  • 20
  • 35
  • 2
    How does `test.toString()` differ from what you need? Can you show an example? – Pekka Jul 05 '10 at 13:34
  • Pekka, test.toString() returns: function () {alert(1);} I need to get the body: alert(1); That is why I mentioned regex – kalan Jul 05 '10 at 13:49

8 Answers8

53

IF(!!!) you can get the toString(), then you can simply take the substring from the first indexOf("{") to the lastIndexOf("}"). So, something like this "works" (as seen on ideone.com):

var test = function () {alert(1);}

var entire = test.toString(); // this part may fail!
var body = entire.substring(entire.indexOf("{") + 1, entire.lastIndexOf("}"));

print(body); // "alert(1);"
polygenelubricants
  • 376,812
  • 128
  • 561
  • 623
  • 4
    In theory that works pretty well. Of course, there are tons of pitfalls due to comments containing "{" and "}". E.g.: `/** @param {Object} x */ function(x) { }` – Domi Apr 06 '14 at 09:23
  • 1
    @Domi Maybe I don't get something, but why would the comments *before* the function declaration be part of the `toString` output? – Zoltán Tamási Sep 08 '16 at 08:18
  • 1
    @ZoltánTamási I just tried it, and you are right. It doesn't do it in Chrome right now. I thought that it would make sense if the function's string would include it's meta-data (noting that comments and other metadata, such as symbol attributes (if ECMAscript will ever add that sort of stuff) usually go in front of the symbol's declaration. There is nothing in the specification that disallows this either). However MDN [proves me wrong](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/toString). According to their specs, your method seems safe and sound! – Domi Sep 08 '16 at 09:14
  • 1
    Please note that you have to be careful with the current method. It doesn't support ES6 oddities like `var testFn=(q)=>q+1;`(I submitted a code edit for that). – Pat Mächler Aug 31 '17 at 14:24
  • 2
    It also breaks for default arguments like `function foo(bar = () => {}) {}`. – Thomas Apr 19 '20 at 06:23
  • Now that parameter destructuring has become a very common thing this approach is no longer a good idea. – connexo May 15 '22 at 18:42
12

2015 update

Upon revisiting the state of function decompilation, it can said that it's generally safe in certain well-considered use cases and enviroments (e.g: Node.js workers with user defined functions).

It should be put in the same bucket as eval, which is a powerful tool that has its place, but should only be used on rare occasions. Think twice, that's my only advice.

The conclusions from Kangax's new research:

  • It's still not standard
  • User-defined functions are generally looking sane
  • There are oddball engines (especially when it comes to source code placement, whitespaces, comments, dead code)
  • There might be future oddball engines (particularly mobile or unusual devices with conservative memory/power consumption)
  • Bound functions don't show their original source (but do preserve identifier... sometimes)
  • You could run into non-standard extensions (like Mozilla's expression closures)
  • ES6 is coming, and functions can now look very different than they used to
  • Minifiers/preprocessors are not your friend

"function decompilation" — a process of getting string representation of a Function object.

Function decompilation is generally recommended against, as it is a non-standard part of the language, and as a result, leads to code being non-interoperable and potentially error-prone.

@kangax on comp.lang.javascript

Community
  • 1
  • 1
gblazex
  • 49,155
  • 12
  • 98
  • 91
  • Though it is not recommended, I think this can be useful when you want to display the code that is being ran (e.g. for a javascript tutorial) – Gad Jul 05 '10 at 13:55
  • 2
    the ones who ask on SO are generally not the ones who write tutorials. Depending on non-standard features **in production use** will result in hard-to-debug code aka kiss-of-death. – gblazex Jul 05 '10 at 14:08
  • 1
    I down-voted this for not being an actual answer to the question, and then immediately up-voted it for being excellent advice. – Sandy Gifford Jun 03 '15 at 15:50
  • Lies! eval should be used everywhere and remorselessly. It is the most powerful tool JavaScript offers. It is the best way to transform a string into a real object at runtime, and it allows you to extend the language itself from within the language. Feel like writing Smalltalk or Perl in a browser with zero runtime overhead by doing a runtime compilation, go for it! All glory to the eval! – Dmytro Oct 22 '16 at 13:56
6

Simplest Use-Case

If you just want to execute the body of the function (e.g. with eval or using the Worker API), you can simply add some code to circumvent all the pitfalls of extracting the body of the function (which, as mentioned by others, is a bad idea in general):

'(' + myFunction + ')()';

I am using this trick in this Worker-related JSFiddle.

Complete Function Serialization With Accurate Stacktrace

I also wrote a more complete library that can:

  1. Serialize any kind of function to string
  2. Be able to send that string representation anywhere else, execute it with any custom arguments, and be able to reproduce the original stacktrace

Check out my CodeBuilder code here.

Note that much of the code takes care of making sure that we get an accurate stacktrace, wherever we execute the serialized function at a later point in time.

This fiddle demonstrates a simplified version of that logic:

  1. Use JSON.stringify to properly serialize the function (that comes in handy when, e.g., we want to make it part of a bigger serialization "data package").
  2. We then wrap it in one eval to un-escape the "JSON-ish"-escaped string (JSON does not allow functions + code, so we must use eval), and then in another eval to get back the object we wanted.
  3. We also use //# sourceMappingURL (or the old version //@ sourceMappingURL) to show the right function name in the stacktrace.
  4. You will find that the Stacktrace looks Ok, but it does not give you the correct row and column information relative to the file that we defined the serialized functions in, which is why my Codebuilder makes use of stacktracejs to fix that.

I use the CodeBuilder stuff in my (now slightly dated) RPC library where you can find some examples of how it is used:

  1. serializeInlineFunction example
  2. serializeFunction example
  3. buildFunctionCall example
Domi
  • 22,151
  • 15
  • 92
  • 122
3

extending @polygenelubricants' answer:

using: .toString()

Testee:

var y = /* olo{lo} */
    /* {alala} */function/* {ff} */ x/*{s}ls{
}ls*/(/*{*{*/)/* {ha-ha-ha} */
/*

it's a function

*/
{
  return 'x';
// }
}
/*
*/

By indexOf and lastIndexOf:

function getFunctionBody(fn) {
    function removeCommentsFromSource(str) {
        return str.replace(/(?:\/\*(?:[\s\S]*?)\*\/)|(?:([\s;])+\/\/(?:.*)$)/gm, '$1');
    }
    var s = removeCommentsFromSource( fn.toString() );
    return s.substring(s.indexOf('{')+1, s.lastIndexOf('}'));
};

getFunctionBody(y);
/*
"
  return 'x' 
"
*/

used: rm comments from js source

Community
  • 1
  • 1
Ivan Black
  • 4,827
  • 1
  • 35
  • 33
1

This code provides the body when using ES6 arrow functions like var testFn=(q)=>q+1;

function getFunctionBody(test){  
    var entire = test.toString(); // note: not safe-guarded; this part may fail like this!
    return entire.substring((entire.indexOf("{")+1)||(entire.indexOf("=>")+2),  entire.lastIndexOf("}")!==-1?entire.lastIndexOf("}"):entire.length);
}

//testing/showcase code
var tests = [
    function () {alert(1);},
    ()=>{return 1;},
    q=>q+1
];

for (var i=0;i<tests.length;i++){
    console.log(tests[i],getFunctionBody(tests[i]));
}

I originally submitted this code as an edit to polygenelubricants accepted answer, but it was rejected as the changes were considered to be too drastic.

Pat Mächler
  • 529
  • 4
  • 14
0
var fn1 = function() {};
var fn2 = function() { alert("lol!"); };

Function.prototype.empty = function() {
var x = this.toString().match(/\s*function\s*\w*\s*\(.*?\)\s*{\s*}\s*;?\s*/);
return x != null;
};

alert(fn1.empty()); // true
alert(fn2.empty()); // false

' Solução proposta pelo Paulo Torres no grupo A.P.D.A. no facebook.

0

you can try this functiion:

function extractFunctionBody(fn) {
    var reg = /function \((.*)\)[ ]?{(.*)}$/g;
    var match = reg.exec(fn.toString().replace(/\n/g, ";"));
    if (match){
        return match[2];
    } else {
        return "";
    }
}
Yuchen Huang
  • 281
  • 3
  • 10
-1

Try this:

/\{(\s*?.*?)*?\}/g.exec(test.toString())[0]

test.toString() will hold your entire declaration.

/{(\s*?.?)?}/g will match everything between your braces

Gad
  • 41,526
  • 13
  • 54
  • 78
  • 2
    Non-greedy is exactly what you *don't* want here (since there may be nested blocks). If you were going down this route, you'd do `/\{.*\}/s` or `\{(?s:.*)\}` - but simpler to just strip based on `{` and `}` as in answer by polygenelubricants. – Peter Boughton Jul 05 '10 at 13:59