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