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