From 40a657b330d2c09c1fbf9f73cf7d340c49204f2a Mon Sep 17 00:00:00 2001 From: LiviaMedeiros Date: Fri, 18 Apr 2025 01:14:24 +0800 Subject: [PATCH] util: add internal `assignFunctionName()` function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/57916 Reviewed-By: Antoine du Hamel Reviewed-By: Vinícius Lourenço Claro Cardoso Reviewed-By: James M Snell --- lib/internal/util.js | 28 ++++++++++++++++++++ test/parallel/test-internal-util-helpers.js | 29 ++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/lib/internal/util.js b/lib/internal/util.js index 6177b151fee..98dc3f81201 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -41,6 +41,7 @@ const { StringPrototypeToUpperCase, Symbol, SymbolFor, + SymbolPrototypeGetDescription, SymbolReplace, SymbolSplit, } = primordials; @@ -906,10 +907,37 @@ const encodingsMap = { __proto__: null }; for (let i = 0; i < encodings.length; ++i) encodingsMap[encodings[i]] = i; +/** + * Reassigns the .name property of a function. + * Should be used when function can't be initially defined with desired name + * or when desired name should include `#`, `[`, `]`, etc. + * @param {string | symbol} name + * @param {Function} fn + * @param {object} [descriptor] + * @returns {Function} the same function, renamed + */ +function assignFunctionName(name, fn, descriptor = kEmptyObject) { + if (typeof name !== 'string') { + const symbolDescription = SymbolPrototypeGetDescription(name); + assert(symbolDescription !== undefined, 'Attempted to name function after descriptionless Symbol'); + name = `[${symbolDescription}]`; + } + return ObjectDefineProperty(fn, 'name', { + __proto__: null, + writable: false, + enumerable: false, + configurable: true, + ...ObjectGetOwnPropertyDescriptor(fn, 'name'), + ...descriptor, + value: name, + }); +} + module.exports = { getLazy, assertCrypto, assertTypeScript, + assignFunctionName, cachedResult, convertToValidSignal, createClassWrapper, diff --git a/test/parallel/test-internal-util-helpers.js b/test/parallel/test-internal-util-helpers.js index bf60cff9bda..01e99d52420 100644 --- a/test/parallel/test-internal-util-helpers.js +++ b/test/parallel/test-internal-util-helpers.js @@ -4,7 +4,7 @@ require('../common'); const assert = require('assert'); const { types } = require('util'); -const { isError } = require('internal/util'); +const { assignFunctionName, isError } = require('internal/util'); const vm = require('vm'); // Special cased errors. Test the internal function which is used in @@ -35,3 +35,30 @@ const vm = require('vm'); assert(!(differentRealmErr instanceof Error)); assert(isError(differentRealmErr)); } + +{ + const nameMap = new Map([ + [ 'meaningfulName', 'meaningfulName' ], + [ '', '' ], + [ Symbol.asyncIterator, '[Symbol.asyncIterator]' ], + [ Symbol('notWellKnownSymbol'), '[notWellKnownSymbol]' ], + ]); + for (const fn of [ + () => {}, + function() {}, + function value() {}, + ({ value() {} }).value, + new Function(), + Function(), + function() {}.bind(null), + class {}, + class value {}, + ]) { + for (const [ stringOrSymbol, expectedName ] of nameMap) { + const namedFn = assignFunctionName(stringOrSymbol, fn); + assert.strictEqual(fn, namedFn); + assert.strictEqual(namedFn.name, expectedName); + assert.strictEqual(namedFn.bind(null).name, `bound ${expectedName}`); + } + } +}