diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index ec1316d3245a41..c30451b05bdac2 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -21,10 +21,36 @@ const { // `uncurryThis` is equivalent to `func => Function.prototype.call.bind(func)`. // It is using `bind.bind(call)` to avoid using `Function.prototype.bind` // and `Function.prototype.call` after it may have been mutated by users. -const { bind, call } = Function.prototype; +const { apply, bind, call } = Function.prototype; const uncurryThis = bind.bind(call); primordials.uncurryThis = uncurryThis; +// `applyBind` is equivalent to `func => Function.prototype.apply.bind(func)`. +// It is using `bind.bind(apply)` to avoid using `Function.prototype.bind` +// and `Function.prototype.apply` after it may have been mutated by users. +const applyBind = bind.bind(apply); +primordials.applyBind = applyBind; + +// Methods that accept a variable number of arguments, and thus it's useful to +// also create `${prefix}${key}Apply`, which uses `Function.prototype.apply`, +// instead of `Function.prototype.call`, and thus doesn't require iterator +// destructuring. +const varargsMethods = [ + // 'ArrayPrototypeConcat' is omitted, because it performs the spread + // on its own for arrays and array-likes with a truthy + // @@isConcatSpreadable symbol property. + 'ArrayOf', + 'ArrayPrototypePush', + 'ArrayPrototypeUnshift', + // 'FunctionPrototypeCall' is omitted, since there's 'ReflectApply' + // and 'FunctionPrototypeApply'. + 'MathHypot', + 'MathMax', + 'MathMin', + 'StringPrototypeConcat', + 'TypedArrayOf', +]; + function getNewKey(key) { return typeof key === 'symbol' ? `Symbol${key.description[7].toUpperCase()}${key.description.slice(8)}` : @@ -51,7 +77,13 @@ function copyPropsRenamed(src, dest, prefix) { if ('get' in desc) { copyAccessor(dest, prefix, newKey, desc); } else { - ReflectDefineProperty(dest, `${prefix}${newKey}`, desc); + const name = `${prefix}${newKey}`; + ReflectDefineProperty(dest, name, desc); + if (varargsMethods.includes(name)) { + ReflectDefineProperty(dest, `${name}Apply`, { + value: applyBind(desc.value, src), + }); + } } } } @@ -63,10 +95,18 @@ function copyPropsRenamedBound(src, dest, prefix) { if ('get' in desc) { copyAccessor(dest, prefix, newKey, desc); } else { - if (typeof desc.value === 'function') { - desc.value = desc.value.bind(src); + const { value } = desc; + if (typeof value === 'function') { + desc.value = value.bind(src); + } + + const name = `${prefix}${newKey}`; + ReflectDefineProperty(dest, name, desc); + if (varargsMethods.includes(name)) { + ReflectDefineProperty(dest, `${name}Apply`, { + value: applyBind(value, src), + }); } - ReflectDefineProperty(dest, `${prefix}${newKey}`, desc); } } } @@ -78,10 +118,18 @@ function copyPrototype(src, dest, prefix) { if ('get' in desc) { copyAccessor(dest, prefix, newKey, desc); } else { - if (typeof desc.value === 'function') { - desc.value = uncurryThis(desc.value); + const { value } = desc; + if (typeof value === 'function') { + desc.value = uncurryThis(value); + } + + const name = `${prefix}${newKey}`; + ReflectDefineProperty(dest, name, desc); + if (varargsMethods.includes(name)) { + ReflectDefineProperty(dest, `${name}Apply`, { + value: applyBind(value), + }); } - ReflectDefineProperty(dest, `${prefix}${newKey}`, desc); } } } diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 41d95b468d14f3..88e70647626c6c 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -6,6 +6,7 @@ const { ArrayPrototypeFilter, ArrayPrototypeForEach, ArrayPrototypePush, + ArrayPrototypePushApply, ArrayPrototypeSort, ArrayPrototypeUnshift, BigIntPrototypeValueOf, @@ -665,7 +666,7 @@ function getKeys(value, showHidden) { if (showHidden) { keys = ObjectGetOwnPropertyNames(value); if (symbols.length !== 0) - ArrayPrototypePush(keys, ...symbols); + ArrayPrototypePushApply(keys, symbols); } else { // This might throw if `value` is a Module Namespace Object from an // unevaluated module, but we don't want to perform the actual type @@ -681,7 +682,7 @@ function getKeys(value, showHidden) { } if (symbols.length !== 0) { const filter = (key) => ObjectPrototypePropertyIsEnumerable(value, key); - ArrayPrototypePush(keys, ...ArrayPrototypeFilter(symbols, filter)); + ArrayPrototypePushApply(keys, ArrayPrototypeFilter(symbols, filter)); } } return keys; diff --git a/lib/repl.js b/lib/repl.js index 09c3a1fabb5204..09494c3f2fcf15 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -1313,6 +1313,7 @@ function complete(line, callback) { if (!this.useGlobal) { // When the context is not `global`, builtins are not own // properties of it. + // `globalBuiltins` is a `SafeSet`, not an Array-like. ArrayPrototypePush(contextOwnNames, ...globalBuiltins); } ArrayPrototypePush(completionGroups, contextOwnNames);