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. 34 Debug = debug.Debug 35 36 var name; 37 var listener_delegate; 38 var listener_called; 39 var exception; 40 var begin_test_count = 0; 41 var end_test_count = 0; 42 var break_count = 0; 43 44 45 // Debug event listener which delegates. 46 function 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. 59 Debug.setListener(listener); 60 61 62 // Initialize for a noew test. 63 function 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. 73 function 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. 81 function 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. 123 function 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. 184 BeginTest("Local 1"); 185 186 function local_1() { 187 debugger; 188 } 189 190 listener_delegate = function(exec_state) { 191 CheckScopeChain([debug.ScopeType.Local, 192 debug.ScopeType.Global], exec_state); 193 CheckScopeContent({}, 0, exec_state); 194 } 195 local_1() 196 EndTest(); 197 198 199 // Local scope with a parameter. 200 BeginTest("Local 2"); 201 202 function local_2(a) { 203 debugger; 204 } 205 206 listener_delegate = function(exec_state) { 207 CheckScopeChain([debug.ScopeType.Local, 208 debug.ScopeType.Global], exec_state); 209 CheckScopeContent({a:1}, 0, exec_state); 210 } 211 local_2(1) 212 EndTest(); 213 214 215 // Local scope with a parameter and a local variable. 216 BeginTest("Local 3"); 217 218 function local_3(a) { 219 var x = 3; 220 debugger; 221 } 222 223 listener_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 } 228 local_3(1) 229 EndTest(); 230 231 232 // Local scope with parameters and local variables. 233 BeginTest("Local 4"); 234 235 function local_4(a, b) { 236 var x = 3; 237 var y = 4; 238 debugger; 239 } 240 241 listener_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 } 246 local_4(1, 2) 247 EndTest(); 248 249 250 // Empty local scope with use of eval. 251 BeginTest("Local 5"); 252 253 function local_5() { 254 eval(''); 255 debugger; 256 } 257 258 listener_delegate = function(exec_state) { 259 CheckScopeChain([debug.ScopeType.Local, 260 debug.ScopeType.Global], exec_state); 261 CheckScopeContent({}, 0, exec_state); 262 } 263 local_5() 264 EndTest(); 265 266 267 // Local introducing local variable using eval. 268 BeginTest("Local 6"); 269 270 function local_6() { 271 eval('var i = 5'); 272 debugger; 273 } 274 275 listener_delegate = function(exec_state) { 276 CheckScopeChain([debug.ScopeType.Local, 277 debug.ScopeType.Global], exec_state); 278 CheckScopeContent({i:5}, 0, exec_state); 279 } 280 local_6() 281 EndTest(); 282 283 284 // Local scope with parameters, local variables and local variable introduced 285 // using eval. 286 BeginTest("Local 7"); 287 288 function 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 296 listener_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 } 301 local_7(1, 2) 302 EndTest(); 303 304 305 // Single empty with block. 306 BeginTest("With 1"); 307 308 function with_1() { 309 with({}) { 310 debugger; 311 } 312 } 313 314 listener_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 } 320 with_1() 321 EndTest(); 322 323 324 // Nested empty with blocks. 325 BeginTest("With 2"); 326 327 function with_2() { 328 with({}) { 329 with({}) { 330 debugger; 331 } 332 } 333 } 334 335 listener_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 } 343 with_2() 344 EndTest(); 345 346 347 // With block using an in-place object literal. 348 BeginTest("With 3"); 349 350 function with_3() { 351 with({a:1,b:2}) { 352 debugger; 353 } 354 } 355 356 listener_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 } 362 with_3() 363 EndTest(); 364 365 366 // Nested with blocks using in-place object literals. 367 BeginTest("With 4"); 368 369 function with_4() { 370 with({a:1,b:2}) { 371 with({a:2,b:1}) { 372 debugger; 373 } 374 } 375 } 376 377 listener_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 } 385 with_4() 386 EndTest(); 387 388 389 // Nested with blocks using existing object. 390 BeginTest("With 5"); 391 392 var with_object = {c:3,d:4}; 393 function with_5() { 394 with(with_object) { 395 with(with_object) { 396 debugger; 397 } 398 } 399 } 400 401 listener_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 } 411 with_5() 412 EndTest(); 413 414 415 // Simple closure formed by returning an inner function referering the outer 416 // functions arguments. 417 BeginTest("Closure 1"); 418 419 function closure_1(a) { 420 function f() { 421 debugger; 422 return a; 423 }; 424 return f; 425 } 426 427 listener_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 } 433 closure_1(1)() 434 EndTest(); 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. 440 BeginTest("Closure 2"); 441 442 function 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 452 listener_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 } 458 closure_2(1, 2)() 459 EndTest(); 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. 466 BeginTest("Closure 3"); 467 468 function 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 478 listener_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 } 484 closure_3(1, 2)() 485 EndTest(); 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... 493 BeginTest("Closure 4"); 494 495 function 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 507 listener_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 } 513 closure_4(1, 2)() 514 EndTest(); 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. 522 BeginTest("Closure 5"); 523 524 function 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 535 listener_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 } 541 closure_5(1, 2)() 542 EndTest(); 543 544 545 // Two closures. Due to optimizations only the parts actually used are provided 546 // through the debugger information. 547 BeginTest("Closure 6"); 548 function 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 563 listener_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 } 571 closure_6(1, 2)() 572 EndTest(); 573 574 575 // Two closures. In the presence of eval all information is provided as the 576 // compiler cannot determine which parts are used. 577 BeginTest("Closure 7"); 578 function 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 597 listener_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 } 606 closure_7(1, 2)() 607 EndTest(); 608 609 610 // Test a mixture of scopes. 611 BeginTest("The full monty"); 612 function 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 638 listener_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 } 653 the_full_monty(1, 2)() 654 EndTest(); 655 656 // Test global scope. 657 BeginTest("Global"); 658 listener_delegate = function(exec_state) { 659 CheckScopeChain([debug.ScopeType.Global], exec_state); 660 } 661 debugger; 662 EndTest(); 663 664 665 BeginTest("Catch block 1"); 666 function catch_block_1() { 667 try { 668 throw 'Exception'; 669 } catch (e) { 670 debugger; 671 } 672 }; 673 674 675 listener_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 } 681 catch_block_1() 682 EndTest(); 683 684 685 BeginTest("Catch block 2"); 686 function catch_block_2() { 687 try { 688 throw 'Exception'; 689 } catch (e) { 690 with({n:10}) { 691 debugger; 692 } 693 } 694 }; 695 696 697 listener_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 } 705 catch_block_2() 706 EndTest(); 707 708 709 BeginTest("Catch block 3"); 710 function catch_block_1() { 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 722 listener_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 } 729 catch_block_1() 730 EndTest(); 731 732 733 BeginTest("Catch block 4"); 734 function catch_block_2() { 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 747 listener_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 } 756 catch_block_2() 757 EndTest(); 758 759 760 assertEquals(begin_test_count, break_count, 'one or more tests did not enter the debugger'); 761 assertEquals(begin_test_count, end_test_count, 'one or more tests did not have its result checked'); 762