• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const {
4  ArrayBufferPrototypeGetByteLength,
5  ArrayFrom,
6  ArrayIsArray,
7  ArrayPrototypePush,
8  ArrayPrototypeSlice,
9  ArrayPrototypeSort,
10  Error,
11  FunctionPrototypeCall,
12  ObjectCreate,
13  ObjectDefineProperties,
14  ObjectDefineProperty,
15  ObjectGetOwnPropertyDescriptor,
16  ObjectGetOwnPropertyDescriptors,
17  ObjectGetPrototypeOf,
18  ObjectFreeze,
19  ObjectPrototypeHasOwnProperty,
20  ObjectSetPrototypeOf,
21  ObjectValues,
22  Promise,
23  ReflectApply,
24  ReflectConstruct,
25  RegExpPrototypeExec,
26  RegExpPrototypeGetDotAll,
27  RegExpPrototypeGetGlobal,
28  RegExpPrototypeGetHasIndices,
29  RegExpPrototypeGetIgnoreCase,
30  RegExpPrototypeGetMultiline,
31  RegExpPrototypeGetSticky,
32  RegExpPrototypeGetUnicode,
33  RegExpPrototypeGetSource,
34  SafeMap,
35  SafeSet,
36  SafeWeakMap,
37  StringPrototypeReplace,
38  StringPrototypeToLowerCase,
39  StringPrototypeToUpperCase,
40  Symbol,
41  SymbolFor,
42  SymbolReplace,
43  SymbolSplit,
44} = primordials;
45
46const {
47  hideStackFrames,
48  codes: {
49    ERR_NO_CRYPTO,
50    ERR_UNKNOWN_SIGNAL,
51  },
52  uvErrmapGet,
53  overrideStackTrace,
54} = require('internal/errors');
55const { signals } = internalBinding('constants').os;
56const {
57  isArrayBufferDetached: _isArrayBufferDetached,
58  privateSymbols: {
59    arrow_message_private_symbol,
60    decorated_private_symbol,
61  },
62  sleep: _sleep,
63  toUSVString: _toUSVString,
64} = internalBinding('util');
65const { isNativeError } = internalBinding('types');
66
67const noCrypto = !process.versions.openssl;
68
69const experimentalWarnings = new SafeSet();
70
71const colorRegExp = /\u001b\[\d\d?m/g; // eslint-disable-line no-control-regex
72
73const unpairedSurrogateRe =
74  /(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])/;
75function toUSVString(val) {
76  const str = `${val}`;
77  // As of V8 5.5, `str.search()` (and `unpairedSurrogateRe[@@search]()`) are
78  // slower than `unpairedSurrogateRe.exec()`.
79  const match = RegExpPrototypeExec(unpairedSurrogateRe, str);
80  if (!match)
81    return str;
82  return _toUSVString(str, match.index);
83}
84
85let uvBinding;
86
87function lazyUv() {
88  uvBinding ??= internalBinding('uv');
89  return uvBinding;
90}
91
92function removeColors(str) {
93  return StringPrototypeReplace(str, colorRegExp, '');
94}
95
96function isError(e) {
97  // An error could be an instance of Error while not being a native error
98  // or could be from a different realm and not be instance of Error but still
99  // be a native error.
100  return isNativeError(e) || e instanceof Error;
101}
102
103// Keep a list of deprecation codes that have been warned on so we only warn on
104// each one once.
105const codesWarned = new SafeSet();
106
107let validateString;
108
109// Mark that a method should not be used.
110// Returns a modified function which warns once by default.
111// If --no-deprecation is set, then it is a no-op.
112function deprecate(fn, msg, code) {
113  if (process.noDeprecation === true) {
114    return fn;
115  }
116
117  // Lazy-load to avoid a circular dependency.
118  if (validateString === undefined)
119    ({ validateString } = require('internal/validators'));
120
121  if (code !== undefined)
122    validateString(code, 'code');
123
124  let warned = false;
125  function deprecated(...args) {
126    if (!warned) {
127      warned = true;
128      if (code !== undefined) {
129        if (!codesWarned.has(code)) {
130          process.emitWarning(msg, 'DeprecationWarning', code, deprecated);
131          codesWarned.add(code);
132        }
133      } else {
134        process.emitWarning(msg, 'DeprecationWarning', deprecated);
135      }
136    }
137    if (new.target) {
138      return ReflectConstruct(fn, args, new.target);
139    }
140    return ReflectApply(fn, this, args);
141  }
142
143  // The wrapper will keep the same prototype as fn to maintain prototype chain
144  ObjectSetPrototypeOf(deprecated, fn);
145  if (fn.prototype) {
146    // Setting this (rather than using Object.setPrototype, as above) ensures
147    // that calling the unwrapped constructor gives an instanceof the wrapped
148    // constructor.
149    deprecated.prototype = fn.prototype;
150  }
151
152  return deprecated;
153}
154
155function decorateErrorStack(err) {
156  if (!(isError(err) && err.stack) || err[decorated_private_symbol])
157    return;
158
159  const arrow = err[arrow_message_private_symbol];
160
161  if (arrow) {
162    err.stack = arrow + err.stack;
163    err[decorated_private_symbol] = true;
164  }
165}
166
167function assertCrypto() {
168  if (noCrypto)
169    throw new ERR_NO_CRYPTO();
170}
171
172// Return undefined if there is no match.
173// Move the "slow cases" to a separate function to make sure this function gets
174// inlined properly. That prioritizes the common case.
175function normalizeEncoding(enc) {
176  if (enc == null || enc === 'utf8' || enc === 'utf-8') return 'utf8';
177  return slowCases(enc);
178}
179
180function slowCases(enc) {
181  switch (enc.length) {
182    case 4:
183      if (enc === 'UTF8') return 'utf8';
184      if (enc === 'ucs2' || enc === 'UCS2') return 'utf16le';
185      enc = `${enc}`.toLowerCase();
186      if (enc === 'utf8') return 'utf8';
187      if (enc === 'ucs2') return 'utf16le';
188      break;
189    case 3:
190      if (enc === 'hex' || enc === 'HEX' ||
191          `${enc}`.toLowerCase() === 'hex')
192        return 'hex';
193      break;
194    case 5:
195      if (enc === 'ascii') return 'ascii';
196      if (enc === 'ucs-2') return 'utf16le';
197      if (enc === 'UTF-8') return 'utf8';
198      if (enc === 'ASCII') return 'ascii';
199      if (enc === 'UCS-2') return 'utf16le';
200      enc = `${enc}`.toLowerCase();
201      if (enc === 'utf-8') return 'utf8';
202      if (enc === 'ascii') return 'ascii';
203      if (enc === 'ucs-2') return 'utf16le';
204      break;
205    case 6:
206      if (enc === 'base64') return 'base64';
207      if (enc === 'latin1' || enc === 'binary') return 'latin1';
208      if (enc === 'BASE64') return 'base64';
209      if (enc === 'LATIN1' || enc === 'BINARY') return 'latin1';
210      enc = `${enc}`.toLowerCase();
211      if (enc === 'base64') return 'base64';
212      if (enc === 'latin1' || enc === 'binary') return 'latin1';
213      break;
214    case 7:
215      if (enc === 'utf16le' || enc === 'UTF16LE' ||
216          `${enc}`.toLowerCase() === 'utf16le')
217        return 'utf16le';
218      break;
219    case 8:
220      if (enc === 'utf-16le' || enc === 'UTF-16LE' ||
221        `${enc}`.toLowerCase() === 'utf-16le')
222        return 'utf16le';
223      break;
224    case 9:
225      if (enc === 'base64url' || enc === 'BASE64URL' ||
226          `${enc}`.toLowerCase() === 'base64url')
227        return 'base64url';
228      break;
229    default:
230      if (enc === '') return 'utf8';
231  }
232}
233
234function emitExperimentalWarning(feature) {
235  if (experimentalWarnings.has(feature)) return;
236  const msg = `${feature} is an experimental feature and might change at any time`;
237  experimentalWarnings.add(feature);
238  process.emitWarning(msg, 'ExperimentalWarning');
239}
240
241function filterDuplicateStrings(items, low) {
242  const map = new SafeMap();
243  for (let i = 0; i < items.length; i++) {
244    const item = items[i];
245    const key = StringPrototypeToLowerCase(item);
246    if (low) {
247      map.set(key, key);
248    } else {
249      map.set(key, item);
250    }
251  }
252  return ArrayPrototypeSort(ArrayFrom(map.values()));
253}
254
255function cachedResult(fn) {
256  let result;
257  return () => {
258    if (result === undefined)
259      result = fn();
260    return ArrayPrototypeSlice(result);
261  };
262}
263
264// Useful for Wrapping an ES6 Class with a constructor Function that
265// does not require the new keyword. For instance:
266//   class A { constructor(x) {this.x = x;}}
267//   const B = createClassWrapper(A);
268//   B() instanceof A // true
269//   B() instanceof B // true
270function createClassWrapper(type) {
271  function fn(...args) {
272    return ReflectConstruct(type, args, new.target || type);
273  }
274  // Mask the wrapper function name and length values
275  ObjectDefineProperties(fn, {
276    name: { __proto__: null, value: type.name },
277    length: { __proto__: null, value: type.length },
278  });
279  ObjectSetPrototypeOf(fn, type);
280  fn.prototype = type.prototype;
281  return fn;
282}
283
284let signalsToNamesMapping;
285function getSignalsToNamesMapping() {
286  if (signalsToNamesMapping !== undefined)
287    return signalsToNamesMapping;
288
289  signalsToNamesMapping = ObjectCreate(null);
290  for (const key in signals) {
291    signalsToNamesMapping[signals[key]] = key;
292  }
293
294  return signalsToNamesMapping;
295}
296
297function convertToValidSignal(signal) {
298  if (typeof signal === 'number' && getSignalsToNamesMapping()[signal])
299    return signal;
300
301  if (typeof signal === 'string') {
302    const signalName = signals[StringPrototypeToUpperCase(signal)];
303    if (signalName) return signalName;
304  }
305
306  throw new ERR_UNKNOWN_SIGNAL(signal);
307}
308
309function getConstructorOf(obj) {
310  while (obj) {
311    const descriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor');
312    if (descriptor !== undefined &&
313        typeof descriptor.value === 'function' &&
314        descriptor.value.name !== '') {
315      return descriptor.value;
316    }
317
318    obj = ObjectGetPrototypeOf(obj);
319  }
320
321  return null;
322}
323
324function getSystemErrorName(err) {
325  const entry = uvErrmapGet(err);
326  return entry ? entry[0] : `Unknown system error ${err}`;
327}
328
329function getSystemErrorMap() {
330  return lazyUv().getErrorMap();
331}
332
333const kCustomPromisifiedSymbol = SymbolFor('nodejs.util.promisify.custom');
334const kCustomPromisifyArgsSymbol = Symbol('customPromisifyArgs');
335
336let validateFunction;
337
338function promisify(original) {
339  // Lazy-load to avoid a circular dependency.
340  if (validateFunction === undefined)
341    ({ validateFunction } = require('internal/validators'));
342
343  validateFunction(original, 'original');
344
345  if (original[kCustomPromisifiedSymbol]) {
346    const fn = original[kCustomPromisifiedSymbol];
347
348    validateFunction(fn, 'util.promisify.custom');
349
350    return ObjectDefineProperty(fn, kCustomPromisifiedSymbol, {
351      __proto__: null,
352      value: fn, enumerable: false, writable: false, configurable: true,
353    });
354  }
355
356  // Names to create an object from in case the callback receives multiple
357  // arguments, e.g. ['bytesRead', 'buffer'] for fs.read.
358  const argumentNames = original[kCustomPromisifyArgsSymbol];
359
360  function fn(...args) {
361    return new Promise((resolve, reject) => {
362      ArrayPrototypePush(args, (err, ...values) => {
363        if (err) {
364          return reject(err);
365        }
366        if (argumentNames !== undefined && values.length > 1) {
367          const obj = {};
368          for (let i = 0; i < argumentNames.length; i++)
369            obj[argumentNames[i]] = values[i];
370          resolve(obj);
371        } else {
372          resolve(values[0]);
373        }
374      });
375      ReflectApply(original, this, args);
376    });
377  }
378
379  ObjectSetPrototypeOf(fn, ObjectGetPrototypeOf(original));
380
381  ObjectDefineProperty(fn, kCustomPromisifiedSymbol, {
382    __proto__: null,
383    value: fn, enumerable: false, writable: false, configurable: true,
384  });
385
386  const descriptors = ObjectGetOwnPropertyDescriptors(original);
387  const propertiesValues = ObjectValues(descriptors);
388  for (let i = 0; i < propertiesValues.length; i++) {
389    // We want to use null-prototype objects to not rely on globally mutable
390    // %Object.prototype%.
391    ObjectSetPrototypeOf(propertiesValues[i], null);
392  }
393  return ObjectDefineProperties(fn, descriptors);
394}
395
396promisify.custom = kCustomPromisifiedSymbol;
397
398// The built-in Array#join is slower in v8 6.0
399function join(output, separator) {
400  let str = '';
401  if (output.length !== 0) {
402    const lastIndex = output.length - 1;
403    for (let i = 0; i < lastIndex; i++) {
404      // It is faster not to use a template string here
405      str += output[i];
406      str += separator;
407    }
408    str += output[lastIndex];
409  }
410  return str;
411}
412
413// As of V8 6.6, depending on the size of the array, this is anywhere
414// between 1.5-10x faster than the two-arg version of Array#splice()
415function spliceOne(list, index) {
416  for (; index + 1 < list.length; index++)
417    list[index] = list[index + 1];
418  list.pop();
419}
420
421const kNodeModulesRE = /^(.*)[\\/]node_modules[\\/]/;
422
423let getStructuredStack;
424
425function isInsideNodeModules() {
426  if (getStructuredStack === undefined) {
427    // Lazy-load to avoid a circular dependency.
428    const { runInNewContext } = require('vm');
429    // Use `runInNewContext()` to get something tamper-proof and
430    // side-effect-free. Since this is currently only used for a deprecated API,
431    // the perf implications should be okay.
432    getStructuredStack = runInNewContext(`(function() {
433      try { Error.stackTraceLimit = Infinity; } catch {}
434      return function structuredStack() {
435        const e = new Error();
436        overrideStackTrace.set(e, (err, trace) => trace);
437        return e.stack;
438      };
439    })()`, { overrideStackTrace }, { filename: 'structured-stack' });
440  }
441
442  const stack = getStructuredStack();
443
444  // Iterate over all stack frames and look for the first one not coming
445  // from inside Node.js itself:
446  if (ArrayIsArray(stack)) {
447    for (const frame of stack) {
448      const filename = frame.getFileName();
449      // If a filename does not start with / or contain \,
450      // it's likely from Node.js core.
451      if (RegExpPrototypeExec(/^\/|\\/, filename) === null)
452        continue;
453      return RegExpPrototypeExec(kNodeModulesRE, filename) !== null;
454    }
455  }
456  return false;
457}
458
459function once(callback) {
460  let called = false;
461  return function(...args) {
462    if (called) return;
463    called = true;
464    return ReflectApply(callback, this, args);
465  };
466}
467
468let validateUint32;
469
470function sleep(msec) {
471  // Lazy-load to avoid a circular dependency.
472  if (validateUint32 === undefined)
473    ({ validateUint32 } = require('internal/validators'));
474
475  validateUint32(msec, 'msec');
476  _sleep(msec);
477}
478
479function createDeferredPromise() {
480  let resolve;
481  let reject;
482  const promise = new Promise((res, rej) => {
483    resolve = res;
484    reject = rej;
485  });
486
487  return { promise, resolve, reject };
488}
489
490// https://heycam.github.io/webidl/#define-the-operations
491function defineOperation(target, name, method) {
492  ObjectDefineProperty(target, name, {
493    __proto__: null,
494    writable: true,
495    enumerable: true,
496    configurable: true,
497    value: method,
498  });
499}
500
501// https://heycam.github.io/webidl/#es-interfaces
502function exposeInterface(target, name, interfaceObject) {
503  ObjectDefineProperty(target, name, {
504    __proto__: null,
505    writable: true,
506    enumerable: false,
507    configurable: true,
508    value: interfaceObject,
509  });
510}
511
512let _DOMException;
513const lazyDOMExceptionClass = () => {
514  _DOMException ??= internalBinding('messaging').DOMException;
515  return _DOMException;
516};
517
518const lazyDOMException = hideStackFrames((message, name) => {
519  _DOMException ??= internalBinding('messaging').DOMException;
520  return new _DOMException(message, name);
521});
522
523const kEnumerableProperty = ObjectCreate(null);
524kEnumerableProperty.enumerable = true;
525ObjectFreeze(kEnumerableProperty);
526
527const kEmptyObject = ObjectFreeze(ObjectCreate(null));
528
529function filterOwnProperties(source, keys) {
530  const filtered = ObjectCreate(null);
531  for (let i = 0; i < keys.length; i++) {
532    const key = keys[i];
533    if (ObjectPrototypeHasOwnProperty(source, key)) {
534      filtered[key] = source[key];
535    }
536  }
537
538  return filtered;
539}
540
541/**
542 * Mimics `obj[key] = value` but ignoring potential prototype inheritance.
543 * @param {any} obj
544 * @param {string} key
545 * @param {any} value
546 * @returns {any}
547 */
548function setOwnProperty(obj, key, value) {
549  return ObjectDefineProperty(obj, key, {
550    __proto__: null,
551    configurable: true,
552    enumerable: true,
553    value,
554    writable: true,
555  });
556}
557
558let internalGlobal;
559function getInternalGlobal() {
560  if (internalGlobal == null) {
561    // Lazy-load to avoid a circular dependency.
562    const { runInNewContext } = require('vm');
563    internalGlobal = runInNewContext('this', undefined, { contextName: 'internal' });
564  }
565  return internalGlobal;
566}
567
568function SideEffectFreeRegExpPrototypeExec(regex, string) {
569  const { RegExp: RegExpFromAnotherRealm } = getInternalGlobal();
570  return FunctionPrototypeCall(RegExpFromAnotherRealm.prototype.exec, regex, string);
571}
572
573const crossRelmRegexes = new SafeWeakMap();
574function getCrossRelmRegex(regex) {
575  const cached = crossRelmRegexes.get(regex);
576  if (cached) return cached;
577
578  let flagString = '';
579  if (RegExpPrototypeGetHasIndices(regex)) flagString += 'd';
580  if (RegExpPrototypeGetGlobal(regex)) flagString += 'g';
581  if (RegExpPrototypeGetIgnoreCase(regex)) flagString += 'i';
582  if (RegExpPrototypeGetMultiline(regex)) flagString += 'm';
583  if (RegExpPrototypeGetDotAll(regex)) flagString += 's';
584  if (RegExpPrototypeGetUnicode(regex)) flagString += 'u';
585  if (RegExpPrototypeGetSticky(regex)) flagString += 'y';
586
587  const { RegExp: RegExpFromAnotherRealm } = getInternalGlobal();
588  const crossRelmRegex = new RegExpFromAnotherRealm(RegExpPrototypeGetSource(regex), flagString);
589  crossRelmRegexes.set(regex, crossRelmRegex);
590  return crossRelmRegex;
591}
592
593function SideEffectFreeRegExpPrototypeSymbolReplace(regex, string, replacement) {
594  return getCrossRelmRegex(regex)[SymbolReplace](string, replacement);
595}
596
597function SideEffectFreeRegExpPrototypeSymbolSplit(regex, string, limit = undefined) {
598  return getCrossRelmRegex(regex)[SymbolSplit](string, limit);
599}
600
601
602function isArrayBufferDetached(value) {
603  if (ArrayBufferPrototypeGetByteLength(value) === 0) {
604    return _isArrayBufferDetached(value);
605  }
606
607  return false;
608}
609
610// Setup user-facing NODE_V8_COVERAGE environment variable that writes
611// ScriptCoverage objects to a specified directory.
612function setupCoverageHooks(dir) {
613  const cwd = require('internal/process/execution').tryGetCwd();
614  const { resolve } = require('path');
615  const coverageDirectory = resolve(cwd, dir);
616  const { sourceMapCacheToObject } =
617    require('internal/source_map/source_map_cache');
618
619  if (process.features.inspector) {
620    internalBinding('profiler').setCoverageDirectory(coverageDirectory);
621    internalBinding('profiler').setSourceMapCacheGetter(sourceMapCacheToObject);
622  } else {
623    process.emitWarning('The inspector is disabled, ' +
624                        'coverage could not be collected',
625                        'Warning');
626    return '';
627  }
628  return coverageDirectory;
629}
630
631module.exports = {
632  assertCrypto,
633  cachedResult,
634  convertToValidSignal,
635  createClassWrapper,
636  createDeferredPromise,
637  decorateErrorStack,
638  defineOperation,
639  deprecate,
640  emitExperimentalWarning,
641  exposeInterface,
642  filterDuplicateStrings,
643  filterOwnProperties,
644  getConstructorOf,
645  getInternalGlobal,
646  getSystemErrorMap,
647  getSystemErrorName,
648  isArrayBufferDetached,
649  isError,
650  isInsideNodeModules,
651  join,
652  lazyDOMException,
653  lazyDOMExceptionClass,
654  normalizeEncoding,
655  once,
656  promisify,
657  SideEffectFreeRegExpPrototypeExec,
658  SideEffectFreeRegExpPrototypeSymbolReplace,
659  SideEffectFreeRegExpPrototypeSymbolSplit,
660  sleep,
661  spliceOne,
662  setupCoverageHooks,
663  toUSVString,
664  removeColors,
665
666  // Symbol used to customize promisify conversion
667  customPromisifyArgs: kCustomPromisifyArgsSymbol,
668
669  // Symbol used to provide a custom inspect function for an object as an
670  // alternative to using 'inspect'
671  customInspectSymbol: SymbolFor('nodejs.util.inspect.custom'),
672
673  // Used by the buffer module to capture an internal reference to the
674  // default isEncoding implementation, just in case userland overrides it.
675  kIsEncodingSymbol: Symbol('kIsEncodingSymbol'),
676  kVmBreakFirstLineSymbol: Symbol('kVmBreakFirstLineSymbol'),
677
678  kEmptyObject,
679  kEnumerableProperty,
680  setOwnProperty,
681};
682