diff --git a/lib/assert.js b/lib/assert.js index 8ca9b211394ba8..bc0440ae9bb8da 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -68,6 +68,9 @@ const { isError } = require('internal/util'); const errorCache = new SafeMap(); const CallTracker = require('internal/assert/calltracker'); +const { + validateFunction, +} = require('internal/validators'); let isDeepEqual; let isDeepStrictEqual; @@ -693,9 +696,7 @@ function expectedException(actual, expected, message, fn) { } function getActual(fn) { - if (typeof fn !== 'function') { - throw new ERR_INVALID_ARG_TYPE('fn', 'Function', fn); - } + validateFunction(fn, 'fn'); try { fn(); } catch (e) { diff --git a/lib/async_hooks.js b/lib/async_hooks.js index 90b48ebe4b2754..2d384736c2d113 100644 --- a/lib/async_hooks.js +++ b/lib/async_hooks.js @@ -16,10 +16,12 @@ const { const { ERR_ASYNC_CALLBACK, ERR_ASYNC_TYPE, - ERR_INVALID_ARG_TYPE, ERR_INVALID_ASYNC_ID } = require('internal/errors').codes; -const { validateString } = require('internal/validators'); +const { + validateFunction, + validateString, +} = require('internal/validators'); const internal_async_hooks = require('internal/async_hooks'); // Get functions @@ -221,8 +223,7 @@ class AsyncResource { } bind(fn) { - if (typeof fn !== 'function') - throw new ERR_INVALID_ARG_TYPE('fn', 'Function', fn); + validateFunction(fn, 'fn'); const ret = FunctionPrototypeBind(this.runInAsyncScope, this, fn); ObjectDefineProperties(ret, { 'length': { diff --git a/lib/diagnostics_channel.js b/lib/diagnostics_channel.js index c29c9ff0052405..1792980ec1b958 100644 --- a/lib/diagnostics_channel.js +++ b/lib/diagnostics_channel.js @@ -15,6 +15,9 @@ const { ERR_INVALID_ARG_TYPE, } } = require('internal/errors'); +const { + validateFunction, +} = require('internal/validators'); const { triggerUncaughtException } = internalBinding('errors'); @@ -23,10 +26,7 @@ const { WeakReference } = internalBinding('util'); // TODO(qard): should there be a C++ channel interface? class ActiveChannel { subscribe(subscription) { - if (typeof subscription !== 'function') { - throw new ERR_INVALID_ARG_TYPE('subscription', ['function'], - subscription); - } + validateFunction(subscription, 'subscription'); ArrayPrototypePush(this._subscribers, subscription); } diff --git a/lib/events.js b/lib/events.js index 75baac156aaf86..7a7abc5c4b3339 100644 --- a/lib/events.js +++ b/lib/events.js @@ -66,7 +66,8 @@ const { } = require('internal/util/inspect'); const { - validateAbortSignal + validateAbortSignal, + validateFunction, } = require('internal/validators'); const kCapture = Symbol('kCapture'); @@ -130,9 +131,7 @@ let defaultMaxListeners = 10; let isEventTarget; function checkListener(listener) { - if (typeof listener !== 'function') { - throw new ERR_INVALID_ARG_TYPE('listener', 'Function', listener); - } + validateFunction(listener, 'listener'); } ObjectDefineProperty(EventEmitter, 'defaultMaxListeners', { diff --git a/lib/fs.js b/lib/fs.js index 096b54d0c11cc7..966016522f02a7 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -126,8 +126,9 @@ const { validateBoolean, validateBuffer, validateCallback, + validateFunction, validateInteger, - validateInt32 + validateInt32, } = require('internal/validators'); // 2 ** 32 - 1 const kMaxUserId = 4294967295; @@ -1628,9 +1629,7 @@ function watchFile(filename, options, listener) { ...options }; - if (typeof listener !== 'function') { - throw new ERR_INVALID_ARG_TYPE('listener', 'Function', listener); - } + validateFunction(listener, 'listener'); stat = statWatchers.get(filename); diff --git a/lib/internal/dgram.js b/lib/internal/dgram.js index 950a82392c8e63..f27baf5e0ef60b 100644 --- a/lib/internal/dgram.js +++ b/lib/internal/dgram.js @@ -8,9 +8,14 @@ const { const { codes } = require('internal/errors'); const { UDP } = internalBinding('udp_wrap'); const { guessHandleType } = internalBinding('util'); -const { isInt32 } = require('internal/validators'); +const { + isInt32, + validateFunction, +} = require('internal/validators'); const { UV_EINVAL } = internalBinding('uv'); -const { ERR_INVALID_ARG_TYPE, ERR_SOCKET_BAD_TYPE } = codes; +const { + ERR_SOCKET_BAD_TYPE, +} = codes; const kStateSymbol = Symbol('state symbol'); let dns; // Lazy load for startup performance. @@ -31,8 +36,8 @@ function newHandle(type, lookup) { } lookup = dns.lookup; - } else if (typeof lookup !== 'function') { - throw new ERR_INVALID_ARG_TYPE('lookup', 'Function', lookup); + } else { + validateFunction(lookup, 'lookup'); } if (type === 'udp4') { diff --git a/lib/internal/fs/streams.js b/lib/internal/fs/streams.js index 9542516aa7c1a2..2e79243da8fb5e 100644 --- a/lib/internal/fs/streams.js +++ b/lib/internal/fs/streams.js @@ -17,7 +17,10 @@ const { ERR_METHOD_NOT_IMPLEMENTED, } = require('internal/errors').codes; const { deprecate } = require('internal/util'); -const { validateInteger } = require('internal/validators'); +const { + validateFunction, + validateInteger, +} = require('internal/validators'); const { errorOrDestroy } = require('internal/streams/destroy'); const fs = require('fs'); const { kRef, kUnref, FileHandle } = require('internal/fs/promises'); @@ -155,20 +158,9 @@ function ReadStream(path, options) { this[kFs] = options.fs || fs; - if (typeof this[kFs].open !== 'function') { - throw new ERR_INVALID_ARG_TYPE('options.fs.open', 'function', - this[kFs].open); - } - - if (typeof this[kFs].read !== 'function') { - throw new ERR_INVALID_ARG_TYPE('options.fs.read', 'function', - this[kFs].read); - } - - if (typeof this[kFs].close !== 'function') { - throw new ERR_INVALID_ARG_TYPE('options.fs.close', 'function', - this[kFs].close); - } + validateFunction(this[kFs].open, 'options.fs.open'); + validateFunction(this[kFs].read, 'options.fs.read'); + validateFunction(this[kFs].close, 'options.fs.close'); options.autoDestroy = options.autoClose === undefined ? true : options.autoClose; @@ -310,30 +302,23 @@ function WriteStream(path, options) { options.decodeStrings = true; this[kFs] = options.fs || fs; - if (typeof this[kFs].open !== 'function') { - throw new ERR_INVALID_ARG_TYPE('options.fs.open', 'function', - this[kFs].open); - } + + validateFunction(this[kFs].open, 'options.fs.open'); if (!this[kFs].write && !this[kFs].writev) { throw new ERR_INVALID_ARG_TYPE('options.fs.write', 'function', this[kFs].write); } - if (this[kFs].write && typeof this[kFs].write !== 'function') { - throw new ERR_INVALID_ARG_TYPE('options.fs.write', 'function', - this[kFs].write); + if (this[kFs].write) { + validateFunction(this[kFs].write, 'options.fs.write'); } - if (this[kFs].writev && typeof this[kFs].writev !== 'function') { - throw new ERR_INVALID_ARG_TYPE('options.fs.writev', 'function', - this[kFs].writev); + if (this[kFs].writev) { + validateFunction(this[kFs].writev, 'options.fs.writev'); } - if (typeof this[kFs].close !== 'function') { - throw new ERR_INVALID_ARG_TYPE('options.fs.close', 'function', - this[kFs].close); - } + validateFunction(this[kFs].close, 'options.fs.close'); // It's enough to override either, in which case only one will be used. if (!this[kFs].write) { diff --git a/lib/internal/process/task_queues.js b/lib/internal/process/task_queues.js index 4081acae951acd..f9e72c67075f94 100644 --- a/lib/internal/process/task_queues.js +++ b/lib/internal/process/task_queues.js @@ -33,12 +33,12 @@ const { emitDestroy, symbols: { async_id_symbol, trigger_async_id_symbol } } = require('internal/async_hooks'); -const { - ERR_INVALID_ARG_TYPE -} = require('internal/errors').codes; const FixedQueue = require('internal/fixed_queue'); -const { validateCallback } = require('internal/validators'); +const { + validateCallback, + validateFunction, +} = require('internal/validators'); // *Must* match Environment::TickInfo::Fields in src/env.h. const kHasTickScheduled = 0; @@ -154,9 +154,7 @@ function runMicrotask() { } function queueMicrotask(callback) { - if (typeof callback !== 'function') { - throw new ERR_INVALID_ARG_TYPE('callback', 'function', callback); - } + validateFunction(callback, 'callback'); const asyncResource = createMicrotaskResource(); asyncResource.callback = callback; diff --git a/lib/internal/quic/util.js b/lib/internal/quic/util.js index 722cc6d74b8397..97da6953647c6b 100644 --- a/lib/internal/quic/util.js +++ b/lib/internal/quic/util.js @@ -116,6 +116,7 @@ const { const { validateBoolean, validateBuffer, + validateFunction, validateInteger, validateObject, validatePort, @@ -175,8 +176,9 @@ function validateCloseCode(code) { } function validateLookup(lookup) { - if (lookup && typeof lookup !== 'function') - throw new ERR_INVALID_ARG_TYPE('options.lookup', 'function', lookup); + if (lookup) { + validateFunction(lookup, 'options.lookup'); + } } function validatePreferredAddress(address) { diff --git a/lib/internal/streams/end-of-stream.js b/lib/internal/streams/end-of-stream.js index 40a294d9af7457..308ce16ea4903e 100644 --- a/lib/internal/streams/end-of-stream.js +++ b/lib/internal/streams/end-of-stream.js @@ -8,6 +8,9 @@ const { ERR_STREAM_PREMATURE_CLOSE } = require('internal/errors').codes; const { once } = require('internal/util'); +const { + validateFunction, +} = require('internal/validators'); function isRequest(stream) { return stream.setHeader && typeof stream.abort === 'function'; @@ -60,9 +63,7 @@ function eos(stream, options, callback) { } else if (typeof options !== 'object') { throw new ERR_INVALID_ARG_TYPE('options', 'object', options); } - if (typeof callback !== 'function') { - throw new ERR_INVALID_ARG_TYPE('callback', 'function', callback); - } + validateFunction(callback, 'callback'); callback = once(callback); diff --git a/lib/internal/validators.js b/lib/internal/validators.js index 192c21b52c83e8..bf7bf93aa268ad 100644 --- a/lib/internal/validators.js +++ b/lib/internal/validators.js @@ -228,6 +228,11 @@ const validateAbortSignal = hideStackFrames((signal, name) => { } }); +const validateFunction = hideStackFrames((value, name) => { + if (typeof value !== 'function') + throw new ERR_INVALID_ARG_TYPE(name, 'Function', value); +}); + module.exports = { isInt32, isUint32, @@ -236,6 +241,7 @@ module.exports = { validateBoolean, validateBuffer, validateEncoding, + validateFunction, validateInt32, validateInteger, validateNumber, diff --git a/lib/internal/vm/module.js b/lib/internal/vm/module.js index 0d49aed62b05e1..3c79aac6d8527a 100644 --- a/lib/internal/vm/module.js +++ b/lib/internal/vm/module.js @@ -40,6 +40,7 @@ const { } = require('internal/errors').codes; const { validateBoolean, + validateFunction, validateInt32, validateUint32, validateString, @@ -187,9 +188,7 @@ class Module { if (this[kWrap] === undefined) { throw new ERR_VM_MODULE_NOT_MODULE(); } - if (typeof linker !== 'function') { - throw new ERR_INVALID_ARG_TYPE('linker', 'function', linker); - } + validateFunction(linker, 'linker'); if (this.status === 'linked') { throw new ERR_VM_MODULE_ALREADY_LINKED(); } @@ -407,10 +406,7 @@ class SyntheticModule extends Module { } }); } - if (typeof evaluateCallback !== 'function') { - throw new ERR_INVALID_ARG_TYPE('evaluateCallback', 'function', - evaluateCallback); - } + validateFunction(evaluateCallback, 'evaluateCallback'); if (typeof options !== 'object' || options === null) { throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); diff --git a/lib/perf_hooks.js b/lib/perf_hooks.js index a5b0fdf1f60028..0d8d85e0f9461d 100644 --- a/lib/perf_hooks.js +++ b/lib/perf_hooks.js @@ -68,7 +68,10 @@ const { kHandle, } = require('internal/histogram'); -const { validateCallback } = require('internal/validators'); +const { + validateCallback, + validateFunction, +} = require('internal/validators'); const { setImmediate } = require('timers'); const kCallback = Symbol('callback'); @@ -463,9 +466,7 @@ class Performance { } timerify(fn) { - if (typeof fn !== 'function') { - throw new ERR_INVALID_ARG_TYPE('fn', 'Function', fn); - } + validateFunction(fn, 'fn'); if (fn[kTimerified]) return fn[kTimerified]; const ret = timerify(fn, fn.length); diff --git a/lib/repl.js b/lib/repl.js index b1905195ab4b47..eecfc964e87511 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -166,6 +166,10 @@ const { } = internalBinding('contextify'); const history = require('internal/repl/history'); +const { + validateFunction, +} = require('internal/validators'); + let nextREPLResourceNumber = 1; // This prevents v8 code cache from getting confused and using a different // cache from a resource of the same name @@ -1432,8 +1436,8 @@ REPLServer.prototype.completeOnEditorMode = (callback) => (err, results) => { REPLServer.prototype.defineCommand = function(keyword, cmd) { if (typeof cmd === 'function') { cmd = { action: cmd }; - } else if (typeof cmd.action !== 'function') { - throw new ERR_INVALID_ARG_TYPE('cmd.action', 'Function', cmd.action); + } else { + validateFunction(cmd.action, 'cmd.action'); } this.commands[keyword] = cmd; }; diff --git a/lib/zlib.js b/lib/zlib.js index 38460c1263aa42..7812f5169545d1 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -69,6 +69,9 @@ const { kMaxLength } = require('buffer'); const { owner_symbol } = require('internal/async_hooks').symbols; +const { + validateFunction, +} = require('internal/validators'); const kFlushFlag = Symbol('kFlushFlag'); const kError = Symbol('kError'); @@ -107,8 +110,7 @@ for (const ckey of ObjectKeys(codes)) { } function zlibBuffer(engine, buffer, callback) { - if (typeof callback !== 'function') - throw new ERR_INVALID_ARG_TYPE('callback', 'function', callback); + validateFunction(callback, 'callback'); // Streams do not support non-Buffer ArrayBufferViews yet. Convert it to a // Buffer without copying. if (isArrayBufferView(buffer) &&