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