mirror of
				https://github.com/tc39/test262.git
				synced 2025-10-31 03:34:08 +01:00 
			
		
		
		
	sourceRevisionAtLastExport: 33f2fb0e53d135f0ee17cfccd9d993eb2a6f47de targetRevisionAtLastExport: 31340cbd9add103f586d501b0c3354b7b182abc0
		
			
				
	
	
		
			438 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			438 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| // 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; }
 | |
| assertThrows(() => new Error().stack);
 | |
| 
 | |
| // 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));
 |