Skip to content

Commit c64a73b

Browse files
TimothyGuMylesBorins
authored andcommitted
promises: more robust stringification
Backport-PR-URL: #17833 PR-URL: #13784 Fixes: #13771 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Rod Vagg <rod@vagg.org> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 24def19 commit c64a73b

File tree

3 files changed

+49
-4
lines changed

3 files changed

+49
-4
lines changed

lib/internal/process/promises.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22

3+
const { safeToString } = process.binding('util');
4+
35
const promiseRejectEvent = process._promiseRejectEvent;
46
const hasBeenNotifiedProperty = new WeakMap();
57
const promiseToGuidProperty = new WeakMap();
@@ -64,12 +66,17 @@ function setupPromises(scheduleMicrotasks) {
6466
hasBeenNotifiedProperty.set(promise, true);
6567
const uid = promiseToGuidProperty.get(promise);
6668
if (!process.emit('unhandledRejection', reason, promise)) {
67-
const warning = new Error('Unhandled promise rejection ' +
68-
`(rejection id: ${uid}): ${reason}`);
69+
const warning = new Error(
70+
`Unhandled promise rejection (rejection id: ${uid}): ` +
71+
safeToString(reason));
6972
warning.name = 'UnhandledPromiseRejectionWarning';
7073
warning.id = uid;
71-
if (reason instanceof Error) {
72-
warning.stack = reason.stack;
74+
try {
75+
if (reason instanceof Error) {
76+
warning.stack = reason.stack;
77+
}
78+
} catch (err) {
79+
// ignored
7380
}
7481
process.emitWarning(warning);
7582
} else {

src/node_util.cc

+7
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ static void GetProxyDetails(const FunctionCallbackInfo<Value>& args) {
5454
args.GetReturnValue().Set(ret);
5555
}
5656

57+
// Side effect-free stringification that will never throw exceptions.
58+
static void SafeToString(const FunctionCallbackInfo<Value>& args) {
59+
auto context = args.GetIsolate()->GetCurrentContext();
60+
args.GetReturnValue().Set(args[0]->ToDetailString(context).ToLocalChecked());
61+
}
62+
5763
static void GetHiddenValue(const FunctionCallbackInfo<Value>& args) {
5864
Environment* env = Environment::GetCurrent(args);
5965

@@ -122,6 +128,7 @@ void Initialize(Local<Object> target,
122128
env->SetMethod(target, "getHiddenValue", GetHiddenValue);
123129
env->SetMethod(target, "setHiddenValue", SetHiddenValue);
124130
env->SetMethod(target, "getProxyDetails", GetProxyDetails);
131+
env->SetMethod(target, "safeToString", SafeToString);
125132

126133
env->SetMethod(target, "startSigintWatchdog", StartSigintWatchdog);
127134
env->SetMethod(target, "stopSigintWatchdog", StopSigintWatchdog);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
const common = require('../common');
3+
4+
const expectedPromiseWarning = 'Unhandled promise rejection (rejection id: ' +
5+
'1): [object Object]';
6+
7+
function throwErr() {
8+
throw new Error('Error from proxy');
9+
}
10+
11+
const thorny = new Proxy({}, {
12+
getPrototypeOf: throwErr,
13+
setPrototypeOf: throwErr,
14+
isExtensible: throwErr,
15+
preventExtensions: throwErr,
16+
getOwnPropertyDescriptor: throwErr,
17+
defineProperty: throwErr,
18+
has: throwErr,
19+
get: throwErr,
20+
set: throwErr,
21+
deleteProperty: throwErr,
22+
ownKeys: throwErr,
23+
apply: throwErr,
24+
construct: throwErr
25+
});
26+
27+
common.expectWarning('UnhandledPromiseRejectionWarning',
28+
expectedPromiseWarning);
29+
30+
// ensure this doesn't crash
31+
Promise.reject(thorny);

0 commit comments

Comments
 (0)