debug-scopes.js revision 6ded16be15dd865a9b21ea304d5273c8be299c87
1// Copyright 2008 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// 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 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 noew 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 assertEquals(this, scope.scopeObject().value()); 91 } 92 } 93 94 // Get the debug command processor. 95 var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); 96 97 // Send a scopes request and check the result. 98 var json; 99 request_json = '{"seq":0,"type":"request","command":"scopes"}' 100 var response_json = dcp.processDebugJSONRequest(request_json); 101 var response = JSON.parse(response_json); 102 assertEquals(scopes.length, response.body.scopes.length); 103 for (var i = 0; i < scopes.length; i++) { 104 assertEquals(i, response.body.scopes[i].index); 105 assertEquals(scopes[i], response.body.scopes[i].type); 106 if (scopes[i] == debug.ScopeType.Local || 107 scopes[i] == debug.ScopeType.Closure) { 108 assertTrue(response.body.scopes[i].object.ref < 0); 109 } else { 110 assertTrue(response.body.scopes[i].object.ref >= 0); 111 } 112 var found = false; 113 for (var j = 0; j < response.refs.length && !found; j++) { 114 found = response.refs[j].handle == response.body.scopes[i].object.ref; 115 } 116 assertTrue(found, "Scope object " + response.body.scopes[i].object.ref + " not found"); 117 } 118} 119 120 121// Check that the content of the scope is as expected. For functions just check 122// that there is a function. 123function CheckScopeContent(content, number, exec_state) { 124 var scope = exec_state.frame().scope(number) 125 var count = 0; 126 for (var p in content) { 127 var property_mirror = scope.scopeObject().property(p); 128 assertFalse(property_mirror.isUndefined(), 'property ' + p + ' not found in scope'); 129 if (typeof(content[p]) === 'function') { 130 assertTrue(property_mirror.value().isFunction()); 131 } else { 132 assertEquals(content[p], property_mirror.value().value(), 'property ' + p + ' has unexpected value'); 133 } 134 count++; 135 } 136 137 // 'arguments' and might be exposed in the local and closure scope. Just 138 // ignore this. 139 var scope_size = scope.scopeObject().properties().length; 140 if (!scope.scopeObject().property('arguments').isUndefined()) { 141 scope_size--; 142 } 143 // Also ignore synthetic variable from catch block. 144 if (!scope.scopeObject().property('.catch-var').isUndefined()) { 145 scope_size--; 146 } 147 148 if (count != scope_size) { 149 print('Names found in scope:'); 150 var names = scope.scopeObject().propertyNames(); 151 for (var i = 0; i < names.length; i++) { 152 print(names[i]); 153 } 154 } 155 assertEquals(count, scope_size); 156 157 // Get the debug command processor. 158 var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); 159 160 // Send a scope request for information on a single scope and check the 161 // result. 162 request_json = '{"seq":0,"type":"request","command":"scope","arguments":{"number":' 163 request_json += scope.scopeIndex(); 164 request_json += '}}' 165 var response_json = dcp.processDebugJSONRequest(request_json); 166 var response = JSON.parse(response_json); 167 assertEquals(scope.scopeType(), response.body.type); 168 assertEquals(number, response.body.index); 169 if (scope.scopeType() == debug.ScopeType.Local || 170 scope.scopeType() == debug.ScopeType.Closure) { 171 assertTrue(response.body.object.ref < 0); 172 } else { 173 assertTrue(response.body.object.ref >= 0); 174 } 175 var found = false; 176 for (var i = 0; i < response.refs.length && !found; i++) { 177 found = response.refs[i].handle == response.body.object.ref; 178 } 179 assertTrue(found, "Scope object " + response.body.object.ref + " not found"); 180} 181 182 183// Simple empty local scope. 184BeginTest("Local 1"); 185 186function local_1() { 187 debugger; 188} 189 190listener_delegate = function(exec_state) { 191 CheckScopeChain([debug.ScopeType.Local, 192 debug.ScopeType.Global], exec_state); 193 CheckScopeContent({}, 0, exec_state); 194} 195local_1() 196EndTest(); 197 198 199// Local scope with a parameter. 200BeginTest("Local 2"); 201 202function local_2(a) { 203 debugger; 204} 205 206listener_delegate = function(exec_state) { 207 CheckScopeChain([debug.ScopeType.Local, 208 debug.ScopeType.Global], exec_state); 209 CheckScopeContent({a:1}, 0, exec_state); 210} 211local_2(1) 212EndTest(); 213 214 215// Local scope with a parameter and a local variable. 216BeginTest("Local 3"); 217 218function local_3(a) { 219 var x = 3; 220 debugger; 221} 222 223listener_delegate = function(exec_state) { 224 CheckScopeChain([debug.ScopeType.Local, 225 debug.ScopeType.Global], exec_state); 226 CheckScopeContent({a:1,x:3}, 0, exec_state); 227} 228local_3(1) 229EndTest(); 230 231 232// Local scope with parameters and local variables. 233BeginTest("Local 4"); 234 235function local_4(a, b) { 236 var x = 3; 237 var y = 4; 238 debugger; 239} 240 241listener_delegate = function(exec_state) { 242 CheckScopeChain([debug.ScopeType.Local, 243 debug.ScopeType.Global], exec_state); 244 CheckScopeContent({a:1,b:2,x:3,y:4}, 0, exec_state); 245} 246local_4(1, 2) 247EndTest(); 248 249 250// Empty local scope with use of eval. 251BeginTest("Local 5"); 252 253function local_5() { 254 eval(''); 255 debugger; 256} 257 258listener_delegate = function(exec_state) { 259 CheckScopeChain([debug.ScopeType.Local, 260 debug.ScopeType.Global], exec_state); 261 CheckScopeContent({}, 0, exec_state); 262} 263local_5() 264EndTest(); 265 266 267// Local introducing local variable using eval. 268BeginTest("Local 6"); 269 270function local_6() { 271 eval('var i = 5'); 272 debugger; 273} 274 275listener_delegate = function(exec_state) { 276 CheckScopeChain([debug.ScopeType.Local, 277 debug.ScopeType.Global], exec_state); 278 CheckScopeContent({i:5}, 0, exec_state); 279} 280local_6() 281EndTest(); 282 283 284// Local scope with parameters, local variables and local variable introduced 285// using eval. 286BeginTest("Local 7"); 287 288function local_7(a, b) { 289 var x = 3; 290 var y = 4; 291 eval('var i = 5'); 292 eval('var j = 6'); 293 debugger; 294} 295 296listener_delegate = function(exec_state) { 297 CheckScopeChain([debug.ScopeType.Local, 298 debug.ScopeType.Global], exec_state); 299 CheckScopeContent({a:1,b:2,x:3,y:4,i:5,j:6}, 0, exec_state); 300} 301local_7(1, 2) 302EndTest(); 303 304 305// Single empty with block. 306BeginTest("With 1"); 307 308function with_1() { 309 with({}) { 310 debugger; 311 } 312} 313 314listener_delegate = function(exec_state) { 315 CheckScopeChain([debug.ScopeType.With, 316 debug.ScopeType.Local, 317 debug.ScopeType.Global], exec_state); 318 CheckScopeContent({}, 0, exec_state); 319} 320with_1() 321EndTest(); 322 323 324// Nested empty with blocks. 325BeginTest("With 2"); 326 327function with_2() { 328 with({}) { 329 with({}) { 330 debugger; 331 } 332 } 333} 334 335listener_delegate = function(exec_state) { 336 CheckScopeChain([debug.ScopeType.With, 337 debug.ScopeType.With, 338 debug.ScopeType.Local, 339 debug.ScopeType.Global], exec_state); 340 CheckScopeContent({}, 0, exec_state); 341 CheckScopeContent({}, 1, exec_state); 342} 343with_2() 344EndTest(); 345 346 347// With block using an in-place object literal. 348BeginTest("With 3"); 349 350function with_3() { 351 with({a:1,b:2}) { 352 debugger; 353 } 354} 355 356listener_delegate = function(exec_state) { 357 CheckScopeChain([debug.ScopeType.With, 358 debug.ScopeType.Local, 359 debug.ScopeType.Global], exec_state); 360 CheckScopeContent({a:1,b:2}, 0, exec_state); 361} 362with_3() 363EndTest(); 364 365 366// Nested with blocks using in-place object literals. 367BeginTest("With 4"); 368 369function with_4() { 370 with({a:1,b:2}) { 371 with({a:2,b:1}) { 372 debugger; 373 } 374 } 375} 376 377listener_delegate = function(exec_state) { 378 CheckScopeChain([debug.ScopeType.With, 379 debug.ScopeType.With, 380 debug.ScopeType.Local, 381 debug.ScopeType.Global], exec_state); 382 CheckScopeContent({a:2,b:1}, 0, exec_state); 383 CheckScopeContent({a:1,b:2}, 1, exec_state); 384} 385with_4() 386EndTest(); 387 388 389// Nested with blocks using existing object. 390BeginTest("With 5"); 391 392var with_object = {c:3,d:4}; 393function with_5() { 394 with(with_object) { 395 with(with_object) { 396 debugger; 397 } 398 } 399} 400 401listener_delegate = function(exec_state) { 402 CheckScopeChain([debug.ScopeType.With, 403 debug.ScopeType.With, 404 debug.ScopeType.Local, 405 debug.ScopeType.Global], exec_state); 406 CheckScopeContent(with_object, 0, exec_state); 407 CheckScopeContent(with_object, 1, exec_state); 408 assertEquals(exec_state.frame().scope(0).scopeObject(), exec_state.frame().scope(1).scopeObject()); 409 assertEquals(with_object, exec_state.frame().scope(1).scopeObject().value()); 410} 411with_5() 412EndTest(); 413 414 415// Simple closure formed by returning an inner function referering the outer 416// functions arguments. 417BeginTest("Closure 1"); 418 419function closure_1(a) { 420 function f() { 421 debugger; 422 return a; 423 }; 424 return f; 425} 426 427listener_delegate = function(exec_state) { 428 CheckScopeChain([debug.ScopeType.Local, 429 debug.ScopeType.Closure, 430 debug.ScopeType.Global], exec_state); 431 CheckScopeContent({a:1}, 1, exec_state); 432} 433closure_1(1)() 434EndTest(); 435 436 437// Simple closure formed by returning an inner function referering the outer 438// functions arguments. Due to VM optimizations parts of the actual closure is 439// missing from the debugger information. 440BeginTest("Closure 2"); 441 442function closure_2(a, b) { 443 var x = a + 2; 444 var y = b + 2; 445 function f() { 446 debugger; 447 return a + x; 448 }; 449 return f; 450} 451 452listener_delegate = function(exec_state) { 453 CheckScopeChain([debug.ScopeType.Local, 454 debug.ScopeType.Closure, 455 debug.ScopeType.Global], exec_state); 456 CheckScopeContent({a:1,x:3}, 1, exec_state); 457} 458closure_2(1, 2)() 459EndTest(); 460 461 462// Simple closure formed by returning an inner function referering the outer 463// functions arguments. Using all arguments and locals from the outer function 464// in the inner function makes these part of the debugger information on the 465// closure. 466BeginTest("Closure 3"); 467 468function closure_3(a, b) { 469 var x = a + 2; 470 var y = b + 2; 471 function f() { 472 debugger; 473 return a + b + x + y; 474 }; 475 return f; 476} 477 478listener_delegate = function(exec_state) { 479 CheckScopeChain([debug.ScopeType.Local, 480 debug.ScopeType.Closure, 481 debug.ScopeType.Global], exec_state); 482 CheckScopeContent({a:1,b:2,x:3,y:4}, 1, exec_state); 483} 484closure_3(1, 2)() 485EndTest(); 486 487 488 489// Simple closure formed by returning an inner function referering the outer 490// functions arguments. Using all arguments and locals from the outer function 491// in the inner function makes these part of the debugger information on the 492// closure. Use the inner function as well... 493BeginTest("Closure 4"); 494 495function closure_4(a, b) { 496 var x = a + 2; 497 var y = b + 2; 498 function f() { 499 debugger; 500 if (f) { 501 return a + b + x + y; 502 } 503 }; 504 return f; 505} 506 507listener_delegate = function(exec_state) { 508 CheckScopeChain([debug.ScopeType.Local, 509 debug.ScopeType.Closure, 510 debug.ScopeType.Global], exec_state); 511 CheckScopeContent({a:1,b:2,x:3,y:4,f:function(){}}, 1, exec_state); 512} 513closure_4(1, 2)() 514EndTest(); 515 516 517 518// Simple closure formed by returning an inner function referering the outer 519// functions arguments. In the presence of eval all arguments and locals 520// (including the inner function itself) from the outer function becomes part of 521// the debugger infformation on the closure. 522BeginTest("Closure 5"); 523 524function closure_5(a, b) { 525 var x = 3; 526 var y = 4; 527 function f() { 528 eval(''); 529 debugger; 530 return 1; 531 }; 532 return f; 533} 534 535listener_delegate = function(exec_state) { 536 CheckScopeChain([debug.ScopeType.Local, 537 debug.ScopeType.Closure, 538 debug.ScopeType.Global], exec_state); 539 CheckScopeContent({a:1,b:2,x:3,y:4,f:function(){}}, 1, exec_state); 540} 541closure_5(1, 2)() 542EndTest(); 543 544 545// Two closures. Due to optimizations only the parts actually used are provided 546// through the debugger information. 547BeginTest("Closure 6"); 548function closure_6(a, b) { 549 function f(a, b) { 550 var x = 3; 551 var y = 4; 552 return function() { 553 var x = 3; 554 var y = 4; 555 debugger; 556 some_global = a; 557 return f; 558 } 559 } 560 return f(a, b); 561} 562 563listener_delegate = function(exec_state) { 564 CheckScopeChain([debug.ScopeType.Local, 565 debug.ScopeType.Closure, 566 debug.ScopeType.Closure, 567 debug.ScopeType.Global], exec_state); 568 CheckScopeContent({a:1}, 1, exec_state); 569 CheckScopeContent({f:function(){}}, 2, exec_state); 570} 571closure_6(1, 2)() 572EndTest(); 573 574 575// Two closures. In the presence of eval all information is provided as the 576// compiler cannot determine which parts are used. 577BeginTest("Closure 7"); 578function closure_7(a, b) { 579 var x = 3; 580 var y = 4; 581 eval('var i = 5'); 582 eval('var j = 6'); 583 function f(a, b) { 584 var x = 3; 585 var y = 4; 586 eval('var i = 5'); 587 eval('var j = 6'); 588 return function() { 589 debugger; 590 some_global = a; 591 return f; 592 } 593 } 594 return f(a, b); 595} 596 597listener_delegate = function(exec_state) { 598 CheckScopeChain([debug.ScopeType.Local, 599 debug.ScopeType.Closure, 600 debug.ScopeType.Closure, 601 debug.ScopeType.Global], exec_state); 602 CheckScopeContent({}, 0, exec_state); 603 CheckScopeContent({a:1,b:2,x:3,y:4,i:5,j:6}, 1, exec_state); 604 CheckScopeContent({a:1,b:2,x:3,y:4,i:5,j:6,f:function(){}}, 2, exec_state); 605} 606closure_7(1, 2)() 607EndTest(); 608 609 610// Test a mixture of scopes. 611BeginTest("The full monty"); 612function the_full_monty(a, b) { 613 var x = 3; 614 var y = 4; 615 eval('var i = 5'); 616 eval('var j = 6'); 617 function f(a, b) { 618 var x = 9; 619 var y = 10; 620 eval('var i = 11'); 621 eval('var j = 12'); 622 with ({j:13}){ 623 return function() { 624 var x = 14; 625 with ({a:15}) { 626 with ({b:16}) { 627 debugger; 628 some_global = a; 629 return f; 630 } 631 } 632 } 633 } 634 } 635 return f(a, b); 636} 637 638listener_delegate = function(exec_state) { 639 CheckScopeChain([debug.ScopeType.With, 640 debug.ScopeType.With, 641 debug.ScopeType.Local, 642 debug.ScopeType.With, 643 debug.ScopeType.Closure, 644 debug.ScopeType.Closure, 645 debug.ScopeType.Global], exec_state); 646 CheckScopeContent({b:16}, 0, exec_state); 647 CheckScopeContent({a:15}, 1, exec_state); 648 CheckScopeContent({x:14}, 2, exec_state); 649 CheckScopeContent({j:13}, 3, exec_state); 650 CheckScopeContent({a:1,b:2,x:9,y:10,i:11,j:12}, 4, exec_state); 651 CheckScopeContent({a:1,b:2,x:3,y:4,i:5,j:6,f:function(){}}, 5, exec_state); 652} 653the_full_monty(1, 2)() 654EndTest(); 655 656// Test global scope. 657BeginTest("Global"); 658listener_delegate = function(exec_state) { 659 CheckScopeChain([debug.ScopeType.Global], exec_state); 660} 661debugger; 662EndTest(); 663 664 665BeginTest("Catch block 1"); 666function catch_block_1() { 667 try { 668 throw 'Exception'; 669 } catch (e) { 670 debugger; 671 } 672}; 673 674 675listener_delegate = function(exec_state) { 676 CheckScopeChain([debug.ScopeType.Catch, 677 debug.ScopeType.Local, 678 debug.ScopeType.Global], exec_state); 679 CheckScopeContent({e:'Exception'}, 0, exec_state); 680} 681catch_block_1() 682EndTest(); 683 684 685BeginTest("Catch block 2"); 686function catch_block_2() { 687 try { 688 throw 'Exception'; 689 } catch (e) { 690 with({n:10}) { 691 debugger; 692 } 693 } 694}; 695 696 697listener_delegate = function(exec_state) { 698 CheckScopeChain([debug.ScopeType.With, 699 debug.ScopeType.Catch, 700 debug.ScopeType.Local, 701 debug.ScopeType.Global], exec_state); 702 CheckScopeContent({n:10}, 0, exec_state); 703 CheckScopeContent({e:'Exception'}, 1, exec_state); 704} 705catch_block_2() 706EndTest(); 707 708 709BeginTest("Catch block 3"); 710function catch_block_3() { 711 // Do eval to dynamically declare a local variable so that the context's 712 // extension slot is initialized with JSContextExtensionObject. 713 eval("var y = 78;"); 714 try { 715 throw 'Exception'; 716 } catch (e) { 717 debugger; 718 } 719}; 720 721 722listener_delegate = function(exec_state) { 723 CheckScopeChain([debug.ScopeType.Catch, 724 debug.ScopeType.Local, 725 debug.ScopeType.Global], exec_state); 726 CheckScopeContent({e:'Exception'}, 0, exec_state); 727 CheckScopeContent({y:78}, 1, exec_state); 728} 729catch_block_3() 730EndTest(); 731 732 733BeginTest("Catch block 4"); 734function catch_block_4() { 735 // Do eval to dynamically declare a local variable so that the context's 736 // extension slot is initialized with JSContextExtensionObject. 737 eval("var y = 98;"); 738 try { 739 throw 'Exception'; 740 } catch (e) { 741 with({n:10}) { 742 debugger; 743 } 744 } 745}; 746 747listener_delegate = function(exec_state) { 748 CheckScopeChain([debug.ScopeType.With, 749 debug.ScopeType.Catch, 750 debug.ScopeType.Local, 751 debug.ScopeType.Global], exec_state); 752 CheckScopeContent({n:10}, 0, exec_state); 753 CheckScopeContent({e:'Exception'}, 1, exec_state); 754 CheckScopeContent({y:98}, 2, exec_state); 755} 756catch_block_4() 757EndTest(); 758 759 760assertEquals(begin_test_count, break_count, 'one or more tests did not enter the debugger'); 761assertEquals(begin_test_count, end_test_count, 'one or more tests did not have its result checked'); 762