• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Originally from narwhal.js (http://narwhaljs.org)
2// Copyright (c) 2009 Thomas Robinson <280north.com>
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the 'Software'), to
6// deal in the Software without restriction, including without limitation the
7// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8// sell copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
18// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
19// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21'use strict';
22
23const {
24  ArrayPrototypeIndexOf,
25  ArrayPrototypeJoin,
26  ArrayPrototypePush,
27  ArrayPrototypeShift,
28  ArrayPrototypeSlice,
29  Error,
30  ErrorCaptureStackTrace,
31  FunctionPrototypeBind,
32  NumberIsNaN,
33  ObjectAssign,
34  ObjectIs,
35  ObjectKeys,
36  ObjectPrototypeIsPrototypeOf,
37  ReflectApply,
38  RegExpPrototypeExec,
39  RegExpPrototypeSymbolReplace,
40  SafeMap,
41  String,
42  StringPrototypeCharCodeAt,
43  StringPrototypeIncludes,
44  StringPrototypeIndexOf,
45  StringPrototypeReplace,
46  StringPrototypeSlice,
47  StringPrototypeSplit,
48  StringPrototypeStartsWith,
49} = primordials;
50
51const { Buffer } = require('buffer');
52const {
53  codes: {
54    ERR_AMBIGUOUS_ARGUMENT,
55    ERR_INVALID_ARG_TYPE,
56    ERR_INVALID_ARG_VALUE,
57    ERR_INVALID_RETURN_VALUE,
58    ERR_MISSING_ARGS,
59  },
60  isErrorStackTraceLimitWritable,
61  overrideStackTrace,
62} = require('internal/errors');
63const AssertionError = require('internal/assert/assertion_error');
64const { openSync, closeSync, readSync } = require('fs');
65const { inspect } = require('internal/util/inspect');
66const { isPromise, isRegExp } = require('internal/util/types');
67const { EOL } = require('internal/constants');
68const { BuiltinModule } = require('internal/bootstrap/loaders');
69const { isError } = require('internal/util');
70
71const errorCache = new SafeMap();
72const CallTracker = require('internal/assert/calltracker');
73const {
74  validateFunction,
75} = require('internal/validators');
76
77let isDeepEqual;
78let isDeepStrictEqual;
79let parseExpressionAt;
80let findNodeAround;
81let tokenizer;
82let decoder;
83
84function lazyLoadComparison() {
85  const comparison = require('internal/util/comparisons');
86  isDeepEqual = comparison.isDeepEqual;
87  isDeepStrictEqual = comparison.isDeepStrictEqual;
88}
89
90// Escape control characters but not \n and \t to keep the line breaks and
91// indentation intact.
92// eslint-disable-next-line no-control-regex
93const escapeSequencesRegExp = /[\x00-\x08\x0b\x0c\x0e-\x1f]/g;
94const meta = [
95  '\\u0000', '\\u0001', '\\u0002', '\\u0003', '\\u0004',
96  '\\u0005', '\\u0006', '\\u0007', '\\b', '',
97  '', '\\u000b', '\\f', '', '\\u000e',
98  '\\u000f', '\\u0010', '\\u0011', '\\u0012', '\\u0013',
99  '\\u0014', '\\u0015', '\\u0016', '\\u0017', '\\u0018',
100  '\\u0019', '\\u001a', '\\u001b', '\\u001c', '\\u001d',
101  '\\u001e', '\\u001f',
102];
103
104const escapeFn = (str) => meta[StringPrototypeCharCodeAt(str, 0)];
105
106let warned = false;
107
108// The assert module provides functions that throw
109// AssertionError's when particular conditions are not met. The
110// assert module must conform to the following interface.
111
112const assert = module.exports = ok;
113
114const NO_EXCEPTION_SENTINEL = {};
115
116// All of the following functions must throw an AssertionError
117// when a corresponding condition is not met, with a message that
118// may be undefined if not provided. All assertion methods provide
119// both the actual and expected values to the assertion error for
120// display purposes.
121
122function innerFail(obj) {
123  if (obj.message instanceof Error) throw obj.message;
124
125  throw new AssertionError(obj);
126}
127
128/**
129 * @param {any} actual
130 * @param {any} expected
131 * @param {string | Error} [message]
132 * @param {string} [operator]
133 * @param {Function} [stackStartFn]
134 */
135function fail(actual, expected, message, operator, stackStartFn) {
136  const argsLen = arguments.length;
137
138  let internalMessage = false;
139  if (actual == null && argsLen <= 1) {
140    internalMessage = true;
141    message = 'Failed';
142  } else if (argsLen === 1) {
143    message = actual;
144    actual = undefined;
145  } else {
146    if (warned === false) {
147      warned = true;
148      process.emitWarning(
149        'assert.fail() with more than one argument is deprecated. ' +
150          'Please use assert.strictEqual() instead or only pass a message.',
151        'DeprecationWarning',
152        'DEP0094',
153      );
154    }
155    if (argsLen === 2)
156      operator = '!=';
157  }
158
159  if (message instanceof Error) throw message;
160
161  const errArgs = {
162    actual,
163    expected,
164    operator: operator === undefined ? 'fail' : operator,
165    stackStartFn: stackStartFn || fail,
166    message,
167  };
168  const err = new AssertionError(errArgs);
169  if (internalMessage) {
170    err.generatedMessage = true;
171  }
172  throw err;
173}
174
175assert.fail = fail;
176
177// The AssertionError is defined in internal/error.
178assert.AssertionError = AssertionError;
179
180function findColumn(fd, column, code) {
181  if (code.length > column + 100) {
182    try {
183      return parseCode(code, column);
184    } catch {
185      // End recursion in case no code could be parsed. The expression should
186      // have been found after 2500 characters, so stop trying.
187      if (code.length - column > 2500) {
188        // eslint-disable-next-line no-throw-literal
189        throw null;
190      }
191    }
192  }
193  // Read up to 2500 bytes more than necessary in columns. That way we address
194  // multi byte characters and read enough data to parse the code.
195  const bytesToRead = column - code.length + 2500;
196  const buffer = Buffer.allocUnsafe(bytesToRead);
197  const bytesRead = readSync(fd, buffer, 0, bytesToRead);
198  code += decoder.write(buffer.slice(0, bytesRead));
199  // EOF: fast path.
200  if (bytesRead < bytesToRead) {
201    return parseCode(code, column);
202  }
203  // Read potentially missing code.
204  return findColumn(fd, column, code);
205}
206
207function getCode(fd, line, column) {
208  let bytesRead = 0;
209  if (line === 0) {
210    // Special handle line number one. This is more efficient and simplifies the
211    // rest of the algorithm. Read more than the regular column number in bytes
212    // to prevent multiple reads in case multi byte characters are used.
213    return findColumn(fd, column, '');
214  }
215  let lines = 0;
216  // Prevent blocking the event loop by limiting the maximum amount of
217  // data that may be read.
218  let maxReads = 32; // bytesPerRead * maxReads = 512 KiB
219  const bytesPerRead = 16384;
220  // Use a single buffer up front that is reused until the call site is found.
221  let buffer = Buffer.allocUnsafe(bytesPerRead);
222  while (maxReads-- !== 0) {
223    // Only allocate a new buffer in case the needed line is found. All data
224    // before that can be discarded.
225    buffer = lines < line ? buffer : Buffer.allocUnsafe(bytesPerRead);
226    bytesRead = readSync(fd, buffer, 0, bytesPerRead);
227    // Read the buffer until the required code line is found.
228    for (let i = 0; i < bytesRead; i++) {
229      if (buffer[i] === 10 && ++lines === line) {
230        // If the end of file is reached, directly parse the code and return.
231        if (bytesRead < bytesPerRead) {
232          return parseCode(buffer.toString('utf8', i + 1, bytesRead), column);
233        }
234        // Check if the read code is sufficient or read more until the whole
235        // expression is read. Make sure multi byte characters are preserved
236        // properly by using the decoder.
237        const code = decoder.write(buffer.slice(i + 1, bytesRead));
238        return findColumn(fd, column, code);
239      }
240    }
241  }
242}
243
244function parseCode(code, offset) {
245  // Lazy load acorn.
246  if (parseExpressionAt === undefined) {
247    const Parser = require('internal/deps/acorn/acorn/dist/acorn').Parser;
248    ({ findNodeAround } = require('internal/deps/acorn/acorn-walk/dist/walk'));
249
250    parseExpressionAt = FunctionPrototypeBind(Parser.parseExpressionAt, Parser);
251    tokenizer = FunctionPrototypeBind(Parser.tokenizer, Parser);
252  }
253  let node;
254  let start;
255  // Parse the read code until the correct expression is found.
256  for (const token of tokenizer(code, { ecmaVersion: 'latest' })) {
257    start = token.start;
258    if (start > offset) {
259      // No matching expression found. This could happen if the assert
260      // expression is bigger than the provided buffer.
261      break;
262    }
263    try {
264      node = parseExpressionAt(code, start, { ecmaVersion: 'latest' });
265      // Find the CallExpression in the tree.
266      node = findNodeAround(node, offset, 'CallExpression');
267      if (node?.node.end >= offset) {
268        return [
269          node.node.start,
270          StringPrototypeReplace(StringPrototypeSlice(code,
271                                                      node.node.start, node.node.end),
272                                 escapeSequencesRegExp, escapeFn),
273        ];
274      }
275    // eslint-disable-next-line no-unused-vars
276    } catch (err) {
277      continue;
278    }
279  }
280  // eslint-disable-next-line no-throw-literal
281  throw null;
282}
283
284function getErrMessage(message, fn) {
285  const tmpLimit = Error.stackTraceLimit;
286  const errorStackTraceLimitIsWritable = isErrorStackTraceLimitWritable();
287  // Make sure the limit is set to 1. Otherwise it could fail (<= 0) or it
288  // does to much work.
289  if (errorStackTraceLimitIsWritable) Error.stackTraceLimit = 1;
290  // We only need the stack trace. To minimize the overhead use an object
291  // instead of an error.
292  const err = {};
293  ErrorCaptureStackTrace(err, fn);
294  if (errorStackTraceLimitIsWritable) Error.stackTraceLimit = tmpLimit;
295
296  overrideStackTrace.set(err, (_, stack) => stack);
297  const call = err.stack[0];
298
299  const filename = call.getFileName();
300  const line = call.getLineNumber() - 1;
301  let column = call.getColumnNumber() - 1;
302  let identifier;
303  let code;
304
305  if (filename) {
306    identifier = `${filename}${line}${column}`;
307
308    // Skip Node.js modules!
309    if (StringPrototypeStartsWith(filename, 'node:') &&
310        BuiltinModule.exists(StringPrototypeSlice(filename, 5))) {
311      errorCache.set(identifier, undefined);
312      return;
313    }
314  } else {
315    return message;
316  }
317
318  if (errorCache.has(identifier)) {
319    return errorCache.get(identifier);
320  }
321
322  let fd;
323  try {
324    // Set the stack trace limit to zero. This makes sure unexpected token
325    // errors are handled faster.
326    if (errorStackTraceLimitIsWritable) Error.stackTraceLimit = 0;
327
328    if (filename) {
329      if (decoder === undefined) {
330        const { StringDecoder } = require('string_decoder');
331        decoder = new StringDecoder('utf8');
332      }
333      fd = openSync(filename, 'r', 0o666);
334      // Reset column and message.
335      ({ 0: column, 1: message } = getCode(fd, line, column));
336      // Flush unfinished multi byte characters.
337      decoder.end();
338    } else {
339      for (let i = 0; i < line; i++) {
340        code = StringPrototypeSlice(code,
341                                    StringPrototypeIndexOf(code, '\n') + 1);
342      }
343      ({ 0: column, 1: message } = parseCode(code, column));
344    }
345    // Always normalize indentation, otherwise the message could look weird.
346    if (StringPrototypeIncludes(message, '\n')) {
347      if (EOL === '\r\n') {
348        message = RegExpPrototypeSymbolReplace(/\r\n/g, message, '\n');
349      }
350      const frames = StringPrototypeSplit(message, '\n');
351      message = ArrayPrototypeShift(frames);
352      for (const frame of frames) {
353        let pos = 0;
354        while (pos < column && (frame[pos] === ' ' || frame[pos] === '\t')) {
355          pos++;
356        }
357        message += `\n  ${StringPrototypeSlice(frame, pos)}`;
358      }
359    }
360    message = `The expression evaluated to a falsy value:\n\n  ${message}\n`;
361    // Make sure to always set the cache! No matter if the message is
362    // undefined or not
363    errorCache.set(identifier, message);
364
365    return message;
366  } catch {
367    // Invalidate cache to prevent trying to read this part again.
368    errorCache.set(identifier, undefined);
369  } finally {
370    // Reset limit.
371    if (errorStackTraceLimitIsWritable) Error.stackTraceLimit = tmpLimit;
372    if (fd !== undefined)
373      closeSync(fd);
374  }
375}
376
377function innerOk(fn, argLen, value, message) {
378  if (!value) {
379    let generatedMessage = false;
380
381    if (argLen === 0) {
382      generatedMessage = true;
383      message = 'No value argument passed to `assert.ok()`';
384    } else if (message == null) {
385      generatedMessage = true;
386      message = getErrMessage(message, fn);
387    } else if (message instanceof Error) {
388      throw message;
389    }
390
391    const err = new AssertionError({
392      actual: value,
393      expected: true,
394      message,
395      operator: '==',
396      stackStartFn: fn,
397    });
398    err.generatedMessage = generatedMessage;
399    throw err;
400  }
401}
402
403/**
404 * Pure assertion tests whether a value is truthy, as determined
405 * by !!value.
406 * @param {...any} args
407 * @returns {void}
408 */
409function ok(...args) {
410  innerOk(ok, args.length, ...args);
411}
412assert.ok = ok;
413
414/**
415 * The equality assertion tests shallow, coercive equality with ==.
416 * @param {any} actual
417 * @param {any} expected
418 * @param {string | Error} [message]
419 * @returns {void}
420 */
421/* eslint-disable no-restricted-properties */
422assert.equal = function equal(actual, expected, message) {
423  if (arguments.length < 2) {
424    throw new ERR_MISSING_ARGS('actual', 'expected');
425  }
426  // eslint-disable-next-line eqeqeq
427  if (actual != expected && (!NumberIsNaN(actual) || !NumberIsNaN(expected))) {
428    innerFail({
429      actual,
430      expected,
431      message,
432      operator: '==',
433      stackStartFn: equal,
434    });
435  }
436};
437
438/**
439 * The non-equality assertion tests for whether two objects are not
440 * equal with !=.
441 * @param {any} actual
442 * @param {any} expected
443 * @param {string | Error} [message]
444 * @returns {void}
445 */
446assert.notEqual = function notEqual(actual, expected, message) {
447  if (arguments.length < 2) {
448    throw new ERR_MISSING_ARGS('actual', 'expected');
449  }
450  // eslint-disable-next-line eqeqeq
451  if (actual == expected || (NumberIsNaN(actual) && NumberIsNaN(expected))) {
452    innerFail({
453      actual,
454      expected,
455      message,
456      operator: '!=',
457      stackStartFn: notEqual,
458    });
459  }
460};
461
462/**
463 * The deep equivalence assertion tests a deep equality relation.
464 * @param {any} actual
465 * @param {any} expected
466 * @param {string | Error} [message]
467 * @returns {void}
468 */
469assert.deepEqual = function deepEqual(actual, expected, message) {
470  if (arguments.length < 2) {
471    throw new ERR_MISSING_ARGS('actual', 'expected');
472  }
473  if (isDeepEqual === undefined) lazyLoadComparison();
474  if (!isDeepEqual(actual, expected)) {
475    innerFail({
476      actual,
477      expected,
478      message,
479      operator: 'deepEqual',
480      stackStartFn: deepEqual,
481    });
482  }
483};
484
485/**
486 * The deep non-equivalence assertion tests for any deep inequality.
487 * @param {any} actual
488 * @param {any} expected
489 * @param {string | Error} [message]
490 * @returns {void}
491 */
492assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
493  if (arguments.length < 2) {
494    throw new ERR_MISSING_ARGS('actual', 'expected');
495  }
496  if (isDeepEqual === undefined) lazyLoadComparison();
497  if (isDeepEqual(actual, expected)) {
498    innerFail({
499      actual,
500      expected,
501      message,
502      operator: 'notDeepEqual',
503      stackStartFn: notDeepEqual,
504    });
505  }
506};
507/* eslint-enable */
508
509/**
510 * The deep strict equivalence assertion tests a deep strict equality
511 * relation.
512 * @param {any} actual
513 * @param {any} expected
514 * @param {string | Error} [message]
515 * @returns {void}
516 */
517assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) {
518  if (arguments.length < 2) {
519    throw new ERR_MISSING_ARGS('actual', 'expected');
520  }
521  if (isDeepEqual === undefined) lazyLoadComparison();
522  if (!isDeepStrictEqual(actual, expected)) {
523    innerFail({
524      actual,
525      expected,
526      message,
527      operator: 'deepStrictEqual',
528      stackStartFn: deepStrictEqual,
529    });
530  }
531};
532
533/**
534 * The deep strict non-equivalence assertion tests for any deep strict
535 * inequality.
536 * @param {any} actual
537 * @param {any} expected
538 * @param {string | Error} [message]
539 * @returns {void}
540 */
541assert.notDeepStrictEqual = notDeepStrictEqual;
542function notDeepStrictEqual(actual, expected, message) {
543  if (arguments.length < 2) {
544    throw new ERR_MISSING_ARGS('actual', 'expected');
545  }
546  if (isDeepEqual === undefined) lazyLoadComparison();
547  if (isDeepStrictEqual(actual, expected)) {
548    innerFail({
549      actual,
550      expected,
551      message,
552      operator: 'notDeepStrictEqual',
553      stackStartFn: notDeepStrictEqual,
554    });
555  }
556}
557
558/**
559 * The strict equivalence assertion tests a strict equality relation.
560 * @param {any} actual
561 * @param {any} expected
562 * @param {string | Error} [message]
563 * @returns {void}
564 */
565assert.strictEqual = function strictEqual(actual, expected, message) {
566  if (arguments.length < 2) {
567    throw new ERR_MISSING_ARGS('actual', 'expected');
568  }
569  if (!ObjectIs(actual, expected)) {
570    innerFail({
571      actual,
572      expected,
573      message,
574      operator: 'strictEqual',
575      stackStartFn: strictEqual,
576    });
577  }
578};
579
580/**
581 * The strict non-equivalence assertion tests for any strict inequality.
582 * @param {any} actual
583 * @param {any} expected
584 * @param {string | Error} [message]
585 * @returns {void}
586 */
587assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
588  if (arguments.length < 2) {
589    throw new ERR_MISSING_ARGS('actual', 'expected');
590  }
591  if (ObjectIs(actual, expected)) {
592    innerFail({
593      actual,
594      expected,
595      message,
596      operator: 'notStrictEqual',
597      stackStartFn: notStrictEqual,
598    });
599  }
600};
601
602class Comparison {
603  constructor(obj, keys, actual) {
604    for (const key of keys) {
605      if (key in obj) {
606        if (actual !== undefined &&
607            typeof actual[key] === 'string' &&
608            isRegExp(obj[key]) &&
609            RegExpPrototypeExec(obj[key], actual[key]) !== null) {
610          this[key] = actual[key];
611        } else {
612          this[key] = obj[key];
613        }
614      }
615    }
616  }
617}
618
619function compareExceptionKey(actual, expected, key, message, keys, fn) {
620  if (!(key in actual) || !isDeepStrictEqual(actual[key], expected[key])) {
621    if (!message) {
622      // Create placeholder objects to create a nice output.
623      const a = new Comparison(actual, keys);
624      const b = new Comparison(expected, keys, actual);
625
626      const err = new AssertionError({
627        actual: a,
628        expected: b,
629        operator: 'deepStrictEqual',
630        stackStartFn: fn,
631      });
632      err.actual = actual;
633      err.expected = expected;
634      err.operator = fn.name;
635      throw err;
636    }
637    innerFail({
638      actual,
639      expected,
640      message,
641      operator: fn.name,
642      stackStartFn: fn,
643    });
644  }
645}
646
647function expectedException(actual, expected, message, fn) {
648  let generatedMessage = false;
649  let throwError = false;
650
651  if (typeof expected !== 'function') {
652    // Handle regular expressions.
653    if (isRegExp(expected)) {
654      const str = String(actual);
655      if (RegExpPrototypeExec(expected, str) !== null)
656        return;
657
658      if (!message) {
659        generatedMessage = true;
660        message = 'The input did not match the regular expression ' +
661                  `${inspect(expected)}. Input:\n\n${inspect(str)}\n`;
662      }
663      throwError = true;
664      // Handle primitives properly.
665    } else if (typeof actual !== 'object' || actual === null) {
666      const err = new AssertionError({
667        actual,
668        expected,
669        message,
670        operator: 'deepStrictEqual',
671        stackStartFn: fn,
672      });
673      err.operator = fn.name;
674      throw err;
675    } else {
676      // Handle validation objects.
677      const keys = ObjectKeys(expected);
678      // Special handle errors to make sure the name and the message are
679      // compared as well.
680      if (expected instanceof Error) {
681        ArrayPrototypePush(keys, 'name', 'message');
682      } else if (keys.length === 0) {
683        throw new ERR_INVALID_ARG_VALUE('error',
684                                        expected, 'may not be an empty object');
685      }
686      if (isDeepEqual === undefined) lazyLoadComparison();
687      for (const key of keys) {
688        if (typeof actual[key] === 'string' &&
689            isRegExp(expected[key]) &&
690            RegExpPrototypeExec(expected[key], actual[key]) !== null) {
691          continue;
692        }
693        compareExceptionKey(actual, expected, key, message, keys, fn);
694      }
695      return;
696    }
697  // Guard instanceof against arrow functions as they don't have a prototype.
698  // Check for matching Error classes.
699  } else if (expected.prototype !== undefined && actual instanceof expected) {
700    return;
701  } else if (ObjectPrototypeIsPrototypeOf(Error, expected)) {
702    if (!message) {
703      generatedMessage = true;
704      message = 'The error is expected to be an instance of ' +
705        `"${expected.name}". Received `;
706      if (isError(actual)) {
707        const name = (actual.constructor && actual.constructor.name) ||
708                     actual.name;
709        if (expected.name === name) {
710          message += 'an error with identical name but a different prototype.';
711        } else {
712          message += `"${name}"`;
713        }
714        if (actual.message) {
715          message += `\n\nError message:\n\n${actual.message}`;
716        }
717      } else {
718        message += `"${inspect(actual, { depth: -1 })}"`;
719      }
720    }
721    throwError = true;
722  } else {
723    // Check validation functions return value.
724    const res = ReflectApply(expected, {}, [actual]);
725    if (res !== true) {
726      if (!message) {
727        generatedMessage = true;
728        const name = expected.name ? `"${expected.name}" ` : '';
729        message = `The ${name}validation function is expected to return` +
730          ` "true". Received ${inspect(res)}`;
731
732        if (isError(actual)) {
733          message += `\n\nCaught error:\n\n${actual}`;
734        }
735      }
736      throwError = true;
737    }
738  }
739
740  if (throwError) {
741    const err = new AssertionError({
742      actual,
743      expected,
744      message,
745      operator: fn.name,
746      stackStartFn: fn,
747    });
748    err.generatedMessage = generatedMessage;
749    throw err;
750  }
751}
752
753function getActual(fn) {
754  validateFunction(fn, 'fn');
755  try {
756    fn();
757  } catch (e) {
758    return e;
759  }
760  return NO_EXCEPTION_SENTINEL;
761}
762
763function checkIsPromise(obj) {
764  // Accept native ES6 promises and promises that are implemented in a similar
765  // way. Do not accept thenables that use a function as `obj` and that have no
766  // `catch` handler.
767  return isPromise(obj) ||
768    (obj !== null && typeof obj === 'object' &&
769    typeof obj.then === 'function' &&
770    typeof obj.catch === 'function');
771}
772
773async function waitForActual(promiseFn) {
774  let resultPromise;
775  if (typeof promiseFn === 'function') {
776    // Return a rejected promise if `promiseFn` throws synchronously.
777    resultPromise = promiseFn();
778    // Fail in case no promise is returned.
779    if (!checkIsPromise(resultPromise)) {
780      throw new ERR_INVALID_RETURN_VALUE('instance of Promise',
781                                         'promiseFn', resultPromise);
782    }
783  } else if (checkIsPromise(promiseFn)) {
784    resultPromise = promiseFn;
785  } else {
786    throw new ERR_INVALID_ARG_TYPE(
787      'promiseFn', ['Function', 'Promise'], promiseFn);
788  }
789
790  try {
791    await resultPromise;
792  } catch (e) {
793    return e;
794  }
795  return NO_EXCEPTION_SENTINEL;
796}
797
798function expectsError(stackStartFn, actual, error, message) {
799  if (typeof error === 'string') {
800    if (arguments.length === 4) {
801      throw new ERR_INVALID_ARG_TYPE('error',
802                                     ['Object', 'Error', 'Function', 'RegExp'],
803                                     error);
804    }
805    if (typeof actual === 'object' && actual !== null) {
806      if (actual.message === error) {
807        throw new ERR_AMBIGUOUS_ARGUMENT(
808          'error/message',
809          `The error message "${actual.message}" is identical to the message.`,
810        );
811      }
812    } else if (actual === error) {
813      throw new ERR_AMBIGUOUS_ARGUMENT(
814        'error/message',
815        `The error "${actual}" is identical to the message.`,
816      );
817    }
818    message = error;
819    error = undefined;
820  } else if (error != null &&
821             typeof error !== 'object' &&
822             typeof error !== 'function') {
823    throw new ERR_INVALID_ARG_TYPE('error',
824                                   ['Object', 'Error', 'Function', 'RegExp'],
825                                   error);
826  }
827
828  if (actual === NO_EXCEPTION_SENTINEL) {
829    let details = '';
830    if (error && error.name) {
831      details += ` (${error.name})`;
832    }
833    details += message ? `: ${message}` : '.';
834    const fnType = stackStartFn === assert.rejects ? 'rejection' : 'exception';
835    innerFail({
836      actual: undefined,
837      expected: error,
838      operator: stackStartFn.name,
839      message: `Missing expected ${fnType}${details}`,
840      stackStartFn,
841    });
842  }
843
844  if (!error)
845    return;
846
847  expectedException(actual, error, message, stackStartFn);
848}
849
850function hasMatchingError(actual, expected) {
851  if (typeof expected !== 'function') {
852    if (isRegExp(expected)) {
853      const str = String(actual);
854      return RegExpPrototypeExec(expected, str) !== null;
855    }
856    throw new ERR_INVALID_ARG_TYPE(
857      'expected', ['Function', 'RegExp'], expected,
858    );
859  }
860  // Guard instanceof against arrow functions as they don't have a prototype.
861  if (expected.prototype !== undefined && actual instanceof expected) {
862    return true;
863  }
864  if (ObjectPrototypeIsPrototypeOf(Error, expected)) {
865    return false;
866  }
867  return ReflectApply(expected, {}, [actual]) === true;
868}
869
870function expectsNoError(stackStartFn, actual, error, message) {
871  if (actual === NO_EXCEPTION_SENTINEL)
872    return;
873
874  if (typeof error === 'string') {
875    message = error;
876    error = undefined;
877  }
878
879  if (!error || hasMatchingError(actual, error)) {
880    const details = message ? `: ${message}` : '.';
881    const fnType = stackStartFn === assert.doesNotReject ?
882      'rejection' : 'exception';
883    innerFail({
884      actual,
885      expected: error,
886      operator: stackStartFn.name,
887      message: `Got unwanted ${fnType}${details}\n` +
888               `Actual message: "${actual && actual.message}"`,
889      stackStartFn,
890    });
891  }
892  throw actual;
893}
894
895/**
896 * Expects the function `promiseFn` to throw an error.
897 * @param {() => any} promiseFn
898 * @param {...any} [args]
899 * @returns {void}
900 */
901assert.throws = function throws(promiseFn, ...args) {
902  expectsError(throws, getActual(promiseFn), ...args);
903};
904
905/**
906 * Expects `promiseFn` function or its value to reject.
907 * @param {() => Promise<any>} promiseFn
908 * @param {...any} [args]
909 * @returns {Promise<void>}
910 */
911assert.rejects = async function rejects(promiseFn, ...args) {
912  expectsError(rejects, await waitForActual(promiseFn), ...args);
913};
914
915/**
916 * Asserts that the function `fn` does not throw an error.
917 * @param {() => any} fn
918 * @param {...any} [args]
919 * @returns {void}
920 */
921assert.doesNotThrow = function doesNotThrow(fn, ...args) {
922  expectsNoError(doesNotThrow, getActual(fn), ...args);
923};
924
925/**
926 * Expects `fn` or its value to not reject.
927 * @param {() => Promise<any>} fn
928 * @param {...any} [args]
929 * @returns {Promise<void>}
930 */
931assert.doesNotReject = async function doesNotReject(fn, ...args) {
932  expectsNoError(doesNotReject, await waitForActual(fn), ...args);
933};
934
935/**
936 * Throws `value` if the value is not `null` or `undefined`.
937 * @param {any} err
938 * @returns {void}
939 */
940assert.ifError = function ifError(err) {
941  if (err !== null && err !== undefined) {
942    let message = 'ifError got unwanted exception: ';
943    if (typeof err === 'object' && typeof err.message === 'string') {
944      if (err.message.length === 0 && err.constructor) {
945        message += err.constructor.name;
946      } else {
947        message += err.message;
948      }
949    } else {
950      message += inspect(err);
951    }
952
953    const newErr = new AssertionError({
954      actual: err,
955      expected: null,
956      operator: 'ifError',
957      message,
958      stackStartFn: ifError,
959    });
960
961    // Make sure we actually have a stack trace!
962    const origStack = err.stack;
963
964    if (typeof origStack === 'string') {
965      // This will remove any duplicated frames from the error frames taken
966      // from within `ifError` and add the original error frames to the newly
967      // created ones.
968      const origStackStart = StringPrototypeIndexOf(origStack, '\n    at');
969      if (origStackStart !== -1) {
970        const originalFrames = StringPrototypeSplit(
971          StringPrototypeSlice(origStack, origStackStart + 1),
972          '\n',
973        );
974        // Filter all frames existing in err.stack.
975        let newFrames = StringPrototypeSplit(newErr.stack, '\n');
976        for (const errFrame of originalFrames) {
977          // Find the first occurrence of the frame.
978          const pos = ArrayPrototypeIndexOf(newFrames, errFrame);
979          if (pos !== -1) {
980            // Only keep new frames.
981            newFrames = ArrayPrototypeSlice(newFrames, 0, pos);
982            break;
983          }
984        }
985        const stackStart = ArrayPrototypeJoin(newFrames, '\n');
986        const stackEnd = ArrayPrototypeJoin(originalFrames, '\n');
987        newErr.stack = `${stackStart}\n${stackEnd}`;
988      }
989    }
990
991    throw newErr;
992  }
993};
994
995function internalMatch(string, regexp, message, fn) {
996  if (!isRegExp(regexp)) {
997    throw new ERR_INVALID_ARG_TYPE(
998      'regexp', 'RegExp', regexp,
999    );
1000  }
1001  const match = fn === assert.match;
1002  if (typeof string !== 'string' ||
1003      RegExpPrototypeExec(regexp, string) !== null !== match) {
1004    if (message instanceof Error) {
1005      throw message;
1006    }
1007
1008    const generatedMessage = !message;
1009
1010    // 'The input was expected to not match the regular expression ' +
1011    message = message || (typeof string !== 'string' ?
1012      'The "string" argument must be of type string. Received type ' +
1013        `${typeof string} (${inspect(string)})` :
1014      (match ?
1015        'The input did not match the regular expression ' :
1016        'The input was expected to not match the regular expression ') +
1017          `${inspect(regexp)}. Input:\n\n${inspect(string)}\n`);
1018    const err = new AssertionError({
1019      actual: string,
1020      expected: regexp,
1021      message,
1022      operator: fn.name,
1023      stackStartFn: fn,
1024    });
1025    err.generatedMessage = generatedMessage;
1026    throw err;
1027  }
1028}
1029
1030/**
1031 * Expects the `string` input to match the regular expression.
1032 * @param {string} string
1033 * @param {RegExp} regexp
1034 * @param {string | Error} [message]
1035 * @returns {void}
1036 */
1037assert.match = function match(string, regexp, message) {
1038  internalMatch(string, regexp, message, match);
1039};
1040
1041/**
1042 * Expects the `string` input not to match the regular expression.
1043 * @param {string} string
1044 * @param {RegExp} regexp
1045 * @param {string | Error} [message]
1046 * @returns {void}
1047 */
1048assert.doesNotMatch = function doesNotMatch(string, regexp, message) {
1049  internalMatch(string, regexp, message, doesNotMatch);
1050};
1051
1052assert.CallTracker = CallTracker;
1053
1054/**
1055 * Expose a strict only variant of assert.
1056 * @param {...any} args
1057 * @returns {void}
1058 */
1059function strict(...args) {
1060  innerOk(strict, args.length, ...args);
1061}
1062
1063assert.strict = ObjectAssign(strict, assert, {
1064  equal: assert.strictEqual,
1065  deepEqual: assert.deepStrictEqual,
1066  notEqual: assert.notStrictEqual,
1067  notDeepEqual: assert.notDeepStrictEqual,
1068});
1069
1070assert.strict.strict = assert.strict;
1071