• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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