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-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 // TODO(ES6): properly activate extended mode 33 "use strict"; 34 35 // Get the Debug object exposed from the debug context global object. 36 var Debug = debug.Debug; 37 38 var test_name; 39 var listener_delegate; 40 var listener_called; 41 var exception; 42 var begin_test_count = 0; 43 var end_test_count = 0; 44 var break_count = 0; 45 46 47 // Debug event listener which delegates. 48 function listener(event, exec_state, event_data, data) { 49 try { 50 if (event == Debug.DebugEvent.Break) { 51 break_count++; 52 listener_called = true; 53 listener_delegate(exec_state); 54 } 55 } catch (e) { 56 exception = e; 57 } 58 } 59 60 // Add the debug event listener. 61 Debug.setListener(listener); 62 63 64 // Initialize for a new test. 65 function BeginTest(name) { 66 test_name = name; 67 listener_delegate = null; 68 listener_called = false; 69 exception = null; 70 begin_test_count++; 71 } 72 73 74 // Check result of a test. 75 function EndTest() { 76 assertTrue(listener_called, "listerner not called for " + test_name); 77 assertNull(exception, test_name); 78 end_test_count++; 79 } 80 81 var global_object = this; 82 83 // Check that the scope chain contains the expected types of scopes. 84 function CheckScopeChain(scopes, exec_state) { 85 assertEquals(scopes.length, exec_state.frame().scopeCount()); 86 for (var i = 0; i < scopes.length; i++) { 87 var scope = exec_state.frame().scope(i); 88 assertTrue(scope.isScope()); 89 assertEquals(scopes[i], scope.scopeType()); 90 91 // Check the global object when hitting the global scope. 92 if (scopes[i] == debug.ScopeType.Global) { 93 // Objects don't have same class (one is "global", other is "Object", 94 // so just check the properties directly. 95 assertPropertiesEqual(global_object, scope.scopeObject().value()); 96 } 97 } 98 99 // Get the debug command processor. 100 var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); 101 102 // Send a scopes request and check the result. 103 var json; 104 var request_json = '{"seq":0,"type":"request","command":"scopes"}'; 105 var response_json = dcp.processDebugJSONRequest(request_json); 106 var response = JSON.parse(response_json); 107 assertEquals(scopes.length, response.body.scopes.length); 108 for (var i = 0; i < scopes.length; i++) { 109 assertEquals(i, response.body.scopes[i].index); 110 assertEquals(scopes[i], response.body.scopes[i].type); 111 if (scopes[i] == debug.ScopeType.Local || 112 scopes[i] == debug.ScopeType.Closure) { 113 assertTrue(response.body.scopes[i].object.ref < 0); 114 } else { 115 assertTrue(response.body.scopes[i].object.ref >= 0); 116 } 117 var found = false; 118 for (var j = 0; j < response.refs.length && !found; j++) { 119 found = response.refs[j].handle == response.body.scopes[i].object.ref; 120 } 121 assertTrue(found, "Scope object " + response.body.scopes[i].object.ref + " not found"); 122 } 123 } 124 125 // Check that the content of the scope is as expected. For functions just check 126 // that there is a function. 127 function CheckScopeContent(content, number, exec_state) { 128 var scope = exec_state.frame().scope(number); 129 var count = 0; 130 for (var p in content) { 131 var property_mirror = scope.scopeObject().property(p); 132 if (property_mirror.isUndefined()) { 133 print('property ' + p + ' not found in scope'); 134 } 135 assertFalse(property_mirror.isUndefined(), 'property ' + p + ' not found in scope'); 136 if (typeof(content[p]) === 'function') { 137 assertTrue(property_mirror.value().isFunction()); 138 } else { 139 assertEquals(content[p], property_mirror.value().value(), 'property ' + p + ' has unexpected value'); 140 } 141 count++; 142 } 143 144 // 'arguments' and might be exposed in the local and closure scope. Just 145 // ignore this. 146 var scope_size = scope.scopeObject().properties().length; 147 if (!scope.scopeObject().property('arguments').isUndefined()) { 148 scope_size--; 149 } 150 // Also ignore synthetic variable from catch block. 151 if (!scope.scopeObject().property('.catch-var').isUndefined()) { 152 scope_size--; 153 } 154 // Skip property with empty name. 155 if (!scope.scopeObject().property('').isUndefined()) { 156 scope_size--; 157 } 158 // Also ignore synthetic variable from block scopes. 159 if (!scope.scopeObject().property('.block').isUndefined()) { 160 scope_size--; 161 } 162 163 if (count != scope_size) { 164 print('Names found in scope:'); 165 var names = scope.scopeObject().propertyNames(); 166 for (var i = 0; i < names.length; i++) { 167 print(names[i]); 168 } 169 } 170 assertEquals(count, scope_size); 171 172 // Get the debug command processor. 173 var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); 174 175 // Send a scope request for information on a single scope and check the 176 // result. 177 var request_json = '{"seq":0,"type":"request","command":"scope","arguments":{"number":'; 178 request_json += scope.scopeIndex(); 179 request_json += '}}'; 180 var response_json = dcp.processDebugJSONRequest(request_json); 181 var response = JSON.parse(response_json); 182 assertEquals(scope.scopeType(), response.body.type); 183 assertEquals(number, response.body.index); 184 if (scope.scopeType() == debug.ScopeType.Local || 185 scope.scopeType() == debug.ScopeType.Closure) { 186 assertTrue(response.body.object.ref < 0); 187 } else { 188 assertTrue(response.body.object.ref >= 0); 189 } 190 var found = false; 191 for (var i = 0; i < response.refs.length && !found; i++) { 192 found = response.refs[i].handle == response.body.object.ref; 193 } 194 assertTrue(found, "Scope object " + response.body.object.ref + " not found"); 195 } 196 197 198 // Simple empty block scope in local scope. 199 BeginTest("Local block 1"); 200 201 function local_block_1() { 202 { 203 debugger; 204 } 205 } 206 207 listener_delegate = function(exec_state) { 208 CheckScopeChain([debug.ScopeType.Local, 209 debug.ScopeType.Global], exec_state); 210 CheckScopeContent({}, 0, exec_state); 211 }; 212 local_block_1(); 213 EndTest(); 214 215 216 // Simple empty block scope in local scope with a parameter. 217 BeginTest("Local 2"); 218 219 function local_2(a) { 220 { 221 debugger; 222 } 223 } 224 225 listener_delegate = function(exec_state) { 226 CheckScopeChain([debug.ScopeType.Local, 227 debug.ScopeType.Global], exec_state); 228 CheckScopeContent({a:1}, 0, exec_state); 229 }; 230 local_2(1); 231 EndTest(); 232 233 234 // Local scope with a parameter and a local variable. 235 BeginTest("Local 3"); 236 237 function local_3(a) { 238 let x = 3; 239 debugger; 240 } 241 242 listener_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 }; 247 local_3(1); 248 EndTest(); 249 250 251 // Local scope with parameters and local variables. 252 BeginTest("Local 4"); 253 254 function local_4(a, b) { 255 let x = 3; 256 let y = 4; 257 debugger; 258 } 259 260 listener_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 }; 265 local_4(1, 2); 266 EndTest(); 267 268 269 // Single variable in a block scope. 270 BeginTest("Local 5"); 271 272 function local_5(a) { 273 { 274 let x = 5; 275 debugger; 276 } 277 } 278 279 listener_delegate = function(exec_state) { 280 CheckScopeChain([debug.ScopeType.Block, 281 debug.ScopeType.Local, 282 debug.ScopeType.Global], exec_state); 283 CheckScopeContent({x:5}, 0, exec_state); 284 CheckScopeContent({a:1}, 1, exec_state); 285 }; 286 local_5(1); 287 EndTest(); 288 289 290 // Two variables in a block scope. 291 BeginTest("Local 6"); 292 293 function local_6(a) { 294 { 295 let x = 6; 296 let y = 7; 297 debugger; 298 } 299 } 300 301 listener_delegate = function(exec_state) { 302 CheckScopeChain([debug.ScopeType.Block, 303 debug.ScopeType.Local, 304 debug.ScopeType.Global], exec_state); 305 CheckScopeContent({x:6,y:7}, 0, exec_state); 306 CheckScopeContent({a:1}, 1, exec_state); 307 }; 308 local_6(1); 309 EndTest(); 310 311 312 // Two variables in a block scope. 313 BeginTest("Local 7"); 314 315 function local_7(a) { 316 { 317 { 318 let x = 8; 319 debugger; 320 } 321 } 322 } 323 324 listener_delegate = function(exec_state) { 325 CheckScopeChain([debug.ScopeType.Block, 326 debug.ScopeType.Local, 327 debug.ScopeType.Global], exec_state); 328 CheckScopeContent({x:8}, 0, exec_state); 329 CheckScopeContent({a:1}, 1, exec_state); 330 }; 331 local_7(1); 332 EndTest(); 333 334 335 // Simple closure formed by returning an inner function referering to an outer 336 // block local variable and an outer function's parameter. 337 BeginTest("Closure 1"); 338 339 function closure_1(a) { 340 var x = 2; 341 let y = 3; 342 if (true) { 343 let z = 4; 344 function f() { 345 debugger; 346 return a + x + y + z; 347 }; 348 return f; 349 } 350 } 351 352 listener_delegate = function(exec_state) { 353 CheckScopeChain([debug.ScopeType.Local, 354 debug.ScopeType.Block, 355 debug.ScopeType.Closure, 356 debug.ScopeType.Global], exec_state); 357 CheckScopeContent({}, 0, exec_state); 358 CheckScopeContent({a:1,x:2,y:3}, 2, exec_state); 359 }; 360 closure_1(1)(); 361 EndTest(); 362 363 364 // Simple for-in loop over the keys of an object. 365 BeginTest("For loop 1"); 366 367 function for_loop_1() { 368 for (let x in {y:undefined}) { 369 debugger; 370 } 371 } 372 373 listener_delegate = function(exec_state) { 374 CheckScopeChain([debug.ScopeType.Block, 375 debug.ScopeType.Local, 376 debug.ScopeType.Global], exec_state); 377 CheckScopeContent({x:'y'}, 0, exec_state); 378 // The function scope contains a temporary iteration variable. 379 CheckScopeContent({x:'y'}, 1, exec_state); 380 }; 381 for_loop_1(); 382 EndTest(); 383 384 385 // For-in loop over the keys of an object with a block scoped let variable 386 // shadowing the iteration variable. 387 BeginTest("For loop 2"); 388 389 function for_loop_2() { 390 for (let x in {y:undefined}) { 391 let x = 3; 392 debugger; 393 } 394 } 395 396 listener_delegate = function(exec_state) { 397 CheckScopeChain([debug.ScopeType.Block, 398 debug.ScopeType.Block, 399 debug.ScopeType.Local, 400 debug.ScopeType.Global], exec_state); 401 CheckScopeContent({x:3}, 0, exec_state); 402 CheckScopeContent({x:'y'}, 1, exec_state); 403 // The function scope contains a temporary iteration variable. 404 CheckScopeContent({x:'y'}, 2, exec_state); 405 }; 406 for_loop_2(); 407 EndTest(); 408 409 410 // Simple for loop. 411 BeginTest("For loop 3"); 412 413 function for_loop_3() { 414 for (let x = 3; x < 4; ++x) { 415 debugger; 416 } 417 } 418 419 listener_delegate = function(exec_state) { 420 CheckScopeChain([debug.ScopeType.Block, 421 debug.ScopeType.Local, 422 debug.ScopeType.Global], exec_state); 423 CheckScopeContent({x:3}, 0, exec_state); 424 CheckScopeContent({}, 1, exec_state); 425 }; 426 for_loop_3(); 427 EndTest(); 428 429 430 // For loop with a block scoped let variable shadowing the iteration variable. 431 BeginTest("For loop 4"); 432 433 function for_loop_4() { 434 for (let x = 3; x < 4; ++x) { 435 let x = 5; 436 debugger; 437 } 438 } 439 440 listener_delegate = function(exec_state) { 441 CheckScopeChain([debug.ScopeType.Block, 442 debug.ScopeType.Block, 443 debug.ScopeType.Local, 444 debug.ScopeType.Global], exec_state); 445 CheckScopeContent({x:5}, 0, exec_state); 446 CheckScopeContent({x:3}, 1, exec_state); 447 CheckScopeContent({}, 2, exec_state); 448 }; 449 for_loop_4(); 450 EndTest(); 451 452 453 // For loop with two variable declarations. 454 BeginTest("For loop 5"); 455 456 function for_loop_5() { 457 for (let x = 3, y = 5; x < 4; ++x) { 458 debugger; 459 } 460 } 461 462 listener_delegate = function(exec_state) { 463 CheckScopeChain([debug.ScopeType.Block, 464 debug.ScopeType.Local, 465 debug.ScopeType.Global], exec_state); 466 CheckScopeContent({x:3,y:5}, 0, exec_state); 467 CheckScopeContent({}, 1, exec_state); 468 }; 469 for_loop_5(); 470 EndTest(); 471