Access function location programmatically

I know that it has been a while when this question was posted. Now, I had the same problem. I need to access the function location on the runtime.

Fortunately, NodeJS did a great job by exposing some v8 internal properties through the inspector module.

I have written a small module called func-loc that helps to retrieve a function location on the runtime.

Example:

const { locate } = require('func-loc');

const fn = () => {
  console.log('Hello there');
};

(async () => {
  const result = await locate(fn);
  console.log(result);
  // Will result: { source: 'file://__BASE_FOLDER__/func-loc/this-file.js', line: 3, column: 12 }
})();

Hope that helps.


The answer, for now, is no.

The [[FunctionLocation]] property you see in Inspector is added in V8Debugger::internalProperties() in the debugger's C++ code, which uses another C++ function V8Debugger::functionLocation() to gather information about the function. functionLocation() then uses a number of V8-specific C++ APIs such as v8::Function::GetScriptLineNumber() and GetScriptColumnNumber() to find out the exact information.

All APIs described above are exclusively available to C++ code, not JavaScript code. In other words, JavaScript code on the webpage does not have direct access to this information.


However, you may be able to get access to the properties using a Chrome extension. More recently, the V8 JavaScript engine used by Chrome has added support to access these properties through the Chrome DevTools Protocol. In particular, you can get the internal properties through the Runtime.getProperties call. Additionally, it seems like Chrome extensions may be able to interact with the DevTools protocol through chrome.debugger.

A proof of concept for using the DevTools protocol in Node.js, which has direct access to the protocol using the Inspector built-in module (helpfully mentioned by Mohamed in their answer):

global.a = () => { /* test function */ };

const s = new (require('inspector').Session)();
s.connect();

let objectId;
s.post('Runtime.evaluate', { expression: 'a' }, (err, { result }) => {
  objectId = result.objectId;
});
s.post('Runtime.getProperties', { objectId }, (err, { internalProperties }) => {
  console.log(internalProperties);
});

yields

[
  {
    name: '[[FunctionLocation]]',
    value: {
      type: 'object',
      subtype: 'internal#location',
      value: [Object],
      description: 'Object'
    }
  },
  {
    name: '[[Scopes]]',
    value: {
      type: 'object',
      subtype: 'internal#scopeList',
      className: 'Array',
      description: 'Scopes[2]',
      objectId: '{"injectedScriptId":1,"id":24}'
    }
  }
]

with Node.js v12.3.1.