1// Copyright 2012 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
29
30// Get the Debug object exposed from the debug context global object.
31var Debug = debug.Debug;
32
33// Accepts a function/closure 'fun' that must have a debugger statement inside.
34// A variable 'variable_name' must be initialized before debugger statement
35// and returned after the statement. The test will alter variable value when
36// on debugger statement and check that returned value reflects the change.
37function RunPauseTest(scope_number, expected_old_result, variable_name,
38    new_value, expected_new_result, fun) {
39  var actual_old_result = fun();
40  assertEquals(expected_old_result, actual_old_result);
41
42  var listener_delegate;
43  var listener_called = false;
44  var exception = null;
45
46  function listener_delegate(exec_state) {
47    var scope = exec_state.frame(0).scope(scope_number);
48    scope.setVariableValue(variable_name, new_value);
49  }
50
51  function listener(event, exec_state, event_data, data) {
52    try {
53      if (event == Debug.DebugEvent.Break) {
54        listener_called = true;
55        listener_delegate(exec_state);
56      }
57    } catch (e) {
58      exception = e;
59    }
60  }
61
62  // Add the debug event listener.
63  Debug.setListener(listener);
64
65  var actual_new_value;
66  try {
67    actual_new_result = fun();
68  } finally {
69    Debug.setListener(null);
70  }
71
72  if (exception != null) {
73   assertUnreachable("Exception in listener\n" + exception.stack);
74  }
75  assertTrue(listener_called);
76
77  assertEquals(expected_new_result, actual_new_result);
78}
79
80// Accepts a closure 'fun' that returns a variable from it's outer scope.
81// The test changes the value of variable via the handle to function and checks
82// that the return value changed accordingly.
83function RunClosureTest(scope_number, expected_old_result, variable_name,
84    new_value, expected_new_result, fun) {
85  var actual_old_result = fun();
86  assertEquals(expected_old_result, actual_old_result);
87
88  var fun_mirror = Debug.MakeMirror(fun);
89
90  var scope = fun_mirror.scope(scope_number);
91  scope.setVariableValue(variable_name, new_value);
92
93  var actual_new_result = fun();
94
95  assertEquals(expected_new_result, actual_new_result);
96}
97
98
99function ClosureTestCase(scope_index, old_result, variable_name, new_value,
100    new_result, success_expected, factory) {
101  this.scope_index_ = scope_index;
102  this.old_result_ = old_result;
103  this.variable_name_ = variable_name;
104  this.new_value_ = new_value;
105  this.new_result_ = new_result;
106  this.success_expected_ = success_expected;
107  this.factory_ = factory;
108}
109
110ClosureTestCase.prototype.run_pause_test = function() {
111  var th = this;
112  var fun = this.factory_(true);
113  this.run_and_catch_(function() {
114    RunPauseTest(th.scope_index_ + 1, th.old_result_, th.variable_name_,
115        th.new_value_, th.new_result_, fun);
116  });
117}
118
119ClosureTestCase.prototype.run_closure_test = function() {
120  var th = this;
121  var fun = this.factory_(false);
122  this.run_and_catch_(function() {
123    RunClosureTest(th.scope_index_, th.old_result_, th.variable_name_,
124        th.new_value_, th.new_result_, fun);
125  });
126}
127
128ClosureTestCase.prototype.run_and_catch_ = function(runnable) {
129  if (this.success_expected_) {
130    runnable();
131  } else {
132    assertThrows(runnable);
133  }
134}
135
136
137// Test scopes visible from closures.
138
139var closure_test_cases = [
140  new ClosureTestCase(0, 'cat', 'v1', 5, 5, true,
141      function Factory(debug_stop) {
142    var v1 = 'cat';
143    return function() {
144      if (debug_stop) debugger;
145      return v1;
146    }
147  }),
148
149  new ClosureTestCase(0, 4, 't', 7, 9, true, function Factory(debug_stop) {
150    var t = 2;
151    var r = eval("t");
152    return function() {
153      if (debug_stop) debugger;
154      return r + t;
155    }
156  }),
157
158  new ClosureTestCase(0, 6, 't', 10, 13, true, function Factory(debug_stop) {
159    var t = 2;
160    var r = eval("t = 3");
161    return function() {
162      if (debug_stop) debugger;
163      return r + t;
164    }
165  }),
166
167  new ClosureTestCase(0, 17, 's', 'Bird', 'Bird', true,
168      function Factory(debug_stop) {
169    eval("var s = 17");
170    return function() {
171      if (debug_stop) debugger;
172      return s;
173    }
174  }),
175
176  new ClosureTestCase(2, 'capybara', 'foo', 77, 77, true,
177      function Factory(debug_stop) {
178    var foo = "capybara";
179    return (function() {
180      var bar = "fish";
181      try {
182        throw {name: "test exception"};
183      } catch (e) {
184        return function() {
185          if (debug_stop) debugger;
186          bar = "beast";
187          return foo;
188        }
189      }
190    })();
191  }),
192
193  new ClosureTestCase(0, 'AlphaBeta', 'eee', 5, '5Beta', true,
194      function Factory(debug_stop) {
195    var foo = "Beta";
196    return (function() {
197      var bar = "fish";
198      try {
199        throw "Alpha";
200      } catch (eee) {
201        return function() {
202          if (debug_stop) debugger;
203          return eee + foo;
204        }
205      }
206    })();
207  })
208];
209
210for (var i = 0; i < closure_test_cases.length; i++) {
211  closure_test_cases[i].run_pause_test();
212}
213
214for (var i = 0; i < closure_test_cases.length; i++) {
215  closure_test_cases[i].run_closure_test();
216}
217
218
219// Test local scope.
220
221RunPauseTest(0, 'HelloYou', 'u', 'We', 'HelloWe', (function Factory() {
222  return function() {
223    var u = "You";
224    var v = "Hello";
225    debugger;
226    return v + u;
227  }
228})());
229
230RunPauseTest(0, 'Helloworld', 'p', 'GoodBye', 'HelloGoodBye',
231    (function Factory() {
232  function H(p) {
233    var v = "Hello";
234    debugger;
235    return v + p;
236  }
237  return function() {
238    return H("world");
239  }
240})());
241
242RunPauseTest(0, 'mouse', 'v1', 'dog', 'dog', (function Factory() {
243  return function() {
244    var v1 = 'cat';
245    eval("v1 = 'mouse'");
246    debugger;
247    return v1;
248  }
249})());
250
251RunPauseTest(0, 'mouse', 'v1', 'dog', 'dog', (function Factory() {
252  return function() {
253    eval("var v1 = 'mouse'");
254    debugger;
255    return v1;
256  }
257})());
258
259
260// Check that we correctly update local variable that
261// is referenced from an inner closure.
262RunPauseTest(0, 'Blue', 'v', 'Green', 'Green', (function Factory() {
263  return function() {
264    function A() {
265      var v = "Blue";
266      function Inner() {
267        return void v;
268      }
269      debugger;
270      return v;
271    }
272    return A();
273  }
274})());
275
276// Check that we correctly update parameter, that is known to be stored
277// both on stack and in heap.
278RunPauseTest(0, 5, 'p', 2012, 2012, (function Factory() {
279  return function() {
280    function A(p) {
281      function Inner() {
282        return void p;
283      }
284      debugger;
285      return p;
286    }
287    return A(5);
288  }
289})());
290
291
292// Test value description protocol JSON
293
294assertEquals(true, Debug.TestApi.CommandProcessorResolveValue({value: true}));
295
296assertSame(null, Debug.TestApi.CommandProcessorResolveValue({type: "null"}));
297assertSame(undefined,
298    Debug.TestApi.CommandProcessorResolveValue({type: "undefined"}));
299
300assertSame("123", Debug.TestApi.CommandProcessorResolveValue(
301    {type: "string", stringDescription: "123"}));
302assertSame(123, Debug.TestApi.CommandProcessorResolveValue(
303    {type: "number", stringDescription: "123"}));
304
305assertSame(Number, Debug.TestApi.CommandProcessorResolveValue(
306    {handle: Debug.MakeMirror(Number).handle()}));
307assertSame(RunClosureTest, Debug.TestApi.CommandProcessorResolveValue(
308    {handle: Debug.MakeMirror(RunClosureTest).handle()}));
309