Skip to content

Commit d139b6d

Browse files
committed
util: add internal assignFunctionName() function
1 parent 0699a99 commit d139b6d

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

lib/internal/util.js

+28
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const {
3838
StringPrototypeToUpperCase,
3939
Symbol,
4040
SymbolFor,
41+
SymbolPrototypeGetDescription,
4142
SymbolReplace,
4243
SymbolSplit,
4344
} = primordials;
@@ -850,10 +851,37 @@ const encodingsMap = { __proto__: null };
850851
for (let i = 0; i < encodings.length; ++i)
851852
encodingsMap[encodings[i]] = i;
852853

854+
/**
855+
* Reassigns the .name property of a function.
856+
* Should be used when function can't be initially defined with desired name
857+
* or when desired name should include `#`, `[`, `]`, etc.
858+
* @param {string | symbol} name
859+
* @param {Function} fn
860+
* @param {object} [descriptor]
861+
* @returns {Function} the same function, renamed
862+
*/
863+
function assignFunctionName(name, fn, descriptor = kEmptyObject) {
864+
if (typeof name !== 'string') {
865+
const symbolDescription = SymbolPrototypeGetDescription(name);
866+
assert(symbolDescription !== undefined, 'Attempted to name function after descriptionless Symbol');
867+
name = `[${symbolDescription}]`;
868+
}
869+
return ObjectDefineProperty(fn, 'name', {
870+
__proto__: null,
871+
writable: false,
872+
enumerable: false,
873+
configurable: true,
874+
...ObjectGetOwnPropertyDescriptor(fn, 'name'),
875+
...descriptor,
876+
value: name,
877+
});
878+
}
879+
853880
module.exports = {
854881
getLazy,
855882
assertCrypto,
856883
assertTypeScript,
884+
assignFunctionName,
857885
cachedResult,
858886
convertToValidSignal,
859887
createClassWrapper,

test/parallel/test-internal-util-helpers.js

+28-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
require('../common');
55
const assert = require('assert');
66
const { types } = require('util');
7-
const { isError } = require('internal/util');
7+
const { assignFunctionName, isError } = require('internal/util');
88
const vm = require('vm');
99

1010
// Special cased errors. Test the internal function which is used in
@@ -35,3 +35,30 @@ const vm = require('vm');
3535
assert(!(differentRealmErr instanceof Error));
3636
assert(isError(differentRealmErr));
3737
}
38+
39+
{
40+
const nameMap = new Map([
41+
[ 'meaningfulName', 'meaningfulName' ],
42+
[ '', '' ],
43+
[ Symbol.asyncIterator, '[Symbol.asyncIterator]' ],
44+
[ Symbol('notWellKnownSymbol'), '[notWellKnownSymbol]' ],
45+
]);
46+
for (const fn of [
47+
() => {},
48+
function() {},
49+
function value() {},
50+
({ value() {} }).value,
51+
new Function(),
52+
Function(),
53+
function() {}.bind(null),
54+
class {},
55+
class value {},
56+
]) {
57+
for (const [ stringOrSymbol, expectedName ] of nameMap) {
58+
const namedFn = assignFunctionName(stringOrSymbol, fn);
59+
assert.strictEqual(fn, namedFn);
60+
assert.strictEqual(namedFn.name, expectedName);
61+
assert.strictEqual(namedFn.bind(null).name, `bound ${expectedName}`);
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)