1'use strict'; 2 3const { 4 ArrayIsArray, 5 NumberIsInteger, 6 NumberMAX_SAFE_INTEGER, 7 NumberMIN_SAFE_INTEGER, 8 NumberParseInt, 9 String, 10} = primordials; 11 12const { 13 hideStackFrames, 14 codes: { 15 ERR_SOCKET_BAD_PORT, 16 ERR_INVALID_ARG_TYPE, 17 ERR_INVALID_ARG_VALUE, 18 ERR_INVALID_OPT_VALUE, 19 ERR_OUT_OF_RANGE, 20 ERR_UNKNOWN_SIGNAL, 21 ERR_INVALID_CALLBACK, 22 } 23} = require('internal/errors'); 24const { normalizeEncoding } = require('internal/util'); 25const { 26 isArrayBufferView 27} = require('internal/util/types'); 28const { signals } = internalBinding('constants').os; 29 30function isInt32(value) { 31 return value === (value | 0); 32} 33 34function isUint32(value) { 35 return value === (value >>> 0); 36} 37 38const octalReg = /^[0-7]+$/; 39const modeDesc = 'must be a 32-bit unsigned integer or an octal string'; 40 41/** 42 * Parse and validate values that will be converted into mode_t (the S_* 43 * constants). Only valid numbers and octal strings are allowed. They could be 44 * converted to 32-bit unsigned integers or non-negative signed integers in the 45 * C++ land, but any value higher than 0o777 will result in platform-specific 46 * behaviors. 47 * 48 * @param {*} value Values to be validated 49 * @param {string} name Name of the argument 50 * @param {number} def If specified, will be returned for invalid values 51 * @returns {number} 52 */ 53function parseFileMode(value, name, def) { 54 if (value == null && def !== undefined) { 55 return def; 56 } 57 58 if (isUint32(value)) { 59 return value; 60 } 61 62 if (typeof value === 'number') { 63 validateInt32(value, name, 0, 2 ** 32 - 1); 64 } 65 66 if (typeof value === 'string') { 67 if (!octalReg.test(value)) { 68 throw new ERR_INVALID_ARG_VALUE(name, value, modeDesc); 69 } 70 return NumberParseInt(value, 8); 71 } 72 73 throw new ERR_INVALID_ARG_VALUE(name, value, modeDesc); 74} 75 76const validateInteger = hideStackFrames( 77 (value, name, min = NumberMIN_SAFE_INTEGER, max = NumberMAX_SAFE_INTEGER) => { 78 if (typeof value !== 'number') 79 throw new ERR_INVALID_ARG_TYPE(name, 'number', value); 80 if (!NumberIsInteger(value)) 81 throw new ERR_OUT_OF_RANGE(name, 'an integer', value); 82 if (value < min || value > max) 83 throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); 84 } 85); 86 87const validateInt32 = hideStackFrames( 88 (value, name, min = -2147483648, max = 2147483647) => { 89 // The defaults for min and max correspond to the limits of 32-bit integers. 90 if (!isInt32(value)) { 91 if (typeof value !== 'number') { 92 throw new ERR_INVALID_ARG_TYPE(name, 'number', value); 93 } 94 if (!NumberIsInteger(value)) { 95 throw new ERR_OUT_OF_RANGE(name, 'an integer', value); 96 } 97 throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); 98 } 99 if (value < min || value > max) { 100 throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); 101 } 102 } 103); 104 105const validateUint32 = hideStackFrames((value, name, positive) => { 106 if (!isUint32(value)) { 107 if (typeof value !== 'number') { 108 throw new ERR_INVALID_ARG_TYPE(name, 'number', value); 109 } 110 if (!NumberIsInteger(value)) { 111 throw new ERR_OUT_OF_RANGE(name, 'an integer', value); 112 } 113 const min = positive ? 1 : 0; 114 // 2 ** 32 === 4294967296 115 throw new ERR_OUT_OF_RANGE(name, `>= ${min} && < 4294967296`, value); 116 } 117 if (positive && value === 0) { 118 throw new ERR_OUT_OF_RANGE(name, '>= 1 && < 4294967296', value); 119 } 120}); 121 122function validateString(value, name) { 123 if (typeof value !== 'string') 124 throw new ERR_INVALID_ARG_TYPE(name, 'string', value); 125} 126 127function validateNumber(value, name) { 128 if (typeof value !== 'number') 129 throw new ERR_INVALID_ARG_TYPE(name, 'number', value); 130} 131 132const validateOneOf = hideStackFrames((value, name, oneOf, option = false) => { 133 if (!oneOf.includes(value)) { 134 const allowed = oneOf 135 .map((v) => (typeof v === 'string' ? `'${v}'` : String(v))) 136 .join(', '); 137 if (!option) { 138 const reason = 'must be one of: ' + allowed; 139 throw new ERR_INVALID_ARG_VALUE(name, value, reason); 140 } else { 141 const reason = 'Must be one of: ' + allowed; 142 throw new ERR_INVALID_OPT_VALUE(name, value, reason); 143 } 144 } 145}); 146 147function validateBoolean(value, name) { 148 if (typeof value !== 'boolean') 149 throw new ERR_INVALID_ARG_TYPE(name, 'boolean', value); 150} 151 152const validateObject = hideStackFrames( 153 (value, name, { 154 nullable = false, 155 allowArray = false, 156 allowFunction = false, 157 } = {}) => { 158 if ((!nullable && value === null) || 159 (!allowArray && ArrayIsArray(value)) || 160 (typeof value !== 'object' && ( 161 !allowFunction || typeof value !== 'function' 162 ))) { 163 throw new ERR_INVALID_ARG_TYPE(name, 'Object', value); 164 } 165 }); 166 167const validateArray = hideStackFrames((value, name, minLength = 0) => { 168 if (!ArrayIsArray(value)) { 169 throw new ERR_INVALID_ARG_TYPE(name, 'Array', value); 170 } 171 if (value.length < minLength) { 172 const reason = `must be longer than ${minLength}`; 173 throw new ERR_INVALID_ARG_VALUE(name, value, reason); 174 } 175}); 176 177function validateSignalName(signal, name = 'signal') { 178 validateString(signal, name); 179 180 if (signals[signal] === undefined) { 181 if (signals[signal.toUpperCase()] !== undefined) { 182 throw new ERR_UNKNOWN_SIGNAL(signal + 183 ' (signals must use all capital letters)'); 184 } 185 186 throw new ERR_UNKNOWN_SIGNAL(signal); 187 } 188} 189 190const validateBuffer = hideStackFrames((buffer, name = 'buffer') => { 191 if (!isArrayBufferView(buffer)) { 192 throw new ERR_INVALID_ARG_TYPE(name, 193 ['Buffer', 'TypedArray', 'DataView'], 194 buffer); 195 } 196}); 197 198function validateEncoding(data, encoding) { 199 const normalizedEncoding = normalizeEncoding(encoding); 200 const length = data.length; 201 202 if (normalizedEncoding === 'hex' && length % 2 !== 0) { 203 throw new ERR_INVALID_ARG_VALUE('encoding', encoding, 204 `is invalid for data of length ${length}`); 205 } 206} 207 208// Check that the port number is not NaN when coerced to a number, 209// is an integer and that it falls within the legal range of port numbers. 210function validatePort(port, name = 'Port', allowZero = true) { 211 if ((typeof port !== 'number' && typeof port !== 'string') || 212 (typeof port === 'string' && port.trim().length === 0) || 213 +port !== (+port >>> 0) || 214 port > 0xFFFF || 215 (port === 0 && !allowZero)) { 216 throw new ERR_SOCKET_BAD_PORT(name, port, allowZero); 217 } 218 return port | 0; 219} 220 221const validateCallback = hideStackFrames((callback) => { 222 if (typeof callback !== 'function') 223 throw new ERR_INVALID_CALLBACK(callback); 224}); 225 226const validateAbortSignal = hideStackFrames((signal, name) => { 227 if (signal !== undefined && 228 (signal === null || 229 typeof signal !== 'object' || 230 !('aborted' in signal))) { 231 throw new ERR_INVALID_ARG_TYPE(name, 'AbortSignal', signal); 232 } 233}); 234 235const validateFunction = hideStackFrames((value, name) => { 236 if (typeof value !== 'function') 237 throw new ERR_INVALID_ARG_TYPE(name, 'Function', value); 238}); 239 240module.exports = { 241 isInt32, 242 isUint32, 243 parseFileMode, 244 validateArray, 245 validateBoolean, 246 validateBuffer, 247 validateEncoding, 248 validateFunction, 249 validateInt32, 250 validateInteger, 251 validateNumber, 252 validateObject, 253 validateOneOf, 254 validatePort, 255 validateSignalName, 256 validateString, 257 validateUint32, 258 validateCallback, 259 validateAbortSignal, 260}; 261