1// Copyright 2008 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
28// Flags: --expose-debug-as debug --nocrankshaft
29// Get the Debug object exposed from the debug context global object.
30Debug = debug.Debug
31
32function DebuggerStatement() {
33  debugger;  /*pause*/
34}
35
36function TestCase(fun, frame_number) {
37  var exception = false;
38  var codeSnippet = undefined;
39  var resultPositions = undefined;
40  var step = 0;
41
42  function listener(event, exec_state, event_data, data) {
43    try {
44      if (event == Debug.DebugEvent.Break ||
45          event == Debug.DebugEvent.Exception) {
46        if (step++ > 0) return;
47        assertHasLineMark(/pause/, exec_state.frame(0));
48        assertHasLineMark(/positions/, exec_state.frame(frame_number));
49        var frame = exec_state.frame(frame_number);
50        codeSnippet = frame.sourceLineText();
51        resultPositions = frame.stepInPositions();
52      }
53    } catch (e) {
54      exception = e
55    }
56
57    function assertHasLineMark(mark, frame) {
58        var line = frame.sourceLineText();
59        if (!mark.exec(frame.sourceLineText())) {
60            throw new Error("Line " + line + " should contain mark " + mark);
61        }
62    }
63  }
64
65  Debug.setListener(listener);
66
67  fun();
68
69  Debug.setListener(null);
70
71  assertTrue(!exception, exception);
72
73  var expectedPositions = {};
74  var markPattern = new RegExp("/\\*#\\*/", "g");
75
76  var matchResult;
77  while ( (matchResult = markPattern.exec(codeSnippet)) ) {
78    expectedPositions[matchResult.index] = true;
79  }
80
81  print(codeSnippet);
82
83  var decoratedResult = codeSnippet;
84
85  function replaceStringRange(s, pos, substitute) {
86   return s.substring(0, pos) + substitute +
87       s.substring(pos + substitute.length);
88  }
89
90  var markLength = 5;
91  var unexpectedPositionFound = false;
92
93  for (var i = 0; i < resultPositions.length; i++) {
94    var col = resultPositions[i].position.column - markLength;
95    if (expectedPositions[col]) {
96      delete expectedPositions[col];
97      decoratedResult = replaceStringRange(decoratedResult, col, "*YES*");
98    } else {
99      decoratedResult = replaceStringRange(decoratedResult, col, "!BAD!");
100      unexpectedPositionFound = true;
101    }
102  }
103
104  print(decoratedResult);
105
106  for (var n in expectedPositions) {
107    assertTrue(false, "Some positions are not reported: " + decoratedResult);
108    break;
109  }
110  assertFalse(unexpectedPositionFound, "Found unexpected position: " +
111      decoratedResult);
112}
113
114function TestCaseWithDebugger(fun) {
115  TestCase(fun, 1);
116}
117
118function TestCaseWithBreakpoint(fun, line_number, frame_number) {
119  var breakpointId = Debug.setBreakPoint(fun, line_number);
120  TestCase(fun, frame_number);
121  Debug.clearBreakPoint(breakpointId);
122}
123
124function TestCaseWithException(fun, frame_number) {
125  Debug.setBreakOnException();
126  TestCase(fun, frame_number);
127  Debug.clearBreakOnException();
128}
129
130
131// Test cases.
132
133// Step in position, when the function call that we are standing at is already
134// being executed.
135var fun = function() {
136  function g(p) {
137    throw String(p); /*pause*/
138  }
139  try {
140    var res = [ g(1), /*#*/g(2) ]; /*positions*/
141  } catch (e) {
142  }
143};
144TestCaseWithBreakpoint(fun, 2, 1);
145TestCaseWithException(fun, 1);
146
147
148// Step in position, when the function call that we are standing at is raising
149// an exception.
150var fun = function() {
151  var o = {
152    g: function(p) {
153      throw p;
154    }
155  };
156  try {
157    var res = [ /*#*/f(1), /*#*/g(2) ]; /*pause, positions*/
158  } catch (e) {
159  }
160};
161TestCaseWithException(fun, 0);
162
163
164// Step-in position, when already paused almost on the first call site.
165var fun = function() {
166  function g(p) {
167    throw p;
168  }
169  try {
170    var res = [ /*#*/g(Math.rand), /*#*/g(2) ]; /*pause, positions*/
171  } catch (e) {
172  }
173};
174TestCaseWithBreakpoint(fun, 5, 0);
175
176// Step-in position, when already paused on the first call site.
177var fun = function() {
178  function g() {
179    throw "Debug";
180  }
181  try {
182    var res = [ /*#*/g(), /*#*/g() ]; /*pause, positions*/
183  } catch (e) {
184  }
185};
186TestCaseWithBreakpoint(fun, 5, 0);
187
188
189// Method calls.
190var fun = function() {
191  var data = {
192    a: function() {}
193  };
194  var res = [ DebuggerStatement(), data./*#*/a(), data[/*#*/String("a")]/*#*/(), data["a"]/*#*/(), data.a, data["a"] ]; /*positions*/
195};
196TestCaseWithDebugger(fun);
197
198// Function call on a value.
199var fun = function() {
200  function g(p) {
201      return g;
202  }
203  var res = [ DebuggerStatement(), /*#*/g(2), /*#*/g(2)/*#*/(3), /*#*/g(0)/*#*/(0)/*#*/(g) ]; /*positions*/
204};
205TestCaseWithDebugger(fun);
206
207// Local function call, closure function call,
208// local function construction call.
209var fun = (function(p) {
210  return function() {
211    function f(a, b) {
212    }
213    var res = /*#*/f(DebuggerStatement(), /*#*/p(/*#*/new f())); /*positions*/
214  };
215})(Object);
216TestCaseWithDebugger(fun);
217
218// Global function, global object construction, calls before pause point.
219var fun = (function(p) {
220  return function() {
221    var res = [ Math.abs(new Object()), DebuggerStatement(), Math./*#*/abs(4), /*#*/new Object()./*#*/toString() ]; /*positions*/
222  };
223})(Object);
224TestCaseWithDebugger(fun);
225