mjsunit.js 25.8 KB
Newer Older
1
// Copyright 2008 the V8 project authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

28 29
function MjsUnitAssertionError(message) {
  this.message = message;
30 31 32 33 34 35 36 37 38 39
  // Temporarily install a custom stack trace formatter and restore the
  // previous value.
  let prevPrepareStackTrace = Error.prepareStackTrace;
  try {
    Error.prepareStackTrace = MjsUnitAssertionError.prepareStackTrace;
    // This allows fetching the stack trace using TryCatch::StackTrace.
    this.stack = new Error("MjsUnitAssertionError").stack;
  } finally {
    Error.prepareStackTrace = prevPrepareStackTrace;
  }
40 41
}

42 43 44 45 46 47
/*
 * This file is included in all mini jsunit test cases.  The test
 * framework expects lines that signal failed tests to start with
 * the f-word and ignore all other lines.
 */

48
MjsUnitAssertionError.prototype.toString = function () {
49
  return this.message + "\n\nStack: " + this.stack;
50 51 52
};


53 54 55 56 57
// Expected and found values the same objects, or the same primitive
// values.
// For known primitive values, please use assertEquals.
var assertSame;

58 59 60
// Inverse of assertSame.
var assertNotSame;

61 62 63 64 65 66
// Expected and found values are identical primitive values or functions
// or similarly structured objects (checking internal properties
// of, e.g., Number and Date objects, the elements of arrays
// and the properties of non-Array objects).
var assertEquals;

67 68 69
// Deep equality predicate used by assertEquals.
var deepEquals;

70 71 72 73 74
// Expected and found values are not identical primitive values or functions
// or similarly structured objects (checking internal properties
// of, e.g., Number and Date objects, the elements of arrays
// and the properties of non-Array objects).
var assertNotEquals;
75 76 77 78

// The difference between expected and found value is within certain tolerance.
var assertEqualsDelta;

79 80 81 82 83 84 85 86 87 88
// The found object is an Array with the same length and elements
// as the expected object. The expected object doesn't need to be an Array,
// as long as it's "array-ish".
var assertArrayEquals;

// The found object must have the same enumerable properties as the
// expected object. The type of object isn't checked.
var assertPropertiesEqual;

// Assert that the string conversion of the found value is equal to
89
// the expected string. Only kept for backwards compatibility, please
90 91 92 93 94 95 96 97 98 99
// check the real structure of the found value.
var assertToStringEquals;

// Checks that the found value is true. Use with boolean expressions
// for tests that doesn't have their own assertXXX function.
var assertTrue;

// Checks that the found value is false.
var assertFalse;

100
// Checks that the found value is null. Kept for historical compatibility,
101 102 103 104 105 106 107 108 109 110 111 112 113
// please just use assertEquals(null, expected).
var assertNull;

// Checks that the found value is *not* null.
var assertNotNull;

// Assert that the passed function or eval code throws an exception.
// The optional second argument is an exception constructor that the
// thrown exception is checked against with "instanceof".
// The optional third argument is a message type string that is compared
// to the type property on the thrown exception.
var assertThrows;

114 115 116 117
// Assert that the passed function throws an exception.
// The exception is checked against the second argument using assertEquals.
var assertThrowsEquals;

118 119 120 121 122 123 124 125 126 127
// Assert that the passed function or eval code does not throw an exception.
var assertDoesNotThrow;

// Asserts that the found value is an instance of the constructor passed
// as the second argument.
var assertInstanceof;

// Assert that this code is never executed (i.e., always fails if executed).
var assertUnreachable;

128
// Assert that the function code is (not) optimized.  If "no sync" is passed
129
// as second argument, we do not wait for the concurrent optimization thread to
130 131 132 133 134
// finish when polling for optimization status.
// Only works with --allow-natives-syntax.
var assertOptimized;
var assertUnoptimized;

135 136 137
// Assert that a string contains another expected substring.
var assertContains;

138 139 140
// Assert that a string matches a given regex.
var assertMatches;

141 142 143 144 145 146 147 148 149
// Assert that a promise resolves or rejects.
// Parameters:
// {promise} - the promise
// {success} - optional - a callback which is called with the result of the
//             resolving promise.
//  {fail} -   optional - a callback which is called with the result of the
//             rejecting promise. If the promise is rejected but no {fail}
//             callback is set, the error is propagated out of the promise
//             chain.
150
var assertPromiseResult;
151

152 153 154
var promiseTestChain;
var promiseTestCount = 0;

155 156 157 158 159 160 161 162
// These bits must be in sync with bits defined in Runtime_GetOptimizationStatus
var V8OptimizationStatus = {
  kIsFunction: 1 << 0,
  kNeverOptimize: 1 << 1,
  kAlwaysOptimize: 1 << 2,
  kMaybeDeopted: 1 << 3,
  kOptimized: 1 << 4,
  kTurboFanned: 1 << 5,
163 164 165 166 167 168
  kInterpreted: 1 << 6,
  kMarkedForOptimization: 1 << 7,
  kMarkedForConcurrentOptimization: 1 << 8,
  kOptimizingConcurrently: 1 << 9,
  kIsExecuting: 1 << 10,
  kTopmostFrameIsTurboFanned: 1 << 11,
169
  kLiteMode: 1 << 12,
170 171
};

172 173 174
// Returns true if --lite-mode is on and we can't ever turn on optimization.
var isNeverOptimizeLiteMode;

175
// Returns true if --no-opt mode is on.
176 177 178 179 180 181 182 183 184 185 186 187 188 189
var isNeverOptimize;

// Returns true if --always-opt mode is on.
var isAlwaysOptimize;

// Returns true if given function in interpreted.
var isInterpreted;

// Returns true if given function is optimized.
var isOptimized;

// Returns true if given function is compiled by TurboFan.
var isTurboFanned;

190 191 192
// Monkey-patchable all-purpose failure handler.
var failWithMessage;

193 194 195
// Returns the formatted failure text.  Used by test-async.js.
var formatFailureText;

196 197
// Returns a pretty-printed string representation of the passed value.
var prettyPrinted;
198

199 200
(function () {  // Scope for utility functions.

201 202 203 204 205 206
  var ObjectPrototypeToString = Object.prototype.toString;
  var NumberPrototypeValueOf = Number.prototype.valueOf;
  var BooleanPrototypeValueOf = Boolean.prototype.valueOf;
  var StringPrototypeValueOf = String.prototype.valueOf;
  var DatePrototypeValueOf = Date.prototype.valueOf;
  var RegExpPrototypeToString = RegExp.prototype.toString;
207
  var ArrayPrototypeForEach = Array.prototype.forEach;
208
  var ArrayPrototypeJoin = Array.prototype.join;
209 210
  var ArrayPrototypeMap = Array.prototype.map;
  var ArrayPrototypePush = Array.prototype.push;
211

212 213 214 215 216 217
  var BigIntPrototypeValueOf;
  // TODO(neis): Remove try-catch once BigInts are enabled by default.
  try {
    BigIntPrototypeValueOf = BigInt.prototype.valueOf;
  } catch(e) {}

218 219
  function classOf(object) {
    // Argument must not be null or undefined.
220
    var string = ObjectPrototypeToString.call(object);
221 222 223
    // String has format [object <ClassName>].
    return string.substring(8, string.length - 1);
  }
224 225


226 227 228 229
  function ValueOf(value) {
    switch (classOf(value)) {
      case "Number":
        return NumberPrototypeValueOf.call(value);
230 231
      case "BigInt":
        return BigIntPrototypeValueOf.call(value);
232 233 234 235 236 237 238 239 240 241 242 243
      case "String":
        return StringPrototypeValueOf.call(value);
      case "Boolean":
        return BooleanPrototypeValueOf.call(value);
      case "Date":
        return DatePrototypeValueOf.call(value);
      default:
        return value;
    }
  }


244
  prettyPrinted = function prettyPrinted(value) {
245 246 247
    switch (typeof value) {
      case "string":
        return JSON.stringify(value);
248 249
      case "bigint":
        return String(value) + "n";
250 251 252 253 254 255
      case "number":
        if (value === 0 && (1 / value) < 0) return "-0";
        // FALLTHROUGH.
      case "boolean":
      case "undefined":
      case "function":
256
      case "symbol":
257 258 259 260 261
        return String(value);
      case "object":
        if (value === null) return "null";
        var objectClass = classOf(value);
        switch (objectClass) {
262
          case "Number":
263
          case "BigInt":
264 265 266
          case "String":
          case "Boolean":
          case "Date":
267
            return objectClass + "(" + prettyPrinted(ValueOf(value)) + ")";
268 269 270
          case "RegExp":
            return RegExpPrototypeToString.call(value);
          case "Array":
271 272
            var mapped = ArrayPrototypeMap.call(
                value, prettyPrintedArrayElement);
273 274
            var joined = ArrayPrototypeJoin.call(mapped, ",");
            return "[" + joined + "]";
275 276 277 278 279 280 281 282 283 284
          case "Uint8Array":
          case "Int8Array":
          case "Int16Array":
          case "Uint16Array":
          case "Uint32Array":
          case "Int32Array":
          case "Float32Array":
          case "Float64Array":
            var joined = ArrayPrototypeJoin.call(value, ",");
            return objectClass + "([" + joined + "])";
285 286 287
          case "Object":
            break;
          default:
288
            return objectClass + "(" + String(value) + ")";
289 290 291 292 293 294 295 296
        }
        // [[Class]] is "Object".
        var name = value.constructor.name;
        if (name) return name + "()";
        return "Object()";
      default:
        return "-- unknown value --";
    }
297 298 299
  }


300
  function prettyPrintedArrayElement(value, index, array) {
301
    if (value === undefined && !(index in array)) return "";
302
    return prettyPrinted(value);
303
  }
304

305

306
  failWithMessage = function failWithMessage(message) {
307 308 309
    throw new MjsUnitAssertionError(message);
  }

310
  formatFailureText = function(expectedText, found, name_opt) {
311
    var message = "Fail" + "ure";
312 313 314
    if (name_opt) {
      // Fix this when we ditch the old test runner.
      message += " (" + name_opt + ")";
315
    }
316

317
    var foundText = prettyPrinted(found);
318 319 320 321 322
    if (expectedText.length <= 40 && foundText.length <= 40) {
      message += ": expected <" + expectedText + "> found <" + foundText + ">";
    } else {
      message += ":\nexpected:\n" + expectedText + "\nfound:\n" + foundText;
    }
323
    return message;
324 325 326 327
  }

  function fail(expectedText, found, name_opt) {
    return failWithMessage(formatFailureText(expectedText, found, name_opt));
328
  }
329 330


331 332 333 334 335 336
  function deepObjectEquals(a, b) {
    var aProps = Object.keys(a);
    aProps.sort();
    var bProps = Object.keys(b);
    bProps.sort();
    if (!deepEquals(aProps, bProps)) {
337
      return false;
338 339 340
    }
    for (var i = 0; i < aProps.length; i++) {
      if (!deepEquals(a[aProps[i]], b[aProps[i]])) {
341 342 343 344 345 346 347
        return false;
      }
    }
    return true;
  }


348
  deepEquals = function deepEquals(a, b) {
349 350 351 352 353
    if (a === b) {
      // Check for -0.
      if (a === 0) return (1 / a) === (1 / b);
      return true;
    }
354 355
    if (typeof a !== typeof b) return false;
    if (typeof a === "number") return isNaN(a) && isNaN(b);
356 357 358 359 360 361
    if (typeof a !== "object" && typeof a !== "function") return false;
    // Neither a nor b is primitive.
    var objectClass = classOf(a);
    if (objectClass !== classOf(b)) return false;
    if (objectClass === "RegExp") {
      // For RegExp, just compare pattern and flags using its toString.
362 363
      return RegExpPrototypeToString.call(a) ===
             RegExpPrototypeToString.call(b);
364 365 366 367 368
    }
    // Functions are only identical to themselves.
    if (objectClass === "Function") return false;
    if (objectClass === "Array") {
      var elementCount = 0;
369
      if (a.length !== b.length) {
370 371 372 373 374 375 376
        return false;
      }
      for (var i = 0; i < a.length; i++) {
        if (!deepEquals(a[i], b[i])) return false;
      }
      return true;
    }
377
    if (objectClass === "String" || objectClass === "Number" ||
378 379
      objectClass === "BigInt" || objectClass === "Boolean" ||
      objectClass === "Date") {
380
      if (ValueOf(a) !== ValueOf(b)) return false;
381 382
    }
    return deepObjectEquals(a, b);
383 384
  }

385
  assertSame = function assertSame(expected, found, name_opt) {
386 387
    // TODO(mstarzinger): We should think about using Harmony's egal operator
    // or the function equivalent Object.is() here.
388
    if (found === expected) {
389
      if (expected !== 0 || (1 / expected) === (1 / found)) return;
390
    } else if ((expected !== expected) && (found !== found)) {
391 392
      return;
    }
393
    fail(prettyPrinted(expected), found, name_opt);
394
  };
395

396 397 398 399 400 401 402 403
  assertNotSame = function assertNotSame(expected, found, name_opt) {
    // TODO(mstarzinger): We should think about using Harmony's egal operator
    // or the function equivalent Object.is() here.
    if (found !== expected) {
      if (expected === 0 || (1 / expected) !== (1 / found)) return;
    } else if (!((expected !== expected) && (found !== found))) {
      return;
    }
404
    fail(prettyPrinted(expected), found, name_opt);
405
  }
406

407 408
  assertEquals = function assertEquals(expected, found, name_opt) {
    if (!deepEquals(found, expected)) {
409
      fail(prettyPrinted(expected), found, name_opt);
410
    }
411
  };
412

413 414
  assertNotEquals = function assertNotEquals(expected, found, name_opt) {
    if (deepEquals(found, expected)) {
415
      fail("not equals to " + prettyPrinted(expected), found, name_opt);
416 417 418
    }
  };

419

420 421
  assertEqualsDelta =
      function assertEqualsDelta(expected, found, delta, name_opt) {
422
    if (Math.abs(expected - found) > delta) {
423
      fail(prettyPrinted(expected) + " +- " + prettyPrinted(delta), found, name_opt);
424
    }
425 426 427
  };


428 429 430 431 432 433
  assertArrayEquals = function assertArrayEquals(expected, found, name_opt) {
    var start = "";
    if (name_opt) {
      start = name_opt + " - ";
    }
    assertEquals(expected.length, found.length, start + "array length");
434
    if (expected.length === found.length) {
435 436 437 438 439 440
      for (var i = 0; i < expected.length; ++i) {
        assertEquals(expected[i], found[i],
                     start + "array element at index " + i);
      }
    }
  };
441 442


443 444 445 446 447 448 449
  assertPropertiesEqual = function assertPropertiesEqual(expected, found,
                                                         name_opt) {
    // Check properties only.
    if (!deepObjectEquals(expected, found)) {
      fail(expected, found, name_opt);
    }
  };
450 451


452 453
  assertToStringEquals = function assertToStringEquals(expected, found,
                                                       name_opt) {
454
    if (expected !== String(found)) {
455 456 457
      fail(expected, found, name_opt);
    }
  };
458 459


460 461 462
  assertTrue = function assertTrue(value, name_opt) {
    assertEquals(true, value, name_opt);
  };
463 464


465 466 467
  assertFalse = function assertFalse(value, name_opt) {
    assertEquals(false, value, name_opt);
  };
468 469


470 471 472
  assertNull = function assertNull(value, name_opt) {
    if (value !== null) {
      fail("null", value, name_opt);
473
    }
474
  };
475 476


477 478 479 480 481
  assertNotNull = function assertNotNull(value, name_opt) {
    if (value === null) {
      fail("not null", value, name_opt);
    }
  };
482 483


484 485
  assertThrows = function assertThrows(code, type_opt, cause_opt) {
    try {
486
      if (typeof code === 'function') {
487 488 489 490 491
        code();
      } else {
        eval(code);
      }
    } catch (e) {
492
      if (typeof type_opt === 'function') {
493
        assertInstanceof(e, type_opt);
494
      } else if (type_opt !== void 0) {
495 496
        failWithMessage(
            'invalid use of assertThrows, maybe you want assertThrowsEquals');
497 498
      }
      if (arguments.length >= 3) {
499 500 501 502 503
        if (cause_opt instanceof RegExp) {
          assertMatches(cause_opt, e.message, "Error message");
        } else {
          assertEquals(cause_opt, e.message, "Error message");
        }
504 505 506
      }
      // Success.
      return;
507
    }
508
    failWithMessage("Did not throw exception");
509
  };
510 511


512 513 514 515
  assertThrowsEquals = function assertThrowsEquals(fun, val) {
    try {
      fun();
    } catch(e) {
516
      assertSame(val, e);
517 518
      return;
    }
519
    failWithMessage("Did not throw exception");
520 521 522
  };


523 524 525
  assertInstanceof = function assertInstanceof(obj, type) {
    if (!(obj instanceof type)) {
      var actualTypeName = null;
526
      var actualConstructor = Object.getPrototypeOf(obj).constructor;
527
      if (typeof actualConstructor === "function") {
528 529
        actualTypeName = actualConstructor.name || String(actualConstructor);
      }
530
      failWithMessage("Object <" + prettyPrinted(obj) + "> is not an instance of <" +
531
               (type.name || type) + ">" +
532
               (actualTypeName ? " but of <" + actualTypeName + ">" : ""));
533 534 535 536 537 538
    }
  };


   assertDoesNotThrow = function assertDoesNotThrow(code, name_opt) {
    try {
539
      if (typeof code === 'function') {
540
        return code();
541
      } else {
542
        return eval(code);
543 544
      }
    } catch (e) {
545
      failWithMessage("threw an exception: " + (e.message || e));
546 547 548 549 550 551 552 553 554
    }
  };

  assertUnreachable = function assertUnreachable(name_opt) {
    // Fix this when we ditch the old test runner.
    var message = "Fail" + "ure: unreachable";
    if (name_opt) {
      message += " - " + name_opt;
    }
555
    failWithMessage(message);
556 557
  };

558 559 560 561 562 563
  assertContains = function(sub, value, name_opt) {
    if (value == null ? (sub != null) : value.indexOf(sub) == -1) {
      fail("contains '" + String(sub) + "'", value, name_opt);
    }
  };

564 565 566 567 568 569 570 571 572
  assertMatches = function(regexp, str, name_opt) {
    if (!(regexp instanceof RegExp)) {
      regexp = new RegExp(regexp);
    }
    if (!str.match(regexp)) {
      fail("should match '" + regexp + "'", str, name_opt);
    }
  };

573 574 575
  function concatenateErrors(stack, exception) {
    // If the exception does not contain a stack trace, wrap it in a new Error.
    if (!exception.stack) exception = new Error(exception);
576

577 578 579 580
    // If the exception already provides a special stack trace, we do not modify
    // it.
    if (typeof exception.stack !== 'string') {
      return exception;
581
    }
582 583 584
    exception.stack = stack + '\n\n' + exception.stack;
    return exception;
  }
585

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
  assertPromiseResult = function(promise, success, fail) {
    const stack = (new Error()).stack;

    var test_promise = promise.then(
        result => {
          try {
            if (--promiseTestCount == 0) testRunner.notifyDone();
            if (success) success(result);
          } catch (e) {
            // Use setTimeout to throw the error again to get out of the promise
            // chain.
            setTimeout(_ => {
              throw concatenateErrors(stack, e);
            }, 0);
          }
        },
        result => {
          try {
            if (--promiseTestCount == 0) testRunner.notifyDone();
            if (!fail) throw result;
606
            fail(result);
607 608 609 610 611 612
          } catch (e) {
            // Use setTimeout to throw the error again to get out of the promise
            // chain.
            setTimeout(_ => {
              throw concatenateErrors(stack, e);
            }, 0);
613 614 615 616
          }
        });

    if (!promiseTestChain) promiseTestChain = Promise.resolve();
617 618 619
    // waitUntilDone is idempotent.
    testRunner.waitUntilDone();
    ++promiseTestCount;
620
    return promiseTestChain.then(test_promise);
621 622
  };

623 624
  var OptimizationStatusImpl = undefined;

625
  var OptimizationStatus = function(fun, sync_opt) {
626 627 628 629 630 631 632
    if (OptimizationStatusImpl === undefined) {
      try {
        OptimizationStatusImpl = new Function(
            "fun", "sync", "return %GetOptimizationStatus(fun, sync);");
      } catch (e) {
        throw new Error("natives syntax not allowed");
      }
633
    }
634
    return OptimizationStatusImpl(fun, sync_opt);
635 636
  }

637 638
  assertUnoptimized = function assertUnoptimized(
      fun, sync_opt, name_opt, skip_if_maybe_deopted = true) {
639
    if (sync_opt === undefined) sync_opt = "";
640
    var opt_status = OptimizationStatus(fun, sync_opt);
641
    // Tests that use assertUnoptimized() do not make sense if --always-opt
642 643 644 645
    // option is provided. Such tests must add --no-always-opt to flags comment.
    assertFalse((opt_status & V8OptimizationStatus.kAlwaysOptimize) !== 0,
                "test does not make sense with --always-opt");
    assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0, name_opt);
646 647
    if (skip_if_maybe_deopted &&
        (opt_status & V8OptimizationStatus.kMaybeDeopted) !== 0) {
648 649 650 651 652
      // When --deopt-every-n-times flag is specified it's no longer guaranteed
      // that particular function is still deoptimized, so keep running the test
      // to stress test the deoptimizer.
      return;
    }
653
    assertFalse((opt_status & V8OptimizationStatus.kOptimized) !== 0, name_opt);
654 655
  }

656 657
  assertOptimized = function assertOptimized(
      fun, sync_opt, name_opt, skip_if_maybe_deopted = true) {
658
    if (sync_opt === undefined) sync_opt = "";
659
    var opt_status = OptimizationStatus(fun, sync_opt);
660 661 662 663 664 665
    // Tests that use assertOptimized() do not make sense for Lite mode where
    // optimization is always disabled, explicitly exit the test with a warning.
    if (opt_status & V8OptimizationStatus.kLiteMode) {
      print("Warning: Test uses assertOptimized in Lite mode, skipping test.");
      quit(0);
    }
666 667
    // Tests that use assertOptimized() do not make sense if --no-opt
    // option is provided. Such tests must add --opt to flags comment.
668
    assertFalse((opt_status & V8OptimizationStatus.kNeverOptimize) !== 0,
669
                "test does not make sense with --no-opt");
670
    assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0, name_opt);
671 672
    if (skip_if_maybe_deopted &&
        (opt_status & V8OptimizationStatus.kMaybeDeopted) !== 0) {
673 674 675 676 677 678 679 680
      // When --deopt-every-n-times flag is specified it's no longer guaranteed
      // that particular function is still optimized, so keep running the test
      // to stress test the deoptimizer.
      return;
    }
    assertTrue((opt_status & V8OptimizationStatus.kOptimized) !== 0, name_opt);
  }

681 682 683 684 685
  isNeverOptimizeLiteMode = function isNeverOptimizeLiteMode() {
    var opt_status = OptimizationStatus(undefined, "");
    return (opt_status & V8OptimizationStatus.kLiteMode) !== 0;
  }

686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
  isNeverOptimize = function isNeverOptimize() {
    var opt_status = OptimizationStatus(undefined, "");
    return (opt_status & V8OptimizationStatus.kNeverOptimize) !== 0;
  }

  isAlwaysOptimize = function isAlwaysOptimize() {
    var opt_status = OptimizationStatus(undefined, "");
    return (opt_status & V8OptimizationStatus.kAlwaysOptimize) !== 0;
  }

  isInterpreted = function isInterpreted(fun) {
    var opt_status = OptimizationStatus(fun, "");
    assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0,
               "not a function");
    return (opt_status & V8OptimizationStatus.kOptimized) === 0 &&
           (opt_status & V8OptimizationStatus.kInterpreted) !== 0;
  }

  isOptimized = function isOptimized(fun) {
    var opt_status = OptimizationStatus(fun, "");
    assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0,
               "not a function");
    return (opt_status & V8OptimizationStatus.kOptimized) !== 0;
  }

  isTurboFanned = function isTurboFanned(fun) {
    var opt_status = OptimizationStatus(fun, "");
    assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0,
               "not a function");
    return (opt_status & V8OptimizationStatus.kOptimized) !== 0 &&
           (opt_status & V8OptimizationStatus.kTurboFanned) !== 0;
717 718
  }

719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
  // Custom V8-specific stack trace formatter that is temporarily installed on
  // the Error object.
  MjsUnitAssertionError.prepareStackTrace = function(error, stack) {
    // Trigger default formatting with recursion.
    try {
      // Filter-out all but the first mjsunit frame.
      let filteredStack = [];
      let inMjsunit = true;
      for (let i = 0; i < stack.length; i++) {
        let frame = stack[i];
        if (inMjsunit) {
          let file = frame.getFileName();
          if (!file || !file.endsWith("mjsunit.js")) {
            inMjsunit = false;
            // Push the last mjsunit frame, typically containing the assertion
            // function.
            if (i > 0) ArrayPrototypePush.call(filteredStack, stack[i-1]);
            ArrayPrototypePush.call(filteredStack, stack[i]);
          }
          continue;
        }
        ArrayPrototypePush.call(filteredStack, frame);
      }
      stack = filteredStack;

      // Infer function names and calculate {max_name_length}
      let max_name_length = 0;
      ArrayPrototypeForEach.call(stack, each => {
        let name = each.getFunctionName();
        if (name == null) name = "";
        if (each.isEval()) {
          name = name;
        } else if (each.isConstructor()) {
          name = "new " + name;
        } else if (each.isNative()) {
          name = "native " + name;
        } else if (!each.isToplevel()) {
          name = each.getTypeName() + "." + name;
        }
        each.name = name;
        max_name_length = Math.max(name.length, max_name_length)
      });

      // Format stack frames.
      stack = ArrayPrototypeMap.call(stack, each => {
        let frame = "    at " + each.name.padEnd(max_name_length);
        let fileName = each.getFileName();
        if (each.isEval()) return frame + " " + each.getEvalOrigin();
        frame += " " + (fileName ? fileName : "");
        let line= each.getLineNumber();
        frame += " " + (line ? line : "");
        let column = each.getColumnNumber();
        frame += (column ? ":" + column : "");
        return frame;
      });
      return "" + error.message + "\n" + ArrayPrototypeJoin.call(stack, "\n");
    } catch(e) {};
    return error.stack;
  }
778
})();