text: // Copyright 2009 the V8 project authors. All rights reserved.
  // 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.
  
  function testMethodNameInference() {
    function Foo() { }
    Foo.prototype.bar = function () { FAIL; };
    (new Foo).bar();
  }
  
  function testNested() {
    function one() {
      function two() {
        function three() {
          FAIL;
        }
        three();
      }
      two();
    }
    one();
  }
  
  function testArrayNative() {
    [1, 2, 3].map(function () { FAIL; });
  }
  
  function testImplicitConversion() {
    function Nirk() { }
    Nirk.prototype.valueOf = function () { FAIL; };
    return 1 + (new Nirk);
  }
  
  function testEval() {
    eval("function Doo() { FAIL; }; Doo();");
  }
  
  function testNestedEval() {
    var x = "FAIL";
    eval("function Outer() { eval('function Inner() { eval(x); }'); Inner(); }; Outer();");
  }
  
  function testEvalWithSourceURL() {
    eval("function Doo() { FAIL; }; Doo();\n//# sourceURL=res://name");
  }
  
  function testNestedEvalWithSourceURL() {
    var x = "FAIL";
    var innerEval = 'function Inner() { eval(x); }\n//@ sourceURL=res://inner-eval';
    eval("function Outer() { eval(innerEval); Inner(); }; Outer();\n//# sourceURL=res://outer-eval");
  }
  
  function testValue() {
    Number.prototype.causeError = function () { FAIL; };
    (1).causeError();
  }
  
  function testConstructor() {
    function Plonk() { FAIL; }
    new Plonk();
  }
  
  function testRenamedMethod() {
    function a$b$c$d() { return FAIL; }
    function Wookie() { }
    Wookie.prototype.d = a$b$c$d;
    (new Wookie).d();
  }
  
  function testAnonymousMethod() {
    (function () { FAIL }).call([1, 2, 3]);
  }
  
  function testFunctionName() {
    function gen(name, counter) {
      var f = function foo() {
        if (counter === 0) {
          FAIL;
        }
        gen(name, counter - 1)();
      }
      if (counter === 4) {
        Object.defineProperty(f, 'name', {get: function(){ throw 239; }});
      } else if (counter == 3) {
        Object.defineProperty(f, 'name', {value: 'boo' + '_' + counter});
      } else {
        Object.defineProperty(f, 'name', {writable: true});
        if (counter === 2)
          f.name = 42;
        else
          f.name = name + '_' + counter;
      }
      return f;
    }
    gen('foo', 4)();
  }
  
  function testFunctionInferredName() {
    var f = function() {
      FAIL;
    }
    f();
  }
  
  function CustomError(message, stripPoint) {
    this.message = message;
    Error.captureStackTrace(this, stripPoint);
  }
  
  CustomError.prototype.toString = function () {
    return "CustomError: " + this.message;
  };
  
  function testDefaultCustomError() {
    throw new CustomError("hep-hey", undefined);
  }
  
  function testStrippedCustomError() {
    throw new CustomError("hep-hey", CustomError);
  }
  
  MyObj = function() { FAIL; }
  
  MyObjCreator = function() {}
  
  MyObjCreator.prototype.Create = function() {
    return new MyObj();
  }
  
  function testClassNames() {
    (new MyObjCreator).Create();
  }
  
  // Utility function for testing that the expected strings occur
  // in the stack trace produced when running the given function.
  function testTrace(name, fun, expected, unexpected) {
    var threw = false;
    try {
      fun();
    } catch (e) {
      for (var i = 0; i < expected.length; i++) {
        assertTrue(e.stack.indexOf(expected[i]) != -1,
                   name + " doesn't contain expected[" + i + "] stack = " + e.stack);
      }
      if (unexpected) {
        for (var i = 0; i < unexpected.length; i++) {
          assertEquals(e.stack.indexOf(unexpected[i]), -1,
                       name + " contains unexpected[" + i + "]");
        }
      }
      threw = true;
    }
    assertTrue(threw, name + " didn't throw");
  }
  
  // Test that the error constructor is not shown in the trace
  function testCallerCensorship() {
    var threw = false;
    try {
      FAIL;
    } catch (e) {
      assertEquals(-1, e.stack.indexOf('at new ReferenceError'),
                   "CallerCensorship contained new ReferenceError");
      threw = true;
    }
    assertTrue(threw, "CallerCensorship didn't throw");
  }
  
  // Test that the explicit constructor call is shown in the trace
  function testUnintendedCallerCensorship() {
    var threw = false;
    try {
      new ReferenceError({
        toString: function () {
          FAIL;
        }
      });
    } catch (e) {
      assertTrue(e.stack.indexOf('at new ReferenceError') != -1,
                 "UnintendedCallerCensorship didn't contain new ReferenceError");
      threw = true;
    }
    assertTrue(threw, "UnintendedCallerCensorship didn't throw");
  }
  
  // If an error occurs while the stack trace is being formatted it should
  // be handled gracefully.
  function testErrorsDuringFormatting() {
    function Nasty() { }
    Nasty.prototype.foo = function () { throw new RangeError(); };
    var n = new Nasty();
    n.__defineGetter__('constructor', function () { CONS_FAIL; });
    assertThrows(()=>n.foo(), RangeError);
    // Now we can't even format the message saying that we couldn't format
    // the stack frame.  Put that in your pipe and smoke it!
    ReferenceError.prototype.toString = function () { NESTED_FAIL; };
    assertThrows(()=>n.foo(), RangeError);
  }
  
  
  // Poisonous object that throws a reference error if attempted converted to
  // a primitive values.
  var thrower = { valueOf: function() { FAIL; },
                  toString: function() { FAIL; } };
  
  // Tests that a native constructor function is included in the
  // stack trace.
  function testTraceNativeConstructor(nativeFunc) {
    var nativeFuncName = nativeFunc.name;
    try {
      new nativeFunc(thrower);
      assertUnreachable(nativeFuncName);
    } catch (e) {
      assertTrue(e.stack.indexOf(nativeFuncName) >= 0, nativeFuncName);
    }
  }
  
  // Tests that a native conversion function is included in the
  // stack trace.
  function testTraceNativeConversion(nativeFunc) {
    var nativeFuncName = nativeFunc.name;
    try {
      nativeFunc(thrower);
      assertUnreachable(nativeFuncName);
    } catch (e) {
      assertTrue(e.stack.indexOf(nativeFuncName) >= 0, nativeFuncName);
    }
  }
  
  
  function testOmittedBuiltin(throwing, omitted) {
    var reached = false;
    try {
      throwing();
      reached = true;
    } catch (e) {
      assertTrue(e.stack.indexOf(omitted) < 0, omitted);
    } finally {
      assertFalse(reached);
    }
  }
  
  
  testTrace("testArrayNative", testArrayNative, ["Array.map"]);
  testTrace("testNested", testNested, ["at one", "at two", "at three"]);
  testTrace("testMethodNameInference", testMethodNameInference, ["at Foo.bar"]);
  testTrace("testImplicitConversion", testImplicitConversion, ["at Nirk.valueOf"]);
  testTrace("testEval", testEval, ["at Doo (eval at testEval"]);
  testTrace("testNestedEval", testNestedEval, ["eval at Inner (eval at Outer"]);
  testTrace("testEvalWithSourceURL", testEvalWithSourceURL,
      [ "at Doo (res://name:1:18)" ]);
  testTrace("testNestedEvalWithSourceURL", testNestedEvalWithSourceURL,
      [" at Inner (res://inner-eval:1:20)",
       " at Outer (res://outer-eval:1:37)"]);
  testTrace("testValue", testValue, ["at Number.causeError"]);
  testTrace("testConstructor", testConstructor, ["new Plonk"]);
  testTrace("testRenamedMethod", testRenamedMethod, ["Wookie.a$b$c$d [as d]"]);
  testTrace("testAnonymousMethod", testAnonymousMethod, ["Array.<anonymous>"]);
  testTrace("testFunctionName", testFunctionName,
      [" at foo_0 ", " at foo_1", " at foo ", " at boo_3 ", " at foo "]);
  testTrace("testFunctionInferredName", testFunctionInferredName, [" at f "]);
  testTrace("testDefaultCustomError", testDefaultCustomError,
      ["hep-hey", "new CustomError"],
      ["collectStackTrace"]);
  testTrace("testStrippedCustomError", testStrippedCustomError, ["hep-hey"],
      ["new CustomError", "collectStackTrace"]);
  testTrace("testClassNames", testClassNames,
            ["new MyObj", "MyObjCreator.Create"], ["as Create"]);
  testCallerCensorship();
  testUnintendedCallerCensorship();
  testErrorsDuringFormatting();
  
  testTraceNativeConversion(String);  // Does ToString on argument.
  testTraceNativeConversion(RegExp);  // Does ToString on argument.
  
  testTraceNativeConstructor(String);  // Does ToString on argument.
  testTraceNativeConstructor(RegExp);  // Does ToString on argument.
  
  // Omitted because QuickSort has builtins object as receiver, and is non-native
  // builtin.
  testOmittedBuiltin(function(){ [thrower, 2].sort(function (a,b) {
                                                       (b < a) - (a < b); });
                     }, "QuickSort");
  
  var reached = false;
  var error = new Error();
  error.toString = function() { reached = true; };
  error.stack;
  assertFalse(reached);
  
  reached = false;
  error = new Error();
  Array.prototype.push = function(x) { reached = true; };
  Array.prototype.join = function(x) { reached = true; };
  error.stack;
  assertFalse(reached);
  
  var fired = false;
  error = new Error({ toString: function() { fired = true; } });
  assertTrue(fired);
  error.stack;
  assertTrue(fired);
  
  // Check that throwing exception in a custom stack trace formatting function
  // does not lead to recursion.
  Error.prepareStackTrace = function() { throw new Error("abc"); };
  var message;
  try {
    try {
      throw new Error();
    } catch (e) {
      e.stack;
    }
  } catch (e) {
    message = e.message;
  }
  
  assertEquals("abc", message);
  
  // Test that modifying Error.prepareStackTrace by itself works.
  Error.prepareStackTrace = function() { Error.prepareStackTrace = "custom"; };
  new Error().stack;
  
  assertEquals("custom", Error.prepareStackTrace);
  
  // Check that the formatted stack trace can be set to undefined.
  error = new Error();
  error.stack = undefined;
  assertEquals(undefined, error.stack);
  
  // Check that the stack trace accessors are not forcibly set.
  var my_error = {};
  Object.freeze(my_error);
  assertThrows(function() { Error.captureStackTrace(my_error); });
  
  my_error = {};
  Object.preventExtensions(my_error);
  assertThrows(function() { Error.captureStackTrace(my_error); });
  
  var fake_error = {};
  my_error = new Error();
  var stolen_getter = Object.getOwnPropertyDescriptor(my_error, 'stack').get;
  Object.defineProperty(fake_error, 'stack', { get: stolen_getter });
  assertEquals(undefined, fake_error.stack);
  
  // Check that overwriting the stack property during stack trace formatting
  // does not crash.
  error = new Error();
  error.__defineGetter__("name", function() { error.stack = "abc"; });
  assertEquals("abc", error.stack);
  
  error = new Error();
  error.__defineGetter__("name", function() { delete error.stack; });
  assertEquals(undefined, error.stack);
  
  // Check that repeated trace collection does not crash.
  error = new Error();
  Error.captureStackTrace(error);
  
  // Check property descriptor.
  var o = {};
  Error.captureStackTrace(o);
  assertEquals([], Object.keys(o));
  var desc = Object.getOwnPropertyDescriptor(o, "stack");
  assertFalse(desc.enumerable);
  assertTrue(desc.configurable);
  assertTrue(desc.writable);
  
  // Check that exceptions thrown within prepareStackTrace throws an exception.
  Error.prepareStackTrace = function(e, frames) { throw 42; }
  
  var x = {}
  assertThrows(() => Error.captureStackTrace(x));
  
  // Check that we don't crash when CaptureSimpleStackTrace returns undefined.
  var o = {};
  var oldStackTraceLimit = Error.stackTraceLimit;
  Error.stackTraceLimit = "not a number";
  Error.captureStackTrace(o);
  Error.stackTraceLimit = oldStackTraceLimit;
  
  // Check that we don't crash when a callsite's function's script is empty.
  Error.prepareStackTrace = function(e, frames) {
    assertEquals(undefined, frames[0].getEvalOrigin());
  }
  try {
    DataView();
    assertUnreachable();
  } catch (e) {
    assertEquals(undefined, e.stack);
  }
  
  // Check that a tight recursion in prepareStackTrace throws when accessing
  // stack. Trying again without a custom formatting function formats correctly.
  var err = new Error("abc");
  Error.prepareStackTrace = () => Error.prepareStackTrace();
  try {
    err.stack;
    assertUnreachable();
  } catch (e) {
    err = e;
  }
  
  Error.prepareStackTrace = undefined;
  assertTrue(
      err.stack.indexOf("RangeError: Maximum call stack size exceeded") != -1);
  assertTrue(err.stack.indexOf("prepareStackTrace") != -1);
  
  // Check that the callsite constructor throws.
  
  Error.prepareStackTrace = (e,s) => s;
  var constructor = new Error().stack[0].constructor;
  
  assertThrows(() => constructor.call());
  assertThrows(() => constructor.call(
      null, {}, () => undefined, {valueOf() { return 0 }}, false));
  