diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index 3928627e5ad466..73d0c30b0bfbc8 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -19,10 +19,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)}` : @@ -49,7 +75,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), + }); + } } } } @@ -61,10 +93,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); } } } @@ -76,10 +116,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 f4d1840238e995..4fa88577e7c739 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -4,6 +4,7 @@ const { Array, ArrayIsArray, ArrayPrototypeFilter, + ArrayPrototypePushApply, BigIntPrototypeValueOf, BooleanPrototypeValueOf, DatePrototypeGetTime, @@ -653,7 +654,7 @@ function getKeys(value, showHidden) { if (showHidden) { keys = ObjectGetOwnPropertyNames(value); if (symbols.length !== 0) - keys.push(...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 @@ -669,7 +670,7 @@ function getKeys(value, showHidden) { } if (symbols.length !== 0) { const filter = (key) => ObjectPrototypePropertyIsEnumerable(value, key); - keys.push(...symbols.filter(filter)); + ArrayPrototypePushApply(keys, ArrayPrototypeFilter(symbols, filter)); } } return keys; diff --git a/lib/repl.js b/lib/repl.js index badd4001d0d8b4..fa800d50f88838 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -1343,6 +1343,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);