Skip to content

Commit cbe0cca

Browse files
authored
update util.getCallSites() implementation to follow node.js (#3381)
1 parent a4685b9 commit cbe0cca

5 files changed

Lines changed: 39 additions & 15 deletions

File tree

src/node/internal/util.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,6 @@ export function isBoxedPrimitive(
121121
): value is number | string | boolean | bigint | symbol;
122122

123123
export function getBuiltinModule(id: string): any;
124-
export function getCallSite(frames: number): Record<string, string>[];
124+
export function getCallSites(frames?: number): Record<string, string>[];
125125
export function processExitImpl(code: number): void;
126126
export const processPlatform: string;

src/node/util.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,12 @@ export function deprecate(
206206
return fn;
207207
}
208208

209-
export function getCallSite(frames: number = 10) {
210-
return utilImpl.getCallSite(frames);
211-
}
209+
// Node.js originally introduced the API with the name `getCallSite()` as an experimental
210+
// API but then renamed it to `getCallSites()` soon after. We had already implemented the
211+
// API with the original name in a release. To avoid the possibility of breaking, we export
212+
// the function using both names.
213+
export const getCallSite = utilImpl.getCallSites.bind(utilImpl);
214+
export const getCallSites = utilImpl.getCallSites.bind(utilImpl);
212215

213216
export function isDeepStrictEqual(a: unknown, b: unknown): boolean {
214217
return _isDeepStrictEqual(a, b);
@@ -247,6 +250,7 @@ export default {
247250
transferableAbortController,
248251
transferableAbortSignal,
249252
getCallSite,
253+
getCallSites,
250254
isDeepStrictEqual,
251255
isArray,
252256
};

src/workerd/api/node/tests/util-nodejs-test.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4343,15 +4343,23 @@ export const debuglog = {
43434343
},
43444344
};
43454345

4346-
export const getCallSiteTest = {
4346+
export const getCallSitesTest = {
43474347
test() {
4348-
const callSites = util.getCallSite();
4348+
const callSites = util.getCallSites();
43494349
assert.strictEqual(callSites.length, 1);
43504350
const [stack] = callSites;
43514351
assert.strictEqual(stack.functionName, 'test');
43524352
assert.strictEqual(stack.scriptName, 'worker');
43534353
assert.strictEqual(typeof stack.lineNumber, 'number');
4354+
assert.strictEqual(typeof stack.columnNumber, 'number');
43544355
assert.strictEqual(typeof stack.column, 'number');
4356+
4357+
// We leave this implementation for compat reasons.
4358+
// Node.js originally introduced the API with the name `getCallSite()` as an experimental
4359+
// API but then renamed it to `getCallSites()` soon after. We had already implemented the
4360+
// API with the original name in a release. To avoid the possibility of breaking, we export
4361+
// the function using both names.
4362+
assert.strictEqual(typeof util.getCallSite, 'function');
43554363
},
43564364
};
43574365

src/workerd/api/node/util.c++

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -177,22 +177,29 @@ jsg::Name UtilModule::getResourceTypeInspect(jsg::Lock& js) {
177177
return js.newApiSymbol("kResourceTypeInspect"_kj);
178178
}
179179

180-
kj::Array<UtilModule::CallSiteEntry> UtilModule::getCallSite(jsg::Lock& js, int frames) {
181-
JSG_REQUIRE(
182-
frames >= 1 && frames <= 200, Error, "Frame count should be between 1 and 200 inclusive."_kj);
183-
auto stack = v8::StackTrace::CurrentStackTrace(js.v8Isolate, frames + 1);
180+
kj::Array<UtilModule::CallSiteEntry> UtilModule::getCallSites(
181+
jsg::Lock& js, jsg::Optional<int> frames) {
182+
KJ_IF_SOME(f, frames) {
183+
JSG_REQUIRE(f >= 1 && f <= 200, Error, "Frame count should be between 1 and 200 inclusive."_kj);
184+
}
185+
186+
auto stack = v8::StackTrace::CurrentStackTrace(js.v8Isolate, frames.orDefault(10) + 1);
184187
const int frameCount = stack->GetFrameCount();
185188
auto objects = kj::Vector<CallSiteEntry>();
186189
objects.reserve(frameCount - 1);
187190

188-
// Frame 0 is node:util. It should be skipped.
189-
for (int i = 1; i < frameCount; ++i) {
191+
for (int i = 0; i < frameCount; ++i) {
190192
auto stack_frame = stack->GetFrame(js.v8Isolate, i);
191193

192194
objects.add(CallSiteEntry{
193195
.functionName = js.toString(stack_frame->GetFunctionName()),
194196
.scriptName = js.toString(stack_frame->GetScriptName()),
195197
.lineNumber = stack_frame->GetLineNumber(),
198+
// Node.js originally implemented the experimental API using the "column" field
199+
// then later renamed it to columnNumber. We had already implemented the API
200+
// using column. To ensure backwards compat without the complexity of a compat
201+
// flag, we just export both.
202+
.columnNumber = stack_frame->GetColumn(),
196203
.column = stack_frame->GetColumn(),
197204
});
198205
}

src/workerd/api/node/util.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,16 @@ class UtilModule final: public jsg::Object {
212212
kj::String functionName;
213213
kj::String scriptName;
214214
int lineNumber;
215+
// Node.js originally introduced the API with the name `getCallSite()` as an experimental
216+
// API but then renamed it to `getCallSites()` soon after. We had already implemented the
217+
// API with the original name in a release. To avoid the possibility of breaking, we export
218+
// the function using both names.
219+
int columnNumber;
215220
int column;
216221

217-
JSG_STRUCT(functionName, scriptName, lineNumber, column);
222+
JSG_STRUCT(functionName, scriptName, lineNumber, columnNumber, column);
218223
};
219-
kj::Array<CallSiteEntry> getCallSite(jsg::Lock& js, int frames);
224+
kj::Array<CallSiteEntry> getCallSites(jsg::Lock& js, jsg::Optional<int> frames);
220225

221226
#define V(Type) bool is##Type(jsg::JsValue value);
222227
JS_UTIL_IS_TYPES(V)
@@ -257,7 +262,7 @@ class UtilModule final: public jsg::Object {
257262
JSG_METHOD(getProxyDetails);
258263
JSG_METHOD(previewEntries);
259264
JSG_METHOD(getConstructorName);
260-
JSG_METHOD(getCallSite);
265+
JSG_METHOD(getCallSites);
261266

262267
#define V(Type) JSG_METHOD(is##Type);
263268
JS_UTIL_IS_TYPES(V)

0 commit comments

Comments
 (0)