diff --git a/lib/_http_server.js b/lib/_http_server.js index 9df050e84289ae..4da10dd1edf133 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -672,8 +672,9 @@ assignFunctionName(EE.captureRejectionSymbol, function(err, event, ...args) { break; } default: - net.Server.prototype[SymbolFor('nodejs.rejection')] - .apply(this, arguments); + ReflectApply( + net.Server.prototype[SymbolFor('nodejs.rejection')], + this, arguments); } }); diff --git a/lib/eslint.config_partial.mjs b/lib/eslint.config_partial.mjs index 3d638a697d233b..fc899bff694524 100644 --- a/lib/eslint.config_partial.mjs +++ b/lib/eslint.config_partial.mjs @@ -38,6 +38,10 @@ const noRestrictedSyntax = [ selector: "ThrowStatement > NewExpression[callee.name=/^ERR_[A-Z_]+$/] > ObjectExpression:first-child:not(:has([key.name='message']):has([key.name='code']):has([key.name='syscall']))", message: 'The context passed into the SystemError constructor must include .code, .syscall, and .message properties.', }, + { + selector: "CallExpression:matches([callee.type='Identifier'][callee.name='FunctionPrototypeApply'], [callee.type='MemberExpression'][callee.property.type='Identifier'][callee.property.name='apply'][arguments.length=2])", + message: 'Use `ReflectApply` instead of %Function.prototype.apply%', + }, ]; export default [ @@ -57,7 +61,13 @@ export default [ rules: { 'prefer-object-spread': 'error', 'no-buffer-constructor': 'error', - 'no-restricted-syntax': noRestrictedSyntax, + 'no-restricted-syntax': [ + ...noRestrictedSyntax, + { + selector: "CallExpression[callee.type='Identifier'][callee.name='ReflectApply'][arguments.2.type='ArrayExpression']", + message: 'Use `FunctionPrototypeCall` to avoid creating an ad-hoc array', + }, + ], 'no-restricted-globals': [ 'error', { diff --git a/lib/events.js b/lib/events.js index 1c732802ef28aa..1e2389f4cf6d7b 100644 --- a/lib/events.js +++ b/lib/events.js @@ -505,7 +505,7 @@ EventEmitter.prototype.emit = function emit(type, ...args) { return false; if (typeof handler === 'function') { - const result = handler.apply(this, args); + const result = ReflectApply(handler, this, args); // We check if result is undefined first because that // is the most common case so we do not pay any perf @@ -517,7 +517,7 @@ EventEmitter.prototype.emit = function emit(type, ...args) { const len = handler.length; const listeners = arrayClone(handler); for (let i = 0; i < len; ++i) { - const result = listeners[i].apply(this, args); + const result = ReflectApply(listeners[i], this, args); // We check if result is undefined first because that // is the most common case so we do not pay any perf @@ -620,7 +620,7 @@ function onceWrapper() { this.fired = true; if (arguments.length === 0) return this.listener.call(this.target); - return this.listener.apply(this.target, arguments); + return ReflectApply(this.listener, this.target, arguments); } } diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index 46ee92a7bb39c4..d698831a1425c7 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -5,6 +5,7 @@ const { ErrorCaptureStackTrace, ObjectDefineProperty, ObjectPrototypeHasOwnProperty, + ReflectApply, Symbol, } = primordials; @@ -125,9 +126,9 @@ function callbackTrampoline(asyncId, resource, cb, ...args) { let result; if (asyncId === 0 && typeof domain_cb === 'function') { args.unshift(cb); - result = domain_cb.apply(this, args); + result = ReflectApply(domain_cb, this, args); } else { - result = cb.apply(this, args); + result = ReflectApply(cb, this, args); } if (asyncId !== 0 && hasHooks(kAfter)) @@ -462,14 +463,14 @@ function clearDefaultTriggerAsyncId() { */ function defaultTriggerAsyncIdScope(triggerAsyncId, block, ...args) { if (triggerAsyncId === undefined) - return block.apply(null, args); + return ReflectApply(block, null, args); // CHECK(NumberIsSafeInteger(triggerAsyncId)) // CHECK(triggerAsyncId > 0) const oldDefaultTriggerAsyncId = async_id_fields[kDefaultTriggerAsyncId]; async_id_fields[kDefaultTriggerAsyncId] = triggerAsyncId; try { - return block.apply(null, args); + return ReflectApply(block, null, args); } finally { async_id_fields[kDefaultTriggerAsyncId] = oldDefaultTriggerAsyncId; } diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index 2264390ea1faff..87f271adcd3e50 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -6,7 +6,6 @@ const { JSONParse, ObjectAssign, ObjectPrototypeHasOwnProperty, - ReflectApply, SafeArrayIterator, SafeMap, SafeSet, @@ -198,8 +197,8 @@ function loadCJSModule(module, source, url, filename, isMain) { }); setOwnProperty(requireFn, 'main', process.mainModule); - ReflectApply(compiledWrapper, module.exports, - [module.exports, requireFn, module, filename, __dirname]); + FunctionPrototypeCall(compiledWrapper, module.exports, + module.exports, requireFn, module, filename, __dirname); setOwnProperty(module, 'loaded', true); } diff --git a/lib/internal/streams/end-of-stream.js b/lib/internal/streams/end-of-stream.js index 99587ae254ddd5..2d520cee367120 100644 --- a/lib/internal/streams/end-of-stream.js +++ b/lib/internal/streams/end-of-stream.js @@ -6,6 +6,7 @@ const { Promise, PromisePrototypeThen, + ReflectApply, SymbolDispose, } = primordials; @@ -280,7 +281,7 @@ function eos(stream, options, callback) { const originalCallback = callback; callback = once((...args) => { disposable[SymbolDispose](); - originalCallback.apply(stream, args); + ReflectApply(originalCallback, stream, args); }); } } @@ -304,13 +305,13 @@ function eosWeb(stream, options, callback) { const originalCallback = callback; callback = once((...args) => { disposable[SymbolDispose](); - originalCallback.apply(stream, args); + ReflectApply(originalCallback, stream, args); }); } } const resolverFn = (...args) => { if (!isAborted) { - process.nextTick(() => callback.apply(stream, args)); + process.nextTick(() => ReflectApply(callback, stream, args)); } }; PromisePrototypeThen( diff --git a/lib/internal/streams/readable.js b/lib/internal/streams/readable.js index 126689854d326d..63346e6486a022 100644 --- a/lib/internal/streams/readable.js +++ b/lib/internal/streams/readable.js @@ -30,6 +30,7 @@ const { ObjectKeys, ObjectSetPrototypeOf, Promise, + ReflectApply, SafeSet, Symbol, SymbolAsyncDispose, @@ -1182,8 +1183,7 @@ Readable.prototype.removeListener = function(ev, fn) { Readable.prototype.off = Readable.prototype.removeListener; Readable.prototype.removeAllListeners = function(ev) { - const res = Stream.prototype.removeAllListeners.apply(this, - arguments); + const res = ReflectApply(Stream.prototype.removeAllListeners, this, arguments); if (ev === 'readable' || ev === undefined) { // We need to check if there is someone still listening to diff --git a/lib/internal/test_runner/mock/mock_timers.js b/lib/internal/test_runner/mock/mock_timers.js index 490f8cd06fc549..643128c3f0031f 100644 --- a/lib/internal/test_runner/mock/mock_timers.js +++ b/lib/internal/test_runner/mock/mock_timers.js @@ -5,7 +5,6 @@ const { ArrayPrototypeIncludes, DatePrototypeGetTime, DatePrototypeToString, - FunctionPrototypeApply, FunctionPrototypeBind, FunctionPrototypeToString, NumberIsNaN, @@ -14,6 +13,7 @@ const { ObjectGetOwnPropertyDescriptor, ObjectGetOwnPropertyDescriptors, PromiseWithResolvers, + ReflectApply, Symbol, SymbolDispose, globalThis, @@ -645,7 +645,7 @@ class MockTimers { let timer = this.#executionQueue.peek(); while (timer) { if (timer.runAt > this.#now) break; - FunctionPrototypeApply(timer.callback, undefined, timer.args); + ReflectApply(timer.callback, undefined, timer.args); // Check if the timeout was cleared by calling clearTimeout inside its own callback const afterCallback = this.#executionQueue.peek();