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