xref: /third_party/node/lib/util.js (revision 1cb0ef41)
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22'use strict';
23
24const {
25  ArrayIsArray,
26  ArrayPrototypeJoin,
27  ArrayPrototypePop,
28  Date,
29  DatePrototypeGetDate,
30  DatePrototypeGetHours,
31  DatePrototypeGetMinutes,
32  DatePrototypeGetMonth,
33  DatePrototypeGetSeconds,
34  Error,
35  FunctionPrototypeBind,
36  NumberIsSafeInteger,
37  ObjectDefineProperties,
38  ObjectDefineProperty,
39  ObjectGetOwnPropertyDescriptors,
40  ObjectKeys,
41  ObjectPrototypeToString,
42  ObjectSetPrototypeOf,
43  ObjectValues,
44  ReflectApply,
45  StringPrototypePadStart,
46} = primordials;
47
48const {
49  codes: {
50    ERR_FALSY_VALUE_REJECTION,
51    ERR_INVALID_ARG_TYPE,
52    ERR_OUT_OF_RANGE,
53  },
54  errnoException,
55  exceptionWithHostPort,
56  hideStackFrames,
57} = require('internal/errors');
58const {
59  format,
60  formatWithOptions,
61  inspect,
62  stripVTControlCharacters,
63} = require('internal/util/inspect');
64const { debuglog } = require('internal/util/debuglog');
65const {
66  validateFunction,
67  validateNumber,
68} = require('internal/validators');
69const { isBuffer } = require('buffer').Buffer;
70const types = require('internal/util/types');
71
72const {
73  deprecate,
74  getSystemErrorMap,
75  getSystemErrorName: internalErrorName,
76  promisify,
77  toUSVString,
78  defineLazyProperties,
79} = require('internal/util');
80
81let abortController;
82
83function lazyAbortController() {
84  abortController ??= require('internal/abort_controller');
85  return abortController;
86}
87
88let internalDeepEqual;
89
90/**
91 * @deprecated since v4.0.0
92 * @param {any} arg
93 * @returns {arg is boolean}
94 */
95function isBoolean(arg) {
96  return typeof arg === 'boolean';
97}
98
99/**
100 * @deprecated since v4.0.0
101 * @param {any} arg
102 * @returns {arg is null}
103 */
104function isNull(arg) {
105  return arg === null;
106}
107
108/**
109 * @deprecated since v4.0.0
110 * @param {any} arg
111 * @returns {arg is (null | undefined)}
112 */
113function isNullOrUndefined(arg) {
114  return arg === null || arg === undefined;
115}
116
117/**
118 * @deprecated since v4.0.0
119 * @param {any} arg
120 * @returns {arg is number}
121 */
122function isNumber(arg) {
123  return typeof arg === 'number';
124}
125
126/**
127 * @param {any} arg
128 * @returns {arg is string}
129 */
130function isString(arg) {
131  return typeof arg === 'string';
132}
133
134/**
135 * @deprecated since v4.0.0
136 * @param {any} arg
137 * @returns {arg is symbol}
138 */
139function isSymbol(arg) {
140  return typeof arg === 'symbol';
141}
142
143/**
144 * @deprecated since v4.0.0
145 * @param {any} arg
146 * @returns {arg is undefined}
147 */
148function isUndefined(arg) {
149  return arg === undefined;
150}
151
152/**
153 * @deprecated since v4.0.0
154 * @param {any} arg
155 * @returns {a is NonNullable<object>}
156 */
157function isObject(arg) {
158  return arg !== null && typeof arg === 'object';
159}
160
161/**
162 * @deprecated since v4.0.0
163 * @param {any} e
164 * @returns {arg is Error}
165 */
166function isError(e) {
167  return ObjectPrototypeToString(e) === '[object Error]' || e instanceof Error;
168}
169
170/**
171 * @deprecated since v4.0.0
172 * @param {any} arg
173 * @returns {arg is Function}
174 */
175function isFunction(arg) {
176  return typeof arg === 'function';
177}
178
179/**
180 * @deprecated since v4.0.0
181 * @param {any} arg
182 * @returns {arg is (boolean | null | number | string | symbol | undefined)}
183 */
184function isPrimitive(arg) {
185  return arg === null ||
186         (typeof arg !== 'object' && typeof arg !== 'function');
187}
188
189/**
190 * @param {number} n
191 * @returns {string}
192 */
193function pad(n) {
194  return StringPrototypePadStart(n.toString(), 2, '0');
195}
196
197const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
198                'Oct', 'Nov', 'Dec'];
199
200/**
201 * @returns {string}  26 Feb 16:19:34
202 */
203function timestamp() {
204  const d = new Date();
205  const t = ArrayPrototypeJoin([
206    pad(DatePrototypeGetHours(d)),
207    pad(DatePrototypeGetMinutes(d)),
208    pad(DatePrototypeGetSeconds(d)),
209  ], ':');
210  return `${DatePrototypeGetDate(d)} ${months[DatePrototypeGetMonth(d)]} ${t}`;
211}
212
213let console;
214/**
215 * Log is just a thin wrapper to console.log that prepends a timestamp
216 * @deprecated since v6.0.0
217 * @type {(...args: any[]) => void}
218 */
219function log(...args) {
220  if (!console) {
221    console = require('internal/console/global');
222  }
223  console.log('%s - %s', timestamp(), format(...args));
224}
225
226/**
227 * Inherit the prototype methods from one constructor into another.
228 *
229 * The Function.prototype.inherits from lang.js rewritten as a standalone
230 * function (not on Function.prototype). NOTE: If this file is to be loaded
231 * during bootstrapping this function needs to be rewritten using some native
232 * functions as prototype setup using normal JavaScript does not work as
233 * expected during bootstrapping (see mirror.js in r114903).
234 * @param {Function} ctor Constructor function which needs to inherit the
235 *     prototype.
236 * @param {Function} superCtor Constructor function to inherit prototype from.
237 * @throws {TypeError} Will error if either constructor is null, or if
238 *     the super constructor lacks a prototype.
239 */
240function inherits(ctor, superCtor) {
241
242  if (ctor === undefined || ctor === null)
243    throw new ERR_INVALID_ARG_TYPE('ctor', 'Function', ctor);
244
245  if (superCtor === undefined || superCtor === null)
246    throw new ERR_INVALID_ARG_TYPE('superCtor', 'Function', superCtor);
247
248  if (superCtor.prototype === undefined) {
249    throw new ERR_INVALID_ARG_TYPE('superCtor.prototype',
250                                   'Object', superCtor.prototype);
251  }
252  ObjectDefineProperty(ctor, 'super_', {
253    __proto__: null,
254    value: superCtor,
255    writable: true,
256    configurable: true,
257  });
258  ObjectSetPrototypeOf(ctor.prototype, superCtor.prototype);
259}
260
261/**
262 * @deprecated since v6.0.0
263 * @template T
264 * @template S
265 * @param {T} target
266 * @param {S} source
267 * @returns {S extends null ? T : (T & S)}
268 */
269function _extend(target, source) {
270  // Don't do anything if source isn't an object
271  if (source === null || typeof source !== 'object') return target;
272
273  const keys = ObjectKeys(source);
274  let i = keys.length;
275  while (i--) {
276    target[keys[i]] = source[keys[i]];
277  }
278  return target;
279}
280
281const callbackifyOnRejected = hideStackFrames((reason, cb) => {
282  // `!reason` guard inspired by bluebird (Ref: https://goo.gl/t5IS6M).
283  // Because `null` is a special error value in callbacks which means "no error
284  // occurred", we error-wrap so the callback consumer can distinguish between
285  // "the promise rejected with null" or "the promise fulfilled with undefined".
286  if (!reason) {
287    reason = new ERR_FALSY_VALUE_REJECTION(reason);
288  }
289  return cb(reason);
290});
291
292/**
293 * @template {(...args: any[]) => Promise<any>} T
294 * @param {T} original
295 * @returns {T extends (...args: infer TArgs) => Promise<infer TReturn> ?
296 *   ((...params: [...TArgs, ((err: Error, ret: TReturn) => any)]) => void) :
297 *   never
298 * }
299 */
300function callbackify(original) {
301  validateFunction(original, 'original');
302
303  // We DO NOT return the promise as it gives the user a false sense that
304  // the promise is actually somehow related to the callback's execution
305  // and that the callback throwing will reject the promise.
306  function callbackified(...args) {
307    const maybeCb = ArrayPrototypePop(args);
308    validateFunction(maybeCb, 'last argument');
309    const cb = FunctionPrototypeBind(maybeCb, this);
310    // In true node style we process the callback on `nextTick` with all the
311    // implications (stack, `uncaughtException`, `async_hooks`)
312    ReflectApply(original, this, args)
313      .then((ret) => process.nextTick(cb, null, ret),
314            (rej) => process.nextTick(callbackifyOnRejected, rej, cb));
315  }
316
317  const descriptors = ObjectGetOwnPropertyDescriptors(original);
318  // It is possible to manipulate a functions `length` or `name` property. This
319  // guards against the manipulation.
320  if (typeof descriptors.length.value === 'number') {
321    descriptors.length.value++;
322  }
323  if (typeof descriptors.name.value === 'string') {
324    descriptors.name.value += 'Callbackified';
325  }
326  const propertiesValues = ObjectValues(descriptors);
327  for (let i = 0; i < propertiesValues.length; i++) {
328  // We want to use null-prototype objects to not rely on globally mutable
329  // %Object.prototype%.
330    ObjectSetPrototypeOf(propertiesValues[i], null);
331  }
332  ObjectDefineProperties(callbackified, descriptors);
333  return callbackified;
334}
335
336/**
337 * @param {number} err
338 * @returns {string}
339 */
340function getSystemErrorName(err) {
341  validateNumber(err, 'err');
342  if (err >= 0 || !NumberIsSafeInteger(err)) {
343    throw new ERR_OUT_OF_RANGE('err', 'a negative integer', err);
344  }
345  return internalErrorName(err);
346}
347
348// Keep the `exports =` so that various functions can still be monkeypatched
349module.exports = {
350  _errnoException: errnoException,
351  _exceptionWithHostPort: exceptionWithHostPort,
352  _extend,
353  callbackify,
354  debug: debuglog,
355  debuglog,
356  deprecate,
357  format,
358  formatWithOptions,
359  getSystemErrorMap,
360  getSystemErrorName,
361  inherits,
362  inspect,
363  isArray: ArrayIsArray,
364  isBoolean,
365  isBuffer,
366  isDeepStrictEqual(a, b) {
367    if (internalDeepEqual === undefined) {
368      internalDeepEqual = require('internal/util/comparisons')
369        .isDeepStrictEqual;
370    }
371    return internalDeepEqual(a, b);
372  },
373  isNull,
374  isNullOrUndefined,
375  isNumber,
376  isString,
377  isSymbol,
378  isUndefined,
379  isRegExp: types.isRegExp,
380  isObject,
381  isDate: types.isDate,
382  isError,
383  isFunction,
384  isPrimitive,
385  log,
386  promisify,
387  stripVTControlCharacters,
388  toUSVString,
389  get transferableAbortSignal() {
390    return lazyAbortController().transferableAbortSignal;
391  },
392  get transferableAbortController() {
393    return lazyAbortController().transferableAbortController;
394  },
395  get aborted() {
396    return lazyAbortController().aborted;
397  },
398  types,
399};
400
401defineLazyProperties(
402  module.exports,
403  'internal/util/parse_args/parse_args',
404  ['parseArgs'],
405);
406
407defineLazyProperties(
408  module.exports,
409  'internal/encoding',
410  ['TextDecoder', 'TextEncoder'],
411);
412
413defineLazyProperties(
414  module.exports,
415  'internal/mime',
416  ['MIMEType', 'MIMEParams'],
417);
418