• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const {
4  ArrayFrom,
5  ArrayIsArray,
6  Error,
7  Map,
8  ObjectCreate,
9  ObjectDefineProperties,
10  ObjectDefineProperty,
11  ObjectGetOwnPropertyDescriptor,
12  ObjectGetOwnPropertyDescriptors,
13  ObjectGetPrototypeOf,
14  ObjectSetPrototypeOf,
15  Promise,
16  ReflectConstruct,
17  Set,
18  Symbol,
19  SymbolFor,
20} = primordials;
21
22const {
23  codes: {
24    ERR_INVALID_ARG_TYPE,
25    ERR_NO_CRYPTO,
26    ERR_UNKNOWN_SIGNAL
27  },
28  uvErrmapGet,
29  overrideStackTrace,
30} = require('internal/errors');
31const { signals } = internalBinding('constants').os;
32const {
33  getHiddenValue,
34  setHiddenValue,
35  arrow_message_private_symbol: kArrowMessagePrivateSymbolIndex,
36  decorated_private_symbol: kDecoratedPrivateSymbolIndex,
37  sleep: _sleep
38} = internalBinding('util');
39const { isNativeError } = internalBinding('types');
40
41const noCrypto = !process.versions.openssl;
42
43const experimentalWarnings = new Set();
44
45const colorRegExp = /\u001b\[\d\d?m/g; // eslint-disable-line no-control-regex
46
47function removeColors(str) {
48  return str.replace(colorRegExp, '');
49}
50
51function isError(e) {
52  // An error could be an instance of Error while not being a native error
53  // or could be from a different realm and not be instance of Error but still
54  // be a native error.
55  return isNativeError(e) || e instanceof Error;
56}
57
58// Keep a list of deprecation codes that have been warned on so we only warn on
59// each one once.
60const codesWarned = new Set();
61
62// Mark that a method should not be used.
63// Returns a modified function which warns once by default.
64// If --no-deprecation is set, then it is a no-op.
65function deprecate(fn, msg, code) {
66  if (process.noDeprecation === true) {
67    return fn;
68  }
69
70  if (code !== undefined && typeof code !== 'string')
71    throw new ERR_INVALID_ARG_TYPE('code', 'string', code);
72
73  let warned = false;
74  function deprecated(...args) {
75    if (!warned) {
76      warned = true;
77      if (code !== undefined) {
78        if (!codesWarned.has(code)) {
79          process.emitWarning(msg, 'DeprecationWarning', code, deprecated);
80          codesWarned.add(code);
81        }
82      } else {
83        process.emitWarning(msg, 'DeprecationWarning', deprecated);
84      }
85    }
86    if (new.target) {
87      return ReflectConstruct(fn, args, new.target);
88    }
89    return fn.apply(this, args);
90  }
91
92  // The wrapper will keep the same prototype as fn to maintain prototype chain
93  ObjectSetPrototypeOf(deprecated, fn);
94  if (fn.prototype) {
95    // Setting this (rather than using Object.setPrototype, as above) ensures
96    // that calling the unwrapped constructor gives an instanceof the wrapped
97    // constructor.
98    deprecated.prototype = fn.prototype;
99  }
100
101  return deprecated;
102}
103
104function decorateErrorStack(err) {
105  if (!(isError(err) && err.stack) ||
106      getHiddenValue(err, kDecoratedPrivateSymbolIndex) === true)
107    return;
108
109  const arrow = getHiddenValue(err, kArrowMessagePrivateSymbolIndex);
110
111  if (arrow) {
112    err.stack = arrow + err.stack;
113    setHiddenValue(err, kDecoratedPrivateSymbolIndex, true);
114  }
115}
116
117function assertCrypto() {
118  if (noCrypto)
119    throw new ERR_NO_CRYPTO();
120}
121
122// Return undefined if there is no match.
123// Move the "slow cases" to a separate function to make sure this function gets
124// inlined properly. That prioritizes the common case.
125function normalizeEncoding(enc) {
126  if (enc == null || enc === 'utf8' || enc === 'utf-8') return 'utf8';
127  return slowCases(enc);
128}
129
130function slowCases(enc) {
131  switch (enc.length) {
132    case 4:
133      if (enc === 'UTF8') return 'utf8';
134      if (enc === 'ucs2' || enc === 'UCS2') return 'utf16le';
135      enc = `${enc}`.toLowerCase();
136      if (enc === 'utf8') return 'utf8';
137      if (enc === 'ucs2') return 'utf16le';
138      break;
139    case 3:
140      if (enc === 'hex' || enc === 'HEX' || `${enc}`.toLowerCase() === 'hex')
141        return 'hex';
142      break;
143    case 5:
144      if (enc === 'ascii') return 'ascii';
145      if (enc === 'ucs-2') return 'utf16le';
146      if (enc === 'UTF-8') return 'utf8';
147      if (enc === 'ASCII') return 'ascii';
148      if (enc === 'UCS-2') return 'utf16le';
149      enc = `${enc}`.toLowerCase();
150      if (enc === 'utf-8') return 'utf8';
151      if (enc === 'ascii') return 'ascii';
152      if (enc === 'ucs-2') return 'utf16le';
153      break;
154    case 6:
155      if (enc === 'base64') return 'base64';
156      if (enc === 'latin1' || enc === 'binary') return 'latin1';
157      if (enc === 'BASE64') return 'base64';
158      if (enc === 'LATIN1' || enc === 'BINARY') return 'latin1';
159      enc = `${enc}`.toLowerCase();
160      if (enc === 'base64') return 'base64';
161      if (enc === 'latin1' || enc === 'binary') return 'latin1';
162      break;
163    case 7:
164      if (enc === 'utf16le' || enc === 'UTF16LE' ||
165        `${enc}`.toLowerCase() === 'utf16le')
166        return 'utf16le';
167      break;
168    case 8:
169      if (enc === 'utf-16le' || enc === 'UTF-16LE' ||
170        `${enc}`.toLowerCase() === 'utf-16le')
171        return 'utf16le';
172      break;
173    default:
174      if (enc === '') return 'utf8';
175  }
176}
177
178function emitExperimentalWarning(feature) {
179  if (experimentalWarnings.has(feature)) return;
180  const msg = `${feature} is an experimental feature. This feature could ` +
181       'change at any time';
182  experimentalWarnings.add(feature);
183  process.emitWarning(msg, 'ExperimentalWarning');
184}
185
186function filterDuplicateStrings(items, low) {
187  const map = new Map();
188  for (let i = 0; i < items.length; i++) {
189    const item = items[i];
190    const key = item.toLowerCase();
191    if (low) {
192      map.set(key, key);
193    } else {
194      map.set(key, item);
195    }
196  }
197  return ArrayFrom(map.values()).sort();
198}
199
200function cachedResult(fn) {
201  let result;
202  return () => {
203    if (result === undefined)
204      result = fn();
205    return result.slice();
206  };
207}
208
209// Useful for Wrapping an ES6 Class with a constructor Function that
210// does not require the new keyword. For instance:
211//   class A { constructor(x) {this.x = x;}}
212//   const B = createClassWrapper(A);
213//   B() instanceof A // true
214//   B() instanceof B // true
215function createClassWrapper(type) {
216  function fn(...args) {
217    return ReflectConstruct(type, args, new.target || type);
218  }
219  // Mask the wrapper function name and length values
220  ObjectDefineProperties(fn, {
221    name: { value: type.name },
222    length: { value: type.length }
223  });
224  ObjectSetPrototypeOf(fn, type);
225  fn.prototype = type.prototype;
226  return fn;
227}
228
229let signalsToNamesMapping;
230function getSignalsToNamesMapping() {
231  if (signalsToNamesMapping !== undefined)
232    return signalsToNamesMapping;
233
234  signalsToNamesMapping = ObjectCreate(null);
235  for (const key in signals) {
236    signalsToNamesMapping[signals[key]] = key;
237  }
238
239  return signalsToNamesMapping;
240}
241
242function convertToValidSignal(signal) {
243  if (typeof signal === 'number' && getSignalsToNamesMapping()[signal])
244    return signal;
245
246  if (typeof signal === 'string') {
247    const signalName = signals[signal.toUpperCase()];
248    if (signalName) return signalName;
249  }
250
251  throw new ERR_UNKNOWN_SIGNAL(signal);
252}
253
254function getConstructorOf(obj) {
255  while (obj) {
256    const descriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor');
257    if (descriptor !== undefined &&
258        typeof descriptor.value === 'function' &&
259        descriptor.value.name !== '') {
260      return descriptor.value;
261    }
262
263    obj = ObjectGetPrototypeOf(obj);
264  }
265
266  return null;
267}
268
269function getSystemErrorName(err) {
270  const entry = uvErrmapGet(err);
271  return entry ? entry[0] : `Unknown system error ${err}`;
272}
273
274const kCustomPromisifiedSymbol = SymbolFor('nodejs.util.promisify.custom');
275const kCustomPromisifyArgsSymbol = Symbol('customPromisifyArgs');
276
277function promisify(original) {
278  if (typeof original !== 'function')
279    throw new ERR_INVALID_ARG_TYPE('original', 'Function', original);
280
281  if (original[kCustomPromisifiedSymbol]) {
282    const fn = original[kCustomPromisifiedSymbol];
283    if (typeof fn !== 'function') {
284      throw new ERR_INVALID_ARG_TYPE('util.promisify.custom', 'Function', fn);
285    }
286    return ObjectDefineProperty(fn, kCustomPromisifiedSymbol, {
287      value: fn, enumerable: false, writable: false, configurable: true
288    });
289  }
290
291  // Names to create an object from in case the callback receives multiple
292  // arguments, e.g. ['bytesRead', 'buffer'] for fs.read.
293  const argumentNames = original[kCustomPromisifyArgsSymbol];
294
295  function fn(...args) {
296    return new Promise((resolve, reject) => {
297      original.call(this, ...args, (err, ...values) => {
298        if (err) {
299          return reject(err);
300        }
301        if (argumentNames !== undefined && values.length > 1) {
302          const obj = {};
303          for (let i = 0; i < argumentNames.length; i++)
304            obj[argumentNames[i]] = values[i];
305          resolve(obj);
306        } else {
307          resolve(values[0]);
308        }
309      });
310    });
311  }
312
313  ObjectSetPrototypeOf(fn, ObjectGetPrototypeOf(original));
314
315  ObjectDefineProperty(fn, kCustomPromisifiedSymbol, {
316    value: fn, enumerable: false, writable: false, configurable: true
317  });
318  return ObjectDefineProperties(
319    fn,
320    ObjectGetOwnPropertyDescriptors(original)
321  );
322}
323
324promisify.custom = kCustomPromisifiedSymbol;
325
326// The build-in Array#join is slower in v8 6.0
327function join(output, separator) {
328  let str = '';
329  if (output.length !== 0) {
330    const lastIndex = output.length - 1;
331    for (let i = 0; i < lastIndex; i++) {
332      // It is faster not to use a template string here
333      str += output[i];
334      str += separator;
335    }
336    str += output[lastIndex];
337  }
338  return str;
339}
340
341// As of V8 6.6, depending on the size of the array, this is anywhere
342// between 1.5-10x faster than the two-arg version of Array#splice()
343function spliceOne(list, index) {
344  for (; index + 1 < list.length; index++)
345    list[index] = list[index + 1];
346  list.pop();
347}
348
349const kNodeModulesRE = /^(.*)[\\/]node_modules[\\/]/;
350
351let getStructuredStack;
352
353function isInsideNodeModules() {
354  if (getStructuredStack === undefined) {
355    // Lazy-load to avoid a circular dependency.
356    const { runInNewContext } = require('vm');
357    // Use `runInNewContext()` to get something tamper-proof and
358    // side-effect-free. Since this is currently only used for a deprecated API,
359    // the perf implications should be okay.
360    getStructuredStack = runInNewContext(`(function() {
361      Error.stackTraceLimit = Infinity;
362      return function structuredStack() {
363        const e = new Error();
364        overrideStackTrace.set(e, (err, trace) => trace);
365        return e.stack;
366      };
367    })()`, { overrideStackTrace }, { filename: 'structured-stack' });
368  }
369
370  const stack = getStructuredStack();
371
372  // Iterate over all stack frames and look for the first one not coming
373  // from inside Node.js itself:
374  if (ArrayIsArray(stack)) {
375    for (const frame of stack) {
376      const filename = frame.getFileName();
377      // If a filename does not start with / or contain \,
378      // it's likely from Node.js core.
379      if (!/^\/|\\/.test(filename))
380        continue;
381      return kNodeModulesRE.test(filename);
382    }
383  }
384  return false;
385}
386
387function once(callback) {
388  let called = false;
389  return function(...args) {
390    if (called) return;
391    called = true;
392    callback.apply(this, args);
393  };
394}
395
396let validateUint32;
397
398function sleep(msec) {
399  // Lazy-load to avoid a circular dependency.
400  if (validateUint32 === undefined)
401    ({ validateUint32 } = require('internal/validators'));
402
403  validateUint32(msec, 'msec');
404  _sleep(msec);
405}
406
407module.exports = {
408  assertCrypto,
409  cachedResult,
410  convertToValidSignal,
411  createClassWrapper,
412  decorateErrorStack,
413  deprecate,
414  emitExperimentalWarning,
415  filterDuplicateStrings,
416  getConstructorOf,
417  getSystemErrorName,
418  isError,
419  isInsideNodeModules,
420  join,
421  normalizeEncoding,
422  once,
423  promisify,
424  sleep,
425  spliceOne,
426  removeColors,
427
428  // Symbol used to customize promisify conversion
429  customPromisifyArgs: kCustomPromisifyArgsSymbol,
430
431  // Symbol used to provide a custom inspect function for an object as an
432  // alternative to using 'inspect'
433  customInspectSymbol: SymbolFor('nodejs.util.inspect.custom'),
434
435  // Used by the buffer module to capture an internal reference to the
436  // default isEncoding implementation, just in case userland overrides it.
437  kIsEncodingSymbol: Symbol('kIsEncodingSymbol'),
438  kVmBreakFirstLineSymbol: Symbol('kVmBreakFirstLineSymbol')
439};
440