6

I've tried looking at global, but it only contains variables, not functions. How can I list all the functions created in my script?

4
  • what would be the use case for this? Commented May 5, 2012 at 20:34
  • 1
    A custom AOP script that I'm creating for a node app. I want to use it for things like profiling, throttling, and custom security policies. I checked out Dojo but had some early probs just loading it into my node app so I thought I'd write a custom script. It shouldn't be that hard. Commented May 5, 2012 at 21:12
  • So you want to do this from within the script itself? I'm not sure it's so easy because you can have anonymous functions, functions inside closures, functions dynamically created etc. Commented May 5, 2012 at 21:18
  • True - but in my case applying aspects to only 'root level' named functions will surfice. Commented May 5, 2012 at 21:27

6 Answers 6

5

Run node debug from command line with the file you want to look at. Then you can use list(some big number here)

node debug mini_file_server.js 
< debugger listening on port 5858
connecting... ok
debug> scripts
  26: mini_file_server.js
debug> list(1000)
  1 var http = require('http'),
  2     util = require('util'),
  3     fs   = require('fs');
  4 
  5 server = http.createServer(function(req, res){  
  6     var stream  = fs.createReadStream('one.html'),
  7         stream2 = fs.createReadStream('two.html');
  8     console.log(stream);
  9     console.log(stream2);
 10     stream.on('end', function(){
 11         stream2.pipe(res, { end:false});
 12     });
 13 
 14     stream2.on('end', function(){
 15         res.end("Thats all!");
 16     });
 17 
 18     res.writeHead(200, {'Content-Type' : 'text/plain'});
 19     stream.pipe(res, { end:false});
 20     stream2.pipe(res, { end:true});
 21 
 22 }).listen(8001);
 23 });
debug> 
Sign up to request clarification or add additional context in comments.

Comments

4

If the function has a name, it'll show up in global just fine:

mb-work-laptop:~ markbessey$ node
> for (var k in global) { console.log(k); }
global
process
GLOBAL
root
Buffer
setTimeout
setInterval
clearTimeout
clearInterval
console
module
require
k
> function z(a) { return a*10; }
> for (var k in global) { console.log(k); }
global
process
GLOBAL
root
Buffer
setTimeout
setInterval
clearTimeout
clearInterval
console
module
require
k
z
> 
> global.z
[Function: z]

3 Comments

I could have sworn I'd tested for this and found that functions weren't available in global, but your script works fine for me too.
Sorry Mark I've demarked this as answer - it works fine in the console, but for the script function a(){ return 1; } for(var k in global) console.log(k) it does not show the function 'a'.
Right. As it turns out, in the interactive mode, those definitions go into global, but if you run a script from "node script.js", they end up as locals in the module. There's definitely a way to get at that info from the debugger, but not sure if the script can access it...
3

This is impossible in node without more advanced reflecting tools like the debugger.

The only way to do this would be to use __parent__ which was removed due to security issues and other things. Like Mark Bessey said, when you run the script those variables become module closure variables. You can not access them elsewhere without explicitly exporting them.

This is not a bug, it's by design. It's just how node works. However, if you just ask your users to write function expression assignments, all would work a-ok:

module.exports = {
    a:function(){
        //same logic you had in the function declaration
    }
}

Then, you can easily reflect on and enumerate module.exports and get all the function names.

Comments

1

cli: http://nodejs.org/docs/v0.3.7/api/debugger.html

gui: https://github.com/dannycoates/node-inspector

There's also https://github.com/c4milo/node-webkit-agent in the works which will be a more powerful version of node-inspector.

Comments

1

If you want to do some AOP, the route is AST.

You could build your own AOP framework with something like: http://esprima.org.

Or you could try node-burrito, excellent for not so complex aspects:

var burrito = require('burrito');

var src = burrito('someCall()', function (node) {
    if (node.name === 'call') node.wrap('qqq(%s)');
});

will generate

qqq(somecall())

Comments

1

Wanted the exact thing you're looking for. Couldn't find any other solutions (most assumed use of "window" or "this" or "global", none of which will work for us in Node).

We can do it in about 25 lines with the help of a few libraries: fs, esprima, and escodegen.

const fs = require('fs');
const esprima = require("esprima");
const escodegen = require("escodegen");

The logic is this:

  1. Let's take the file this function is in, and first read it as text, just like any other file we would read as plain text
  2. We'll use esprima to parse that text into a valid tree, part of which will contain our functions
  3. We'll filter out that tree to only contain functions (except for this function we're using to do this! We're not after it)
  4. For that, we'll need to grab the easy function declarations
  5. But ideally we can also grab any functions that were declared as variables with arrow expressions, which will take a little more work but is very doable.
  6. Next we want to reconstruct these objects from the tree back into actual usable functions in the code, so for all of our functions:
  7. Use escodegen to reconstruct that object into a string that looks just like written code for that function
  8. Convert that string into a usable function within the script itself again

The end result will spit back out an object with the key:value pairs where the key is the string of the function's name, and the value is the function itself.

function getAllFunctionsInThisFileExceptThisFunction() {
    const thisFunctionName = arguments.callee.name;
    const rawTextFromThisFile = fs.readFileSync(__filename, "utf8");
    const parsed = esprima.parseScript(rawTextFromThisFile);
    const allDeclaredVariables = parsed.body.filter(e=>e.type === "VariableDeclaration");
    const allDeclaredFunctions = parsed.body.filter(e=>e.type === "FunctionDeclaration");
   
    let allFunctions = []
    for (declaredVariable of allDeclaredVariables){ 
        const declarations = declaredVariable.declarations[0];
        if (declarations.init.type === "ArrowFunctionExpression"){ 
            const anonymousFunction = declarations.init;
            let reconstructedFunction = anonymousFunction;
            reconstructedFunction.id = declarations.id;
            allFunctions.push(reconstructedFunction);
        }
    }
    allFunctions.push(...allDeclaredFunctions)

    const allFunctionsExceptThisOne = allFunctions.filter(e => e.id.name !== thisFunctionName);
    let functionsDict = {};
    for (parsedFunction of allFunctionsExceptThisOne) {
        const functionString = escodegen.generate(parsedFunction);
        const newFunction = eval(`(${functionString})`)
        functionsDict[parsedFunction.id.name] = newFunction;
    }
    return functionsDict;
}

From there you can play away with it like any other object/dictionary.

const allFunctionsDict = getAllFunctionsInThisFileExceptThisFunction();
console.log( allFunctionsDict["sum"](10,30) ) //prints 40
console.log( allFunctionsDict["difference"](350,250) ) //prints 100
console.log( allFunctionsDict["product"](6,4) ) // prints 24
console.log( Object.keys(allFunctionsDict) ) //prints [ 'product', 'sum', 'difference' ]

function sum(a, b) {
    return a + b;
}

function difference(a, b) {
    return a - b;
}

const product = (a,b) => { 
    return a * b;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.