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. 31 var 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. 37 function 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. 83 function 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 99 function 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 110 ClosureTestCase.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 119 ClosureTestCase.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 128 ClosureTestCase.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 139 var 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 210 for (var i = 0; i < closure_test_cases.length; i++) { 211 closure_test_cases[i].run_pause_test(); 212 } 213 214 for (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 221 RunPauseTest(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 230 RunPauseTest(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 242 RunPauseTest(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 251 RunPauseTest(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. 262 RunPauseTest(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. 278 RunPauseTest(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 294 assertEquals(true, Debug.TestApi.CommandProcessorResolveValue({value: true})); 295 296 assertSame(null, Debug.TestApi.CommandProcessorResolveValue({type: "null"})); 297 assertSame(undefined, 298 Debug.TestApi.CommandProcessorResolveValue({type: "undefined"})); 299 300 assertSame("123", Debug.TestApi.CommandProcessorResolveValue( 301 {type: "string", stringDescription: "123"})); 302 assertSame(123, Debug.TestApi.CommandProcessorResolveValue( 303 {type: "number", stringDescription: "123"})); 304 305 assertSame(Number, Debug.TestApi.CommandProcessorResolveValue( 306 {handle: Debug.MakeMirror(Number).handle()})); 307 assertSame(RunClosureTest, Debug.TestApi.CommandProcessorResolveValue( 308 {handle: Debug.MakeMirror(RunClosureTest).handle()})); 309