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