debug-blockscopes.js revision 69a99ed0b2b2ef69d393c371b03db3a98aaf880e
1// Copyright 2011 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 --harmony-block-scoping 29// The functions used for testing backtraces. They are at the top to make the 30// testing of source line/column easier. 31 32 33// Get the Debug object exposed from the debug context global object. 34Debug = debug.Debug; 35 36var test_name; 37var listener_delegate; 38var listener_called; 39var exception; 40var begin_test_count = 0; 41var end_test_count = 0; 42var break_count = 0; 43 44 45// Debug event listener which delegates. 46function listener(event, exec_state, event_data, data) { 47 try { 48 if (event == Debug.DebugEvent.Break) { 49 break_count++; 50 listener_called = true; 51 listener_delegate(exec_state); 52 } 53 } catch (e) { 54 exception = e; 55 } 56} 57 58// Add the debug event listener. 59Debug.setListener(listener); 60 61 62// Initialize for a new test. 63function BeginTest(name) { 64 test_name = name; 65 listener_delegate = null; 66 listener_called = false; 67 exception = null; 68 begin_test_count++; 69} 70 71 72// Check result of a test. 73function EndTest() { 74 assertTrue(listener_called, "listerner not called for " + test_name); 75 assertNull(exception, test_name); 76 end_test_count++; 77} 78 79 80// Check that the scope chain contains the expected types of scopes. 81function CheckScopeChain(scopes, exec_state) { 82 assertEquals(scopes.length, exec_state.frame().scopeCount()); 83 for (var i = 0; i < scopes.length; i++) { 84 var scope = exec_state.frame().scope(i); 85 assertTrue(scope.isScope()); 86 assertEquals(scopes[i], scope.scopeType()); 87 88 // Check the global object when hitting the global scope. 89 if (scopes[i] == debug.ScopeType.Global) { 90 // Objects don't have same class (one is "global", other is "Object", 91 // so just check the properties directly. 92 assertPropertiesEqual(this, scope.scopeObject().value()); 93 } 94 } 95 96 // Get the debug command processor. 97 var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); 98 99 // Send a scopes request and check the result. 100 var json; 101 var request_json = '{"seq":0,"type":"request","command":"scopes"}'; 102 var response_json = dcp.processDebugJSONRequest(request_json); 103 var response = JSON.parse(response_json); 104 assertEquals(scopes.length, response.body.scopes.length); 105 for (var i = 0; i < scopes.length; i++) { 106 assertEquals(i, response.body.scopes[i].index); 107 assertEquals(scopes[i], response.body.scopes[i].type); 108 if (scopes[i] == debug.ScopeType.Local || 109 scopes[i] == debug.ScopeType.Closure) { 110 assertTrue(response.body.scopes[i].object.ref < 0); 111 } else { 112 assertTrue(response.body.scopes[i].object.ref >= 0); 113 } 114 var found = false; 115 for (var j = 0; j < response.refs.length && !found; j++) { 116 found = response.refs[j].handle == response.body.scopes[i].object.ref; 117 } 118 assertTrue(found, "Scope object " + response.body.scopes[i].object.ref + " not found"); 119 } 120} 121 122// Check that the content of the scope is as expected. For functions just check 123// that there is a function. 124function CheckScopeContent(content, number, exec_state) { 125 var scope = exec_state.frame().scope(number); 126 var count = 0; 127 for (var p in content) { 128 var property_mirror = scope.scopeObject().property(p); 129 if (property_mirror.isUndefined()) { 130 print('property ' + p + ' not found in scope'); 131 } 132 assertFalse(property_mirror.isUndefined(), 'property ' + p + ' not found in scope'); 133 if (typeof(content[p]) === 'function') { 134 assertTrue(property_mirror.value().isFunction()); 135 } else { 136 assertEquals(content[p], property_mirror.value().value(), 'property ' + p + ' has unexpected value'); 137 } 138 count++; 139 } 140 141 // 'arguments' and might be exposed in the local and closure scope. Just 142 // ignore this. 143 var scope_size = scope.scopeObject().properties().length; 144 if (!scope.scopeObject().property('arguments').isUndefined()) { 145 scope_size--; 146 } 147 // Also ignore synthetic variable from catch block. 148 if (!scope.scopeObject().property('.catch-var').isUndefined()) { 149 scope_size--; 150 } 151 // Skip property with empty name. 152 if (!scope.scopeObject().property('').isUndefined()) { 153 scope_size--; 154 } 155 // Also ignore synthetic variable from block scopes. 156 if (!scope.scopeObject().property('.block').isUndefined()) { 157 scope_size--; 158 } 159 160 if (count != scope_size) { 161 print('Names found in scope:'); 162 var names = scope.scopeObject().propertyNames(); 163 for (var i = 0; i < names.length; i++) { 164 print(names[i]); 165 } 166 } 167 assertEquals(count, scope_size); 168 169 // Get the debug command processor. 170 var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); 171 172 // Send a scope request for information on a single scope and check the 173 // result. 174 var request_json = '{"seq":0,"type":"request","command":"scope","arguments":{"number":'; 175 request_json += scope.scopeIndex(); 176 request_json += '}}'; 177 var response_json = dcp.processDebugJSONRequest(request_json); 178 var response = JSON.parse(response_json); 179 assertEquals(scope.scopeType(), response.body.type); 180 assertEquals(number, response.body.index); 181 if (scope.scopeType() == debug.ScopeType.Local || 182 scope.scopeType() == debug.ScopeType.Closure) { 183 assertTrue(response.body.object.ref < 0); 184 } else { 185 assertTrue(response.body.object.ref >= 0); 186 } 187 var found = false; 188 for (var i = 0; i < response.refs.length && !found; i++) { 189 found = response.refs[i].handle == response.body.object.ref; 190 } 191 assertTrue(found, "Scope object " + response.body.object.ref + " not found"); 192} 193 194 195// Simple empty block scope in local scope. 196BeginTest("Local block 1"); 197 198function local_block_1() { 199 { 200 debugger; 201 } 202} 203 204listener_delegate = function(exec_state) { 205 CheckScopeChain([debug.ScopeType.Block, 206 debug.ScopeType.Local, 207 debug.ScopeType.Global], exec_state); 208 CheckScopeContent({}, 0, exec_state); 209 CheckScopeContent({}, 1, exec_state); 210}; 211local_block_1(); 212EndTest(); 213 214 215// Local scope with a parameter. 216BeginTest("Local 2"); 217 218function local_2(a) { 219 { 220 debugger; 221 } 222} 223 224listener_delegate = function(exec_state) { 225 CheckScopeChain([debug.ScopeType.Block, 226 debug.ScopeType.Local, 227 debug.ScopeType.Global], exec_state); 228 CheckScopeContent({a:1}, 1, exec_state); 229}; 230local_2(1); 231EndTest(); 232 233 234// Local scope with a parameter and a local variable. 235BeginTest("Local 3"); 236 237function local_3(a) { 238 let x = 3; 239 debugger; 240} 241 242listener_delegate = function(exec_state) { 243 CheckScopeChain([debug.ScopeType.Local, 244 debug.ScopeType.Global], exec_state); 245 CheckScopeContent({a:1,x:3}, 0, exec_state); 246}; 247local_3(1); 248EndTest(); 249 250 251// Local scope with parameters and local variables. 252BeginTest("Local 4"); 253 254function local_4(a, b) { 255 let x = 3; 256 let y = 4; 257 debugger; 258} 259 260listener_delegate = function(exec_state) { 261 CheckScopeChain([debug.ScopeType.Local, 262 debug.ScopeType.Global], exec_state); 263 CheckScopeContent({a:1,b:2,x:3,y:4}, 0, exec_state); 264}; 265local_4(1, 2); 266EndTest(); 267 268 269// Single empty with block. 270BeginTest("With block 1"); 271 272function with_block_1() { 273 with({}) { 274 debugger; 275 } 276} 277 278listener_delegate = function(exec_state) { 279 CheckScopeChain([debug.ScopeType.Block, 280 debug.ScopeType.With, 281 debug.ScopeType.Local, 282 debug.ScopeType.Global], exec_state); 283 CheckScopeContent({}, 0, exec_state); 284 CheckScopeContent({}, 1, exec_state); 285}; 286with_block_1(); 287EndTest(); 288 289 290// Nested empty with blocks. 291BeginTest("With block 2"); 292 293function with_block_2() { 294 with({}) { 295 with({}) { 296 debugger; 297 } 298 } 299} 300 301listener_delegate = function(exec_state) { 302 CheckScopeChain([debug.ScopeType.Block, 303 debug.ScopeType.With, 304 debug.ScopeType.Block, 305 debug.ScopeType.With, 306 debug.ScopeType.Local, 307 debug.ScopeType.Global], exec_state); 308 CheckScopeContent({}, 0, exec_state); 309 CheckScopeContent({}, 1, exec_state); 310 CheckScopeContent({}, 2, exec_state); 311 CheckScopeContent({}, 3, exec_state); 312}; 313with_block_2(); 314EndTest(); 315 316 317// With block using an in-place object literal. 318BeginTest("With block 3"); 319 320function with_block_3() { 321 with({a:1,b:2}) { 322 debugger; 323 } 324} 325 326listener_delegate = function(exec_state) { 327 CheckScopeChain([debug.ScopeType.Block, 328 debug.ScopeType.With, 329 debug.ScopeType.Local, 330 debug.ScopeType.Global], exec_state); 331 CheckScopeContent({}, 0, exec_state); 332 CheckScopeContent({a:1,b:2}, 1, exec_state); 333}; 334with_block_3(); 335EndTest(); 336 337 338// Nested with blocks using in-place object literals. 339BeginTest("With block 4"); 340 341function with_block_4() { 342 with({a:1,b:2}) { 343 with({a:2,b:1}) { 344 debugger; 345 } 346 } 347} 348 349listener_delegate = function(exec_state) { 350 CheckScopeChain([debug.ScopeType.Block, 351 debug.ScopeType.With, 352 debug.ScopeType.Block, 353 debug.ScopeType.With, 354 debug.ScopeType.Local, 355 debug.ScopeType.Global], exec_state); 356 CheckScopeContent({a:2,b:1}, 1, exec_state); 357 CheckScopeContent({a:1,b:2}, 3, exec_state); 358}; 359with_block_4(); 360EndTest(); 361 362 363// Simple closure formed by returning an inner function referering to an outer 364// block local variable and an outer function's parameter. 365BeginTest("Closure 1"); 366 367function closure_1(a) { 368 var x = 2; 369 let y = 3; 370 if (true) { 371 let z = 4; 372 function f() { 373 debugger; 374 return a + x + y + z; 375 }; 376 return f; 377 } 378} 379 380listener_delegate = function(exec_state) { 381 CheckScopeChain([debug.ScopeType.Local, 382 debug.ScopeType.Block, 383 debug.ScopeType.Closure, 384 debug.ScopeType.Global], exec_state); 385 CheckScopeContent({}, 0, exec_state); 386 CheckScopeContent({a:1,x:2,y:3}, 2, exec_state); 387}; 388closure_1(1)(); 389EndTest(); 390