32

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)

2
  • 3
    How does test.toString() differ from what you need? Can you show an example? Commented Jul 5, 2010 at 13:34
  • Pekka, test.toString() returns: function () {alert(1);} I need to get the body: alert(1); That is why I mentioned regex Commented Jul 5, 2010 at 13:49

8 Answers 8

51

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);" 
Sign up to request clarification or add additional context in comments.

6 Comments

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 Maybe I don't get something, but why would the comments before the function declaration be part of the toString output?
@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. According to their specs, your method seems safe and sound!
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).
It also breaks for default arguments like function foo(bar = () => {}) {}.
|
12

2015 update

Upon revisiting the state of function decompilation, it can be 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

4 Comments

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)
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.
I down-voted this for not being an actual answer to the question, and then immediately up-voted it for being excellent advice.
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!
7

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

Comments

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

2 Comments

You do realize that neither of your comments are actually part of y, as you're declaring a function, not a string, right?
@DanielSmedegaardBuus, from function to } only.
2

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.

2 Comments

note that it has problem with arrow function returning an object in brackets, e.g. () => ({ status: 200, body: 'hello world' }), it removes the curly braces making the function body invalid
for the previous comment with arrow fn returning object in brackets, I added this condition before the last return if ((entire.replace(/\s+/g, '')).includes('=>({'))) { return entire.match(/(({.*}))/g)[0] } and it's working fine now (note that I removed spaces to do the check just in case the function doesn't have any spaces then it would still work. Now () =>({ status: 200, body: 'hello world' }) will return { status: 200, body: 'hello world' } which is valid, instead of status: 200, body: 'hello world' which is invalid
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.

1 Comment

Thanks for your contribution Siqueira :) But, please post your responses in English.
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 ""; } } 

Comments

-1

Try this:

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

test.toString() will hold your entire declaration.

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

1 Comment

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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.