1// Copyright 2009 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28function testMethodNameInference() {
29  function Foo() { }
30  Foo.prototype.bar = function () { FAIL; };
31  (new Foo).bar();
32}
33
34function testNested() {
35  function one() {
36    function two() {
37      function three() {
38        FAIL;
39      }
40      three();
41    }
42    two();
43  }
44  one();
45}
46
47function testArrayNative() {
48  [1, 2, 3].map(function () { FAIL; });
49}
50
51function testImplicitConversion() {
52  function Nirk() { }
53  Nirk.prototype.valueOf = function () { FAIL; };
54  return 1 + (new Nirk);
55}
56
57function testEval() {
58  eval("function Doo() { FAIL; }; Doo();");
59}
60
61function testNestedEval() {
62  var x = "FAIL";
63  eval("function Outer() { eval('function Inner() { eval(x); }'); Inner(); }; Outer();");
64}
65
66function testValue() {
67  Number.prototype.causeError = function () { FAIL; };
68  (1).causeError();
69}
70
71function testConstructor() {
72  function Plonk() { FAIL; }
73  new Plonk();
74}
75
76function testRenamedMethod() {
77  function a$b$c$d() { return FAIL; }
78  function Wookie() { }
79  Wookie.prototype.d = a$b$c$d;
80  (new Wookie).d();
81}
82
83function testAnonymousMethod() {
84  (function () { FAIL }).call([1, 2, 3]);
85}
86
87function CustomError(message, stripPoint) {
88  this.message = message;
89  Error.captureStackTrace(this, stripPoint);
90}
91
92CustomError.prototype.toString = function () {
93  return "CustomError: " + this.message;
94};
95
96function testDefaultCustomError() {
97  throw new CustomError("hep-hey", undefined);
98}
99
100function testStrippedCustomError() {
101  throw new CustomError("hep-hey", CustomError);
102}
103
104// Utility function for testing that the expected strings occur
105// in the stack trace produced when running the given function.
106function testTrace(name, fun, expected, unexpected) {
107  var threw = false;
108  try {
109    fun();
110  } catch (e) {
111    for (var i = 0; i < expected.length; i++) {
112      assertTrue(e.stack.indexOf(expected[i]) != -1,
113                 name + " doesn't contain expected[" + i + "]");
114    }
115    if (unexpected) {
116      for (var i = 0; i < unexpected.length; i++) {
117        assertEquals(e.stack.indexOf(unexpected[i]), -1,
118                     name + " contains unexpected[" + i + "]");
119      }
120    }
121    threw = true;
122  }
123  assertTrue(threw, name + " didn't throw");
124}
125
126// Test that the error constructor is not shown in the trace
127function testCallerCensorship() {
128  var threw = false;
129  try {
130    FAIL;
131  } catch (e) {
132    assertEquals(-1, e.stack.indexOf('at new ReferenceError'),
133                 "CallerCensorship contained new ReferenceError");
134    threw = true;
135  }
136  assertTrue(threw, "CallerCensorship didn't throw");
137}
138
139// Test that the explicit constructor call is shown in the trace
140function testUnintendedCallerCensorship() {
141  var threw = false;
142  try {
143    new ReferenceError({
144      toString: function () {
145        FAIL;
146      }
147    });
148  } catch (e) {
149    assertTrue(e.stack.indexOf('at new ReferenceError') != -1,
150               "UnintendedCallerCensorship didn't contain new ReferenceError");
151    threw = true;
152  }
153  assertTrue(threw, "UnintendedCallerCensorship didn't throw");
154}
155
156// If an error occurs while the stack trace is being formatted it should
157// be handled gracefully.
158function testErrorsDuringFormatting() {
159  function Nasty() { }
160  Nasty.prototype.foo = function () { throw new RangeError(); };
161  var n = new Nasty();
162  n.__defineGetter__('constructor', function () { CONS_FAIL; });
163  var threw = false;
164  try {
165    n.foo();
166  } catch (e) {
167    threw = true;
168    assertTrue(e.stack.indexOf('<error: ReferenceError') != -1,
169               "ErrorsDuringFormatting didn't contain error: ReferenceError");
170  }
171  assertTrue(threw, "ErrorsDuringFormatting didn't throw");
172  threw = false;
173  // Now we can't even format the message saying that we couldn't format
174  // the stack frame.  Put that in your pipe and smoke it!
175  ReferenceError.prototype.toString = function () { NESTED_FAIL; };
176  try {
177    n.foo();
178  } catch (e) {
179    threw = true;
180    assertTrue(e.stack.indexOf('<error>') != -1,
181               "ErrorsDuringFormatting didn't contain <error>");
182  }
183  assertTrue(threw, "ErrorsDuringFormatting didnt' throw (2)");
184}
185
186
187testTrace("testArrayNative", testArrayNative, ["Array.map (native)"]);
188testTrace("testNested", testNested, ["at one", "at two", "at three"]);
189testTrace("testMethodNameInference", testMethodNameInference, ["at Foo.bar"]);
190testTrace("testImplicitConversion", testImplicitConversion, ["at Nirk.valueOf"]);
191testTrace("testEval", testEval, ["at Doo (eval at testEval"]);
192testTrace("testNestedEval", testNestedEval, ["eval at Inner (eval at Outer"]);
193testTrace("testValue", testValue, ["at Number.causeError"]);
194testTrace("testConstructor", testConstructor, ["new Plonk"]);
195testTrace("testRenamedMethod", testRenamedMethod, ["Wookie.a$b$c$d [as d]"]);
196testTrace("testAnonymousMethod", testAnonymousMethod, ["Array.<anonymous>"]);
197testTrace("testDefaultCustomError", testDefaultCustomError,
198    ["hep-hey", "new CustomError"],
199    ["collectStackTrace"]);
200testTrace("testStrippedCustomError", testStrippedCustomError, ["hep-hey"],
201    ["new CustomError", "collectStackTrace"]);
202testCallerCensorship();
203testUnintendedCallerCensorship();
204testErrorsDuringFormatting();
205