From 0deef2d2b13f5cd9228d9d46e3f2df681eb94214 Mon Sep 17 00:00:00 2001 From: Chenyu Yang Date: Mon, 18 Mar 2024 14:54:58 +0800 Subject: [PATCH] util: fix `%s` format behavior with `Symbol.toPrimitive` This commit ensures `console.log("%s", obj)` correctly invokes `obj[Symbol.toPrimitive]` for string conversion, fixing unexpected object display issue. PR-URL: https://github.com/nodejs/node/pull/50992 Fixes: https://github.com/nodejs/node/issues/50909 Reviewed-By: Ruben Bridgewater Reviewed-By: Chengzhong Wu Reviewed-By: James M Snell Reviewed-By: Zeyu "Alex" Yang --- lib/internal/util/inspect.js | 6 ++++++ test/parallel/test-util-format.js | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index d6096a5e09cf05..674cb43ab1f01f 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -87,6 +87,7 @@ const { SymbolIterator, SymbolPrototypeToString, SymbolPrototypeValueOf, + SymbolToPrimitive, SymbolToStringTag, TypedArrayPrototypeGetLength, TypedArrayPrototypeGetSymbolToStringTag, @@ -2103,6 +2104,11 @@ function hasBuiltInToString(value) { value = proxyTarget; } + // Check if value has a custom Symbol.toPrimitive transformation. + if (typeof value[SymbolToPrimitive] === 'function') { + return false; + } + // Count objects that have no `toString` function as built-in. if (typeof value.toString !== 'function') { return true; diff --git a/test/parallel/test-util-format.js b/test/parallel/test-util-format.js index 12e7833bb24457..8d2cab5a9c7a1c 100644 --- a/test/parallel/test-util-format.js +++ b/test/parallel/test-util-format.js @@ -269,6 +269,27 @@ assert.strictEqual(util.format('%s', -Infinity), '-Infinity'); ); } +// Symbol.toPrimitive handling for string format specifier +{ + const objectWithToPrimitive = { + [Symbol.toPrimitive](hint) { + switch (hint) { + case 'number': + return 42; + case 'string': + return 'string representation'; + case 'default': + default: + return 'default context'; + } + } + }; + + assert.strictEqual(util.format('%s', +objectWithToPrimitive), '42'); + assert.strictEqual(util.format('%s', objectWithToPrimitive), 'string representation'); + assert.strictEqual(util.format('%s', objectWithToPrimitive + ''), 'default context'); +} + // JSON format specifier assert.strictEqual(util.format('%j'), '%j'); assert.strictEqual(util.format('%j', 42), '42');