diff --git a/lib/util.js b/lib/util.js index cdee29d60d775d..05064553a38f7b 100644 --- a/lib/util.js +++ b/lib/util.js @@ -279,8 +279,10 @@ function parseEnv(content) { * Returns the callSite * @returns {object} */ -function getCallSite() { - return binding.getCallSite(); +function getCallSite(frames = 10) { + // Using kDefaultMaxCallStackSizeToCapture as reference + validateNumber(frames, 'frames', 1, 200); + return binding.getCallSite(frames); }; // Keep the `exports =` so that various functions can still be monkeypatched diff --git a/src/node_util.cc b/src/node_util.cc index f90dc30af97945..301c5ec80c2bb6 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -258,8 +258,12 @@ static void GetCallSite(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); - Local stack = - StackTrace::CurrentStackTrace(isolate, env->stack_trace_limit()); + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsNumber()); + const uint32_t frames = args[0].As()->Value(); + + // +1 for disregarding node:util + Local stack = StackTrace::CurrentStackTrace(isolate, frames + 1); Local callsites = Array::New(isolate); // Frame 0 is node:util. It should be skipped. diff --git a/test/parallel/test-util-getCallSite.js b/test/parallel/test-util-getCallSite.js index 488dacb0a11934..019165f1d344a9 100644 --- a/test/parallel/test-util-getCallSite.js +++ b/test/parallel/test-util-getCallSite.js @@ -1,6 +1,6 @@ 'use strict'; -require('../common'); +const common = require('../common'); const fixtures = require('../common/fixtures'); const file = fixtures.path('get-call-site.js'); @@ -19,6 +19,54 @@ const assert = require('node:assert'); ); } +{ + const callsite = getCallSite(3); + assert.strictEqual(callsite.length, 3); + assert.match( + callsite[0].scriptName, + /test-util-getCallSite/, + 'node:util should be ignored', + ); +} + +{ + const callsite = getCallSite(3.6); + assert.strictEqual(callsite.length, 3); +} + +{ + const callsite = getCallSite(3.4); + assert.strictEqual(callsite.length, 3); +} + +{ + assert.throws(() => { + // Max than kDefaultMaxCallStackSizeToCapture + getCallSite(201); + }, common.expectsError({ + code: 'ERR_OUT_OF_RANGE' + })); + assert.throws(() => { + getCallSite(-1); + }, common.expectsError({ + code: 'ERR_OUT_OF_RANGE' + })); + assert.throws(() => { + getCallSite({}); + }, common.expectsError({ + code: 'ERR_INVALID_ARG_TYPE' + })); +} + +{ + const callsite = getCallSite(1); + assert.strictEqual(callsite.length, 1); + assert.match( + callsite[0].scriptName, + /test-util-getCallSite/, + 'node:util should be ignored', + ); +} { const { status, stderr, stdout } = spawnSync(