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