Skip to content

Commit

Permalink
lib: add util.getCallSite() API
Browse files Browse the repository at this point in the history
  • Loading branch information
RafaelGSS committed Aug 14, 2024
1 parent 9b3d22d commit 201e50e
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 0 deletions.
50 changes: 50 additions & 0 deletions doc/api/util.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,56 @@ util.formatWithOptions({ colors: true }, 'See object %O', { foo: 42 });
// when printed to a terminal.
```

## `util.getCallSite()`

> Stability: 1.1 - Active development
<!-- YAML
added: REPLACEME
-->

* Returns: {CallSite\[]} An array of CallSite objects, read more at <https://v8.dev/docs/stack-trace-api#customizing-stack-traces>.

Returns an array of V8 CallSite objects containing the stacktrace of
the caller function.

```js
const util = require('node:util');

function exampleFunction() {
const callSites = util.getCallSite();

console.log('Call Sites:');
callSites.forEach((callSite, index) => {
console.log(`CallSite ${index + 1}:`);
console.log(`Function Name: ${callSite.getFunctionName()}`);
console.log(`File Name: ${callSite.getFileName()}`);
console.log(`Line Number: ${callSite.getLineNumber()}`);
console.log(`Column Number: ${callSite.getColumnNumber()}`);
});
// CallSite 1:
// Function Name: exampleFunction
// File Name: /home/example.js
// Line Number: 5
// Column Number: 26

// CallSite 2:
// Function Name: anotherFunction
// File Name: /home/example.js
// Line Number: 22
// Column Number: 3

// ...
}

// A function to simulate another stack layer
function anotherFunction() {
exampleFunction();
}

anotherFunction();
```

## `util.getSystemErrorName(err)`

<!-- YAML
Expand Down
21 changes: 21 additions & 0 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,26 @@ function parseEnv(content) {
return binding.parseEnv(content);
}

/**
* Returns the callSite
* @returns {CallSite[]}
*/
function getCallSite() { /* eslint-disable no-restricted-syntax */
const originalStackFormatter = Error.prepareStackTrace;
Error.prepareStackTrace = (_err, stack) => {
if (stack && stack.length > 1) {
// Remove node:util
return stack.slice(1);
}
return stack;
};
const err = new Error();
// With the V8 Error API, the stack is not formatted until it is accessed
err.stack; // eslint-disable-line no-unused-expressions
Error.prepareStackTrace = originalStackFormatter;
return err.stack;
};

// Keep the `exports =` so that various functions can still be monkeypatched
module.exports = {
_errnoException,
Expand All @@ -289,6 +309,7 @@ module.exports = {
format,
styleText,
formatWithOptions,
getCallSite,
getSystemErrorMap,
getSystemErrorName,
inherits,
Expand Down
4 changes: 4 additions & 0 deletions test/fixtures/get-call-site.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const util = require('node:util');
const assert = require('node:assert');
assert.ok(util.getCallSite().length > 1);
process.stdout.write(util.getCallSite()[0].getFileName());
54 changes: 54 additions & 0 deletions test/parallel/test-util-getCallSite.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use strict';

require('../common');

const fixtures = require('../common/fixtures');
const file = fixtures.path('get-call-site.js');

const { getCallSite } = require('node:util');
const { spawnSync } = require('node:child_process');
const assert = require('node:assert');

{
const callsite = getCallSite();
assert.ok(callsite.length > 1);
assert.match(
callsite[0].getFileName(),
/test-util-getCallSite/,
'node:util should be ignored',
);
}


{
const { status, stderr, stdout } = spawnSync(
process.execPath,
[
'-e',
`const util = require('util');
const assert = require('assert');
assert.ok(util.getCallSite().length > 1);
process.stdout.write(util.getCallSite()[0].getFileName());
`,
],
);
assert.strictEqual(status, 0, stderr.toString());
assert.strictEqual(stdout.toString(), '[eval]');
}

{
const { status, stderr, stdout } = spawnSync(
process.execPath,
[file],
);
assert.strictEqual(status, 0, stderr.toString());
assert.strictEqual(stdout.toString(), file);
}

{
const originalStackTraceLimit = Error.stackTraceLimit;
Error.stackTraceLimit = 0;
const callsite = getCallSite();
assert.strictEqual(callsite.length, 0);
Error.stackTraceLimit = originalStackTraceLimit;
}

0 comments on commit 201e50e

Please sign in to comment.