Commit cbac135e5aa for nodejs
commit cbac135e5aa56af5eb50793dff550d0b27d1fda8
Author: Matteo Collina <hello@matteocollina.com>
Date: Sun Oct 11 09:07:49 2026 +0200
diagnostics_channel: fix default callback context
traceCallback() defaults to a frozen context but writes the callback's
result or error to it. Allocate a fresh context when undefined is passed
and subscribers are present, matching traceSync() and tracePromise().
Set the prototype after allocation to avoid dictionary-mode properties.
Assisted-by: pi
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/66481
Reviewed-By: Bryan English <bryan@bryanenglish.com>
diff --git a/lib/diagnostics_channel.js b/lib/diagnostics_channel.js
index dcf55010f90..bb0c7d66050 100644
--- a/lib/diagnostics_channel.js
+++ b/lib/diagnostics_channel.js
@@ -647,11 +647,15 @@ class TracingChannel {
}
}
- traceCallback(fn, position = -1, context = kEmptyObject, thisArg, ...args) {
+ traceCallback(fn, position = -1, context = undefined, thisArg, ...args) {
if (!this.hasSubscribers) {
return ReflectApply(fn, thisArg, args);
}
+ if (context === undefined) {
+ context = ObjectSetPrototypeOf({}, null);
+ }
+
const { error } = this;
const continuationWindow = this.#continuationWindow;
diff --git a/test/parallel/test-diagnostics-channel-tracing-channel-callback-default-context.js b/test/parallel/test-diagnostics-channel-tracing-channel-callback-default-context.js
new file mode 100644
index 00000000000..ae332a079e1
--- /dev/null
+++ b/test/parallel/test-diagnostics-channel-tracing-channel-callback-default-context.js
@@ -0,0 +1,56 @@
+'use strict';
+
+const common = require('../common');
+const assert = require('assert');
+const dc = require('diagnostics_channel');
+
+for (const expectedError of [null, new Error('test')]) {
+ const channel = dc.tracingChannel(`test-${expectedError ? 'error' : 'result'}`);
+ const contexts = [];
+ const results = [{ value: 1 }, { value: 2 }];
+ let completed = 0;
+
+ channel.subscribe({
+ start: common.mustCall((context) => {
+ assert.strictEqual(context.result, undefined);
+ assert.strictEqual(context.error, undefined);
+ for (const previous of contexts) {
+ assert.notStrictEqual(context, previous);
+ }
+ contexts.push(context);
+ }, 2),
+ asyncStart: common.mustCall((context) => {
+ assert.strictEqual(context, contexts[completed]);
+ if (expectedError) {
+ assert.strictEqual(context.error, expectedError);
+ assert.strictEqual(context.result, undefined);
+ } else {
+ assert.strictEqual(context.result, results[completed]);
+ assert.strictEqual(context.error, undefined);
+ }
+ completed++;
+ }, 2),
+ asyncEnd: common.mustCall(2),
+ error: expectedError ? common.mustCall((context) => {
+ assert.strictEqual(context, contexts[completed]);
+ assert.strictEqual(context.error, expectedError);
+ }, 2) : common.mustNotCall(),
+ });
+
+ for (const result of results) {
+ channel.traceCallback(common.mustCall((callback) => {
+ setImmediate(callback, expectedError, expectedError ? undefined : result);
+ }), undefined, undefined, undefined, common.mustCall((err, value) => {
+ assert.strictEqual(err, expectedError);
+ assert.strictEqual(value, expectedError ? undefined : result);
+ }));
+ }
+
+ setImmediate(common.mustCall(() => {
+ assert.strictEqual(completed, 2);
+ for (const [index, context] of contexts.entries()) {
+ assert.strictEqual(context.error, expectedError || undefined);
+ assert.strictEqual(context.result, expectedError ? undefined : results[index]);
+ }
+ }));
+}