1 // Copyright 2006-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 // Default number of frames to include in the response to backtrace request. 29 const kDefaultBacktraceLength = 10; 30 31 const Debug = {}; 32 33 // Regular expression to skip "crud" at the beginning of a source line which is 34 // not really code. Currently the regular expression matches whitespace and 35 // comments. 36 const sourceLineBeginningSkip = /^(?:\s*(?:\/\*.*?\*\/)*)*/; 37 38 // Debug events which can occour in the V8 JavaScript engine. These originate 39 // from the API include file debug.h. 40 Debug.DebugEvent = { Break: 1, 41 Exception: 2, 42 NewFunction: 3, 43 BeforeCompile: 4, 44 AfterCompile: 5, 45 ScriptCollected: 6 }; 46 47 // Types of exceptions that can be broken upon. 48 Debug.ExceptionBreak = { Caught : 0, 49 Uncaught: 1 }; 50 51 // The different types of steps. 52 Debug.StepAction = { StepOut: 0, 53 StepNext: 1, 54 StepIn: 2, 55 StepMin: 3, 56 StepInMin: 4 }; 57 58 // The different types of scripts matching enum ScriptType in objects.h. 59 Debug.ScriptType = { Native: 0, 60 Extension: 1, 61 Normal: 2 }; 62 63 // The different types of script compilations matching enum 64 // Script::CompilationType in objects.h. 65 Debug.ScriptCompilationType = { Host: 0, 66 Eval: 1, 67 JSON: 2 }; 68 69 // The different script break point types. 70 Debug.ScriptBreakPointType = { ScriptId: 0, 71 ScriptName: 1 }; 72 73 function ScriptTypeFlag(type) { 74 return (1 << type); 75 } 76 77 // Globals. 78 var next_response_seq = 0; 79 var next_break_point_number = 1; 80 var break_points = []; 81 var script_break_points = []; 82 var debugger_flags = { 83 breakPointsActive: { 84 value: true, 85 getValue: function() { return this.value; }, 86 setValue: function(value) { 87 this.value = !!value; 88 %SetDisableBreak(!this.value); 89 } 90 }, 91 breakOnCaughtException: { 92 getValue: function() { return Debug.isBreakOnException(); }, 93 setValue: function(value) { 94 if (value) { 95 Debug.setBreakOnException(); 96 } else { 97 Debug.clearBreakOnException(); 98 } 99 } 100 }, 101 breakOnUncaughtException: { 102 getValue: function() { return Debug.isBreakOnUncaughtException(); }, 103 setValue: function(value) { 104 if (value) { 105 Debug.setBreakOnUncaughtException(); 106 } else { 107 Debug.clearBreakOnUncaughtException(); 108 } 109 } 110 }, 111 }; 112 var lol_is_enabled = %HasLOLEnabled(); 113 114 115 // Create a new break point object and add it to the list of break points. 116 function MakeBreakPoint(source_position, opt_script_break_point) { 117 var break_point = new BreakPoint(source_position, opt_script_break_point); 118 break_points.push(break_point); 119 return break_point; 120 } 121 122 123 // Object representing a break point. 124 // NOTE: This object does not have a reference to the function having break 125 // point as this would cause function not to be garbage collected when it is 126 // not used any more. We do not want break points to keep functions alive. 127 function BreakPoint(source_position, opt_script_break_point) { 128 this.source_position_ = source_position; 129 if (opt_script_break_point) { 130 this.script_break_point_ = opt_script_break_point; 131 } else { 132 this.number_ = next_break_point_number++; 133 } 134 this.hit_count_ = 0; 135 this.active_ = true; 136 this.condition_ = null; 137 this.ignoreCount_ = 0; 138 } 139 140 141 BreakPoint.prototype.number = function() { 142 return this.number_; 143 }; 144 145 146 BreakPoint.prototype.func = function() { 147 return this.func_; 148 }; 149 150 151 BreakPoint.prototype.source_position = function() { 152 return this.source_position_; 153 }; 154 155 156 BreakPoint.prototype.hit_count = function() { 157 return this.hit_count_; 158 }; 159 160 161 BreakPoint.prototype.active = function() { 162 if (this.script_break_point()) { 163 return this.script_break_point().active(); 164 } 165 return this.active_; 166 }; 167 168 169 BreakPoint.prototype.condition = function() { 170 if (this.script_break_point() && this.script_break_point().condition()) { 171 return this.script_break_point().condition(); 172 } 173 return this.condition_; 174 }; 175 176 177 BreakPoint.prototype.ignoreCount = function() { 178 return this.ignoreCount_; 179 }; 180 181 182 BreakPoint.prototype.script_break_point = function() { 183 return this.script_break_point_; 184 }; 185 186 187 BreakPoint.prototype.enable = function() { 188 this.active_ = true; 189 }; 190 191 192 BreakPoint.prototype.disable = function() { 193 this.active_ = false; 194 }; 195 196 197 BreakPoint.prototype.setCondition = function(condition) { 198 this.condition_ = condition; 199 }; 200 201 202 BreakPoint.prototype.setIgnoreCount = function(ignoreCount) { 203 this.ignoreCount_ = ignoreCount; 204 }; 205 206 207 BreakPoint.prototype.isTriggered = function(exec_state) { 208 // Break point not active - not triggered. 209 if (!this.active()) return false; 210 211 // Check for conditional break point. 212 if (this.condition()) { 213 // If break point has condition try to evaluate it in the top frame. 214 try { 215 var mirror = exec_state.frame(0).evaluate(this.condition()); 216 // If no sensible mirror or non true value break point not triggered. 217 if (!(mirror instanceof ValueMirror) || !%ToBoolean(mirror.value_)) { 218 return false; 219 } 220 } catch (e) { 221 // Exception evaluating condition counts as not triggered. 222 return false; 223 } 224 } 225 226 // Update the hit count. 227 this.hit_count_++; 228 if (this.script_break_point_) { 229 this.script_break_point_.hit_count_++; 230 } 231 232 // If the break point has an ignore count it is not triggered. 233 if (this.ignoreCount_ > 0) { 234 this.ignoreCount_--; 235 return false; 236 } 237 238 // Break point triggered. 239 return true; 240 }; 241 242 243 // Function called from the runtime when a break point is hit. Returns true if 244 // the break point is triggered and supposed to break execution. 245 function IsBreakPointTriggered(break_id, break_point) { 246 return break_point.isTriggered(MakeExecutionState(break_id)); 247 } 248 249 250 // Object representing a script break point. The script is referenced by its 251 // script name or script id and the break point is represented as line and 252 // column. 253 function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column, 254 opt_groupId) { 255 this.type_ = type; 256 if (type == Debug.ScriptBreakPointType.ScriptId) { 257 this.script_id_ = script_id_or_name; 258 } else { // type == Debug.ScriptBreakPointType.ScriptName 259 this.script_name_ = script_id_or_name; 260 } 261 this.line_ = opt_line || 0; 262 this.column_ = opt_column; 263 this.groupId_ = opt_groupId; 264 this.hit_count_ = 0; 265 this.active_ = true; 266 this.condition_ = null; 267 this.ignoreCount_ = 0; 268 this.break_points_ = []; 269 } 270 271 272 //Creates a clone of script breakpoint that is linked to another script. 273 ScriptBreakPoint.prototype.cloneForOtherScript = function (other_script) { 274 var copy = new ScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId, 275 other_script.id, this.line_, this.column_, this.groupId_); 276 copy.number_ = next_break_point_number++; 277 script_break_points.push(copy); 278 279 copy.hit_count_ = this.hit_count_; 280 copy.active_ = this.active_; 281 copy.condition_ = this.condition_; 282 copy.ignoreCount_ = this.ignoreCount_; 283 return copy; 284 } 285 286 287 ScriptBreakPoint.prototype.number = function() { 288 return this.number_; 289 }; 290 291 292 ScriptBreakPoint.prototype.groupId = function() { 293 return this.groupId_; 294 }; 295 296 297 ScriptBreakPoint.prototype.type = function() { 298 return this.type_; 299 }; 300 301 302 ScriptBreakPoint.prototype.script_id = function() { 303 return this.script_id_; 304 }; 305 306 307 ScriptBreakPoint.prototype.script_name = function() { 308 return this.script_name_; 309 }; 310 311 312 ScriptBreakPoint.prototype.line = function() { 313 return this.line_; 314 }; 315 316 317 ScriptBreakPoint.prototype.column = function() { 318 return this.column_; 319 }; 320 321 322 ScriptBreakPoint.prototype.actual_locations = function() { 323 var locations = []; 324 for (var i = 0; i < this.break_points_.length; i++) { 325 locations.push(this.break_points_[i].actual_location); 326 } 327 return locations; 328 } 329 330 331 ScriptBreakPoint.prototype.update_positions = function(line, column) { 332 this.line_ = line; 333 this.column_ = column; 334 } 335 336 337 ScriptBreakPoint.prototype.hit_count = function() { 338 return this.hit_count_; 339 }; 340 341 342 ScriptBreakPoint.prototype.active = function() { 343 return this.active_; 344 }; 345 346 347 ScriptBreakPoint.prototype.condition = function() { 348 return this.condition_; 349 }; 350 351 352 ScriptBreakPoint.prototype.ignoreCount = function() { 353 return this.ignoreCount_; 354 }; 355 356 357 ScriptBreakPoint.prototype.enable = function() { 358 this.active_ = true; 359 }; 360 361 362 ScriptBreakPoint.prototype.disable = function() { 363 this.active_ = false; 364 }; 365 366 367 ScriptBreakPoint.prototype.setCondition = function(condition) { 368 this.condition_ = condition; 369 }; 370 371 372 ScriptBreakPoint.prototype.setIgnoreCount = function(ignoreCount) { 373 this.ignoreCount_ = ignoreCount; 374 375 // Set ignore count on all break points created from this script break point. 376 for (var i = 0; i < this.break_points_.length; i++) { 377 this.break_points_[i].setIgnoreCount(ignoreCount); 378 } 379 }; 380 381 382 // Check whether a script matches this script break point. Currently this is 383 // only based on script name. 384 ScriptBreakPoint.prototype.matchesScript = function(script) { 385 if (this.type_ == Debug.ScriptBreakPointType.ScriptId) { 386 return this.script_id_ == script.id; 387 } else { // this.type_ == Debug.ScriptBreakPointType.ScriptName 388 return this.script_name_ == script.nameOrSourceURL() && 389 script.line_offset <= this.line_ && 390 this.line_ < script.line_offset + script.lineCount(); 391 } 392 }; 393 394 395 // Set the script break point in a script. 396 ScriptBreakPoint.prototype.set = function (script) { 397 var column = this.column(); 398 var line = this.line(); 399 // If the column is undefined the break is on the line. To help locate the 400 // first piece of breakable code on the line try to find the column on the 401 // line which contains some source. 402 if (IS_UNDEFINED(column)) { 403 var source_line = script.sourceLine(this.line()); 404 405 // Allocate array for caching the columns where the actual source starts. 406 if (!script.sourceColumnStart_) { 407 script.sourceColumnStart_ = new Array(script.lineCount()); 408 } 409 410 // Fill cache if needed and get column where the actual source starts. 411 if (IS_UNDEFINED(script.sourceColumnStart_[line])) { 412 script.sourceColumnStart_[line] = 413 source_line.match(sourceLineBeginningSkip)[0].length; 414 } 415 column = script.sourceColumnStart_[line]; 416 } 417 418 // Convert the line and column into an absolute position within the script. 419 var position = Debug.findScriptSourcePosition(script, this.line(), column); 420 421 // If the position is not found in the script (the script might be shorter 422 // than it used to be) just ignore it. 423 if (position === null) return; 424 425 // Create a break point object and set the break point. 426 break_point = MakeBreakPoint(position, this); 427 break_point.setIgnoreCount(this.ignoreCount()); 428 var actual_position = %SetScriptBreakPoint(script, position, break_point); 429 if (IS_UNDEFINED(actual_position)) { 430 actual_position = position; 431 } 432 var actual_location = script.locationFromPosition(actual_position, true); 433 break_point.actual_location = { line: actual_location.line, 434 column: actual_location.column }; 435 this.break_points_.push(break_point); 436 return break_point; 437 }; 438 439 440 // Clear all the break points created from this script break point 441 ScriptBreakPoint.prototype.clear = function () { 442 var remaining_break_points = []; 443 for (var i = 0; i < break_points.length; i++) { 444 if (break_points[i].script_break_point() && 445 break_points[i].script_break_point() === this) { 446 %ClearBreakPoint(break_points[i]); 447 } else { 448 remaining_break_points.push(break_points[i]); 449 } 450 } 451 break_points = remaining_break_points; 452 this.break_points_ = []; 453 }; 454 455 456 // Function called from runtime when a new script is compiled to set any script 457 // break points set in this script. 458 function UpdateScriptBreakPoints(script) { 459 for (var i = 0; i < script_break_points.length; i++) { 460 if (script_break_points[i].type() == Debug.ScriptBreakPointType.ScriptName && 461 script_break_points[i].matchesScript(script)) { 462 script_break_points[i].set(script); 463 } 464 } 465 } 466 467 468 function GetScriptBreakPoints(script) { 469 var result = []; 470 for (var i = 0; i < script_break_points.length; i++) { 471 if (script_break_points[i].matchesScript(script)) { 472 result.push(script_break_points[i]); 473 } 474 } 475 return result; 476 } 477 478 479 Debug.setListener = function(listener, opt_data) { 480 if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) { 481 throw new Error('Parameters have wrong types.'); 482 } 483 %SetDebugEventListener(listener, opt_data); 484 }; 485 486 487 Debug.breakExecution = function(f) { 488 %Break(); 489 }; 490 491 Debug.breakLocations = function(f) { 492 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); 493 return %GetBreakLocations(f); 494 }; 495 496 // Returns a Script object. If the parameter is a function the return value 497 // is the script in which the function is defined. If the parameter is a string 498 // the return value is the script for which the script name has that string 499 // value. If it is a regexp and there is a unique script whose name matches 500 // we return that, otherwise undefined. 501 Debug.findScript = function(func_or_script_name) { 502 if (IS_FUNCTION(func_or_script_name)) { 503 return %FunctionGetScript(func_or_script_name); 504 } else if (IS_REGEXP(func_or_script_name)) { 505 var scripts = Debug.scripts(); 506 var last_result = null; 507 var result_count = 0; 508 for (var i in scripts) { 509 var script = scripts[i]; 510 if (func_or_script_name.test(script.name)) { 511 last_result = script; 512 result_count++; 513 } 514 } 515 // Return the unique script matching the regexp. If there are more 516 // than one we don't return a value since there is no good way to 517 // decide which one to return. Returning a "random" one, say the 518 // first, would introduce nondeterminism (or something close to it) 519 // because the order is the heap iteration order. 520 if (result_count == 1) { 521 return last_result; 522 } else { 523 return undefined; 524 } 525 } else { 526 return %GetScript(func_or_script_name); 527 } 528 }; 529 530 // Returns the script source. If the parameter is a function the return value 531 // is the script source for the script in which the function is defined. If the 532 // parameter is a string the return value is the script for which the script 533 // name has that string value. 534 Debug.scriptSource = function(func_or_script_name) { 535 return this.findScript(func_or_script_name).source; 536 }; 537 538 Debug.source = function(f) { 539 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); 540 return %FunctionGetSourceCode(f); 541 }; 542 543 Debug.disassemble = function(f) { 544 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); 545 return %DebugDisassembleFunction(f); 546 }; 547 548 Debug.disassembleConstructor = function(f) { 549 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); 550 return %DebugDisassembleConstructor(f); 551 }; 552 553 Debug.ExecuteInDebugContext = function(f, without_debugger) { 554 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); 555 return %ExecuteInDebugContext(f, !!without_debugger); 556 }; 557 558 Debug.sourcePosition = function(f) { 559 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); 560 return %FunctionGetScriptSourcePosition(f); 561 }; 562 563 564 Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) { 565 var script = %FunctionGetScript(func); 566 var script_offset = %FunctionGetScriptSourcePosition(func); 567 return script.locationFromLine(opt_line, opt_column, script_offset); 568 } 569 570 571 // Returns the character position in a script based on a line number and an 572 // optional position within that line. 573 Debug.findScriptSourcePosition = function(script, opt_line, opt_column) { 574 var location = script.locationFromLine(opt_line, opt_column); 575 return location ? location.position : null; 576 } 577 578 579 Debug.findBreakPoint = function(break_point_number, remove) { 580 var break_point; 581 for (var i = 0; i < break_points.length; i++) { 582 if (break_points[i].number() == break_point_number) { 583 break_point = break_points[i]; 584 // Remove the break point from the list if requested. 585 if (remove) { 586 break_points.splice(i, 1); 587 } 588 break; 589 } 590 } 591 if (break_point) { 592 return break_point; 593 } else { 594 return this.findScriptBreakPoint(break_point_number, remove); 595 } 596 }; 597 598 Debug.findBreakPointActualLocations = function(break_point_number) { 599 for (var i = 0; i < script_break_points.length; i++) { 600 if (script_break_points[i].number() == break_point_number) { 601 return script_break_points[i].actual_locations(); 602 } 603 } 604 for (var i = 0; i < break_points.length; i++) { 605 if (break_points[i].number() == break_point_number) { 606 return [break_points[i].actual_location]; 607 } 608 } 609 return []; 610 } 611 612 Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) { 613 if (!IS_FUNCTION(func)) throw new Error('Parameters have wrong types.'); 614 // Break points in API functions are not supported. 615 if (%FunctionIsAPIFunction(func)) { 616 throw new Error('Cannot set break point in native code.'); 617 } 618 // Find source position relative to start of the function 619 var break_position = 620 this.findFunctionSourceLocation(func, opt_line, opt_column).position; 621 var source_position = break_position - this.sourcePosition(func); 622 // Find the script for the function. 623 var script = %FunctionGetScript(func); 624 // Break in builtin JavaScript code is not supported. 625 if (script.type == Debug.ScriptType.Native) { 626 throw new Error('Cannot set break point in native code.'); 627 } 628 // If the script for the function has a name convert this to a script break 629 // point. 630 if (script && script.id) { 631 // Adjust the source position to be script relative. 632 source_position += %FunctionGetScriptSourcePosition(func); 633 // Find line and column for the position in the script and set a script 634 // break point from that. 635 var location = script.locationFromPosition(source_position, false); 636 return this.setScriptBreakPointById(script.id, 637 location.line, location.column, 638 opt_condition); 639 } else { 640 // Set a break point directly on the function. 641 var break_point = MakeBreakPoint(source_position); 642 var actual_position = 643 %SetFunctionBreakPoint(func, source_position, break_point); 644 actual_position += this.sourcePosition(func); 645 var actual_location = script.locationFromPosition(actual_position, true); 646 break_point.actual_location = { line: actual_location.line, 647 column: actual_location.column }; 648 break_point.setCondition(opt_condition); 649 return break_point.number(); 650 } 651 }; 652 653 654 Debug.setBreakPointByScriptIdAndPosition = function(script_id, position, 655 condition, enabled) 656 { 657 break_point = MakeBreakPoint(position); 658 break_point.setCondition(condition); 659 if (!enabled) 660 break_point.disable(); 661 var scripts = this.scripts(); 662 for (var i = 0; i < scripts.length; i++) { 663 if (script_id == scripts[i].id) { 664 break_point.actual_position = %SetScriptBreakPoint(scripts[i], position, 665 break_point); 666 break; 667 } 668 } 669 return break_point; 670 }; 671 672 673 Debug.enableBreakPoint = function(break_point_number) { 674 var break_point = this.findBreakPoint(break_point_number, false); 675 // Only enable if the breakpoint hasn't been deleted: 676 if (break_point) { 677 break_point.enable(); 678 } 679 }; 680 681 682 Debug.disableBreakPoint = function(break_point_number) { 683 var break_point = this.findBreakPoint(break_point_number, false); 684 // Only enable if the breakpoint hasn't been deleted: 685 if (break_point) { 686 break_point.disable(); 687 } 688 }; 689 690 691 Debug.changeBreakPointCondition = function(break_point_number, condition) { 692 var break_point = this.findBreakPoint(break_point_number, false); 693 break_point.setCondition(condition); 694 }; 695 696 697 Debug.changeBreakPointIgnoreCount = function(break_point_number, ignoreCount) { 698 if (ignoreCount < 0) { 699 throw new Error('Invalid argument'); 700 } 701 var break_point = this.findBreakPoint(break_point_number, false); 702 break_point.setIgnoreCount(ignoreCount); 703 }; 704 705 706 Debug.clearBreakPoint = function(break_point_number) { 707 var break_point = this.findBreakPoint(break_point_number, true); 708 if (break_point) { 709 return %ClearBreakPoint(break_point); 710 } else { 711 break_point = this.findScriptBreakPoint(break_point_number, true); 712 if (!break_point) { 713 throw new Error('Invalid breakpoint'); 714 } 715 } 716 }; 717 718 719 Debug.clearAllBreakPoints = function() { 720 for (var i = 0; i < break_points.length; i++) { 721 break_point = break_points[i]; 722 %ClearBreakPoint(break_point); 723 } 724 break_points = []; 725 }; 726 727 728 Debug.disableAllBreakPoints = function() { 729 // Disable all user defined breakpoints: 730 for (var i = 1; i < next_break_point_number; i++) { 731 Debug.disableBreakPoint(i); 732 } 733 // Disable all exception breakpoints: 734 %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false); 735 %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false); 736 }; 737 738 739 Debug.findScriptBreakPoint = function(break_point_number, remove) { 740 var script_break_point; 741 for (var i = 0; i < script_break_points.length; i++) { 742 if (script_break_points[i].number() == break_point_number) { 743 script_break_point = script_break_points[i]; 744 // Remove the break point from the list if requested. 745 if (remove) { 746 script_break_point.clear(); 747 script_break_points.splice(i,1); 748 } 749 break; 750 } 751 } 752 return script_break_point; 753 } 754 755 756 // Sets a breakpoint in a script identified through id or name at the 757 // specified source line and column within that line. 758 Debug.setScriptBreakPoint = function(type, script_id_or_name, 759 opt_line, opt_column, opt_condition, 760 opt_groupId) { 761 // Create script break point object. 762 var script_break_point = 763 new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column, 764 opt_groupId); 765 766 // Assign number to the new script break point and add it. 767 script_break_point.number_ = next_break_point_number++; 768 script_break_point.setCondition(opt_condition); 769 script_break_points.push(script_break_point); 770 771 // Run through all scripts to see if this script break point matches any 772 // loaded scripts. 773 var scripts = this.scripts(); 774 for (var i = 0; i < scripts.length; i++) { 775 if (script_break_point.matchesScript(scripts[i])) { 776 script_break_point.set(scripts[i]); 777 } 778 } 779 780 return script_break_point.number(); 781 } 782 783 784 Debug.setScriptBreakPointById = function(script_id, 785 opt_line, opt_column, 786 opt_condition, opt_groupId) { 787 return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId, 788 script_id, opt_line, opt_column, 789 opt_condition, opt_groupId); 790 } 791 792 793 Debug.setScriptBreakPointByName = function(script_name, 794 opt_line, opt_column, 795 opt_condition, opt_groupId) { 796 return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName, 797 script_name, opt_line, opt_column, 798 opt_condition, opt_groupId); 799 } 800 801 802 Debug.enableScriptBreakPoint = function(break_point_number) { 803 var script_break_point = this.findScriptBreakPoint(break_point_number, false); 804 script_break_point.enable(); 805 }; 806 807 808 Debug.disableScriptBreakPoint = function(break_point_number) { 809 var script_break_point = this.findScriptBreakPoint(break_point_number, false); 810 script_break_point.disable(); 811 }; 812 813 814 Debug.changeScriptBreakPointCondition = function(break_point_number, condition) { 815 var script_break_point = this.findScriptBreakPoint(break_point_number, false); 816 script_break_point.setCondition(condition); 817 }; 818 819 820 Debug.changeScriptBreakPointIgnoreCount = function(break_point_number, ignoreCount) { 821 if (ignoreCount < 0) { 822 throw new Error('Invalid argument'); 823 } 824 var script_break_point = this.findScriptBreakPoint(break_point_number, false); 825 script_break_point.setIgnoreCount(ignoreCount); 826 }; 827 828 829 Debug.scriptBreakPoints = function() { 830 return script_break_points; 831 } 832 833 834 Debug.clearStepping = function() { 835 %ClearStepping(); 836 } 837 838 Debug.setBreakOnException = function() { 839 return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, true); 840 }; 841 842 Debug.clearBreakOnException = function() { 843 return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false); 844 }; 845 846 Debug.isBreakOnException = function() { 847 return !!%IsBreakOnException(Debug.ExceptionBreak.Caught); 848 }; 849 850 Debug.setBreakOnUncaughtException = function() { 851 return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true); 852 }; 853 854 Debug.clearBreakOnUncaughtException = function() { 855 return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false); 856 }; 857 858 Debug.isBreakOnUncaughtException = function() { 859 return !!%IsBreakOnException(Debug.ExceptionBreak.Uncaught); 860 }; 861 862 Debug.showBreakPoints = function(f, full) { 863 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); 864 var source = full ? this.scriptSource(f) : this.source(f); 865 var offset = full ? this.sourcePosition(f) : 0; 866 var locations = this.breakLocations(f); 867 if (!locations) return source; 868 locations.sort(function(x, y) { return x - y; }); 869 var result = ""; 870 var prev_pos = 0; 871 var pos; 872 for (var i = 0; i < locations.length; i++) { 873 pos = locations[i] - offset; 874 result += source.slice(prev_pos, pos); 875 result += "[B" + i + "]"; 876 prev_pos = pos; 877 } 878 pos = source.length; 879 result += source.substring(prev_pos, pos); 880 return result; 881 }; 882 883 884 // Get all the scripts currently loaded. Locating all the scripts is based on 885 // scanning the heap. 886 Debug.scripts = function() { 887 // Collect all scripts in the heap. 888 return %DebugGetLoadedScripts(); 889 }; 890 891 892 Debug.debuggerFlags = function() { 893 return debugger_flags; 894 }; 895 896 Debug.MakeMirror = MakeMirror; 897 898 function MakeExecutionState(break_id) { 899 return new ExecutionState(break_id); 900 } 901 902 function ExecutionState(break_id) { 903 this.break_id = break_id; 904 this.selected_frame = 0; 905 } 906 907 ExecutionState.prototype.prepareStep = function(opt_action, opt_count) { 908 var action = Debug.StepAction.StepIn; 909 if (!IS_UNDEFINED(opt_action)) action = %ToNumber(opt_action); 910 var count = opt_count ? %ToNumber(opt_count) : 1; 911 912 return %PrepareStep(this.break_id, action, count); 913 } 914 915 ExecutionState.prototype.evaluateGlobal = function(source, disable_break, 916 opt_additional_context) { 917 return MakeMirror(%DebugEvaluateGlobal(this.break_id, source, 918 Boolean(disable_break), 919 opt_additional_context)); 920 }; 921 922 ExecutionState.prototype.frameCount = function() { 923 return %GetFrameCount(this.break_id); 924 }; 925 926 ExecutionState.prototype.threadCount = function() { 927 return %GetThreadCount(this.break_id); 928 }; 929 930 ExecutionState.prototype.frame = function(opt_index) { 931 // If no index supplied return the selected frame. 932 if (opt_index == null) opt_index = this.selected_frame; 933 if (opt_index < 0 || opt_index >= this.frameCount()) 934 throw new Error('Illegal frame index.'); 935 return new FrameMirror(this.break_id, opt_index); 936 }; 937 938 ExecutionState.prototype.setSelectedFrame = function(index) { 939 var i = %ToNumber(index); 940 if (i < 0 || i >= this.frameCount()) throw new Error('Illegal frame index.'); 941 this.selected_frame = i; 942 }; 943 944 ExecutionState.prototype.selectedFrame = function() { 945 return this.selected_frame; 946 }; 947 948 ExecutionState.prototype.debugCommandProcessor = function(opt_is_running) { 949 return new DebugCommandProcessor(this, opt_is_running); 950 }; 951 952 953 function MakeBreakEvent(exec_state, break_points_hit) { 954 return new BreakEvent(exec_state, break_points_hit); 955 } 956 957 958 function BreakEvent(exec_state, break_points_hit) { 959 this.exec_state_ = exec_state; 960 this.break_points_hit_ = break_points_hit; 961 } 962 963 964 BreakEvent.prototype.executionState = function() { 965 return this.exec_state_; 966 }; 967 968 969 BreakEvent.prototype.eventType = function() { 970 return Debug.DebugEvent.Break; 971 }; 972 973 974 BreakEvent.prototype.func = function() { 975 return this.exec_state_.frame(0).func(); 976 }; 977 978 979 BreakEvent.prototype.sourceLine = function() { 980 return this.exec_state_.frame(0).sourceLine(); 981 }; 982 983 984 BreakEvent.prototype.sourceColumn = function() { 985 return this.exec_state_.frame(0).sourceColumn(); 986 }; 987 988 989 BreakEvent.prototype.sourceLineText = function() { 990 return this.exec_state_.frame(0).sourceLineText(); 991 }; 992 993 994 BreakEvent.prototype.breakPointsHit = function() { 995 return this.break_points_hit_; 996 }; 997 998 999 BreakEvent.prototype.toJSONProtocol = function() { 1000 var o = { seq: next_response_seq++, 1001 type: "event", 1002 event: "break", 1003 body: { invocationText: this.exec_state_.frame(0).invocationText(), 1004 } 1005 }; 1006 1007 // Add script related information to the event if available. 1008 var script = this.func().script(); 1009 if (script) { 1010 o.body.sourceLine = this.sourceLine(), 1011 o.body.sourceColumn = this.sourceColumn(), 1012 o.body.sourceLineText = this.sourceLineText(), 1013 o.body.script = MakeScriptObject_(script, false); 1014 } 1015 1016 // Add an Array of break points hit if any. 1017 if (this.breakPointsHit()) { 1018 o.body.breakpoints = []; 1019 for (var i = 0; i < this.breakPointsHit().length; i++) { 1020 // Find the break point number. For break points originating from a 1021 // script break point supply the script break point number. 1022 var breakpoint = this.breakPointsHit()[i]; 1023 var script_break_point = breakpoint.script_break_point(); 1024 var number; 1025 if (script_break_point) { 1026 number = script_break_point.number(); 1027 } else { 1028 number = breakpoint.number(); 1029 } 1030 o.body.breakpoints.push(number); 1031 } 1032 } 1033 return JSON.stringify(ObjectToProtocolObject_(o)); 1034 }; 1035 1036 1037 function MakeExceptionEvent(exec_state, exception, uncaught) { 1038 return new ExceptionEvent(exec_state, exception, uncaught); 1039 } 1040 1041 1042 function ExceptionEvent(exec_state, exception, uncaught) { 1043 this.exec_state_ = exec_state; 1044 this.exception_ = exception; 1045 this.uncaught_ = uncaught; 1046 } 1047 1048 1049 ExceptionEvent.prototype.executionState = function() { 1050 return this.exec_state_; 1051 }; 1052 1053 1054 ExceptionEvent.prototype.eventType = function() { 1055 return Debug.DebugEvent.Exception; 1056 }; 1057 1058 1059 ExceptionEvent.prototype.exception = function() { 1060 return this.exception_; 1061 } 1062 1063 1064 ExceptionEvent.prototype.uncaught = function() { 1065 return this.uncaught_; 1066 } 1067 1068 1069 ExceptionEvent.prototype.func = function() { 1070 return this.exec_state_.frame(0).func(); 1071 }; 1072 1073 1074 ExceptionEvent.prototype.sourceLine = function() { 1075 return this.exec_state_.frame(0).sourceLine(); 1076 }; 1077 1078 1079 ExceptionEvent.prototype.sourceColumn = function() { 1080 return this.exec_state_.frame(0).sourceColumn(); 1081 }; 1082 1083 1084 ExceptionEvent.prototype.sourceLineText = function() { 1085 return this.exec_state_.frame(0).sourceLineText(); 1086 }; 1087 1088 1089 ExceptionEvent.prototype.toJSONProtocol = function() { 1090 var o = new ProtocolMessage(); 1091 o.event = "exception"; 1092 o.body = { uncaught: this.uncaught_, 1093 exception: MakeMirror(this.exception_) 1094 }; 1095 1096 // Exceptions might happen whithout any JavaScript frames. 1097 if (this.exec_state_.frameCount() > 0) { 1098 o.body.sourceLine = this.sourceLine(); 1099 o.body.sourceColumn = this.sourceColumn(); 1100 o.body.sourceLineText = this.sourceLineText(); 1101 1102 // Add script information to the event if available. 1103 var script = this.func().script(); 1104 if (script) { 1105 o.body.script = MakeScriptObject_(script, false); 1106 } 1107 } else { 1108 o.body.sourceLine = -1; 1109 } 1110 1111 return o.toJSONProtocol(); 1112 }; 1113 1114 1115 function MakeCompileEvent(exec_state, script, before) { 1116 return new CompileEvent(exec_state, script, before); 1117 } 1118 1119 1120 function CompileEvent(exec_state, script, before) { 1121 this.exec_state_ = exec_state; 1122 this.script_ = MakeMirror(script); 1123 this.before_ = before; 1124 } 1125 1126 1127 CompileEvent.prototype.executionState = function() { 1128 return this.exec_state_; 1129 }; 1130 1131 1132 CompileEvent.prototype.eventType = function() { 1133 if (this.before_) { 1134 return Debug.DebugEvent.BeforeCompile; 1135 } else { 1136 return Debug.DebugEvent.AfterCompile; 1137 } 1138 }; 1139 1140 1141 CompileEvent.prototype.script = function() { 1142 return this.script_; 1143 }; 1144 1145 1146 CompileEvent.prototype.toJSONProtocol = function() { 1147 var o = new ProtocolMessage(); 1148 o.running = true; 1149 if (this.before_) { 1150 o.event = "beforeCompile"; 1151 } else { 1152 o.event = "afterCompile"; 1153 } 1154 o.body = {}; 1155 o.body.script = this.script_; 1156 1157 return o.toJSONProtocol(); 1158 } 1159 1160 1161 function MakeNewFunctionEvent(func) { 1162 return new NewFunctionEvent(func); 1163 } 1164 1165 1166 function NewFunctionEvent(func) { 1167 this.func = func; 1168 } 1169 1170 1171 NewFunctionEvent.prototype.eventType = function() { 1172 return Debug.DebugEvent.NewFunction; 1173 }; 1174 1175 1176 NewFunctionEvent.prototype.name = function() { 1177 return this.func.name; 1178 }; 1179 1180 1181 NewFunctionEvent.prototype.setBreakPoint = function(p) { 1182 Debug.setBreakPoint(this.func, p || 0); 1183 }; 1184 1185 1186 function MakeScriptCollectedEvent(exec_state, id) { 1187 return new ScriptCollectedEvent(exec_state, id); 1188 } 1189 1190 1191 function ScriptCollectedEvent(exec_state, id) { 1192 this.exec_state_ = exec_state; 1193 this.id_ = id; 1194 } 1195 1196 1197 ScriptCollectedEvent.prototype.id = function() { 1198 return this.id_; 1199 }; 1200 1201 1202 ScriptCollectedEvent.prototype.executionState = function() { 1203 return this.exec_state_; 1204 }; 1205 1206 1207 ScriptCollectedEvent.prototype.toJSONProtocol = function() { 1208 var o = new ProtocolMessage(); 1209 o.running = true; 1210 o.event = "scriptCollected"; 1211 o.body = {}; 1212 o.body.script = { id: this.id() }; 1213 return o.toJSONProtocol(); 1214 } 1215 1216 1217 function MakeScriptObject_(script, include_source) { 1218 var o = { id: script.id(), 1219 name: script.name(), 1220 lineOffset: script.lineOffset(), 1221 columnOffset: script.columnOffset(), 1222 lineCount: script.lineCount(), 1223 }; 1224 if (!IS_UNDEFINED(script.data())) { 1225 o.data = script.data(); 1226 } 1227 if (include_source) { 1228 o.source = script.source(); 1229 } 1230 return o; 1231 }; 1232 1233 1234 function DebugCommandProcessor(exec_state, opt_is_running) { 1235 this.exec_state_ = exec_state; 1236 this.running_ = opt_is_running || false; 1237 }; 1238 1239 1240 DebugCommandProcessor.prototype.processDebugRequest = function (request) { 1241 return this.processDebugJSONRequest(request); 1242 } 1243 1244 1245 function ProtocolMessage(request) { 1246 // Update sequence number. 1247 this.seq = next_response_seq++; 1248 1249 if (request) { 1250 // If message is based on a request this is a response. Fill the initial 1251 // response from the request. 1252 this.type = 'response'; 1253 this.request_seq = request.seq; 1254 this.command = request.command; 1255 } else { 1256 // If message is not based on a request it is a dabugger generated event. 1257 this.type = 'event'; 1258 } 1259 this.success = true; 1260 // Handler may set this field to control debugger state. 1261 this.running = undefined; 1262 } 1263 1264 1265 ProtocolMessage.prototype.setOption = function(name, value) { 1266 if (!this.options_) { 1267 this.options_ = {}; 1268 } 1269 this.options_[name] = value; 1270 } 1271 1272 1273 ProtocolMessage.prototype.failed = function(message) { 1274 this.success = false; 1275 this.message = message; 1276 } 1277 1278 1279 ProtocolMessage.prototype.toJSONProtocol = function() { 1280 // Encode the protocol header. 1281 var json = {}; 1282 json.seq= this.seq; 1283 if (this.request_seq) { 1284 json.request_seq = this.request_seq; 1285 } 1286 json.type = this.type; 1287 if (this.event) { 1288 json.event = this.event; 1289 } 1290 if (this.command) { 1291 json.command = this.command; 1292 } 1293 if (this.success) { 1294 json.success = this.success; 1295 } else { 1296 json.success = false; 1297 } 1298 if (this.body) { 1299 // Encode the body part. 1300 var bodyJson; 1301 var serializer = MakeMirrorSerializer(true, this.options_); 1302 if (this.body instanceof Mirror) { 1303 bodyJson = serializer.serializeValue(this.body); 1304 } else if (this.body instanceof Array) { 1305 bodyJson = []; 1306 for (var i = 0; i < this.body.length; i++) { 1307 if (this.body[i] instanceof Mirror) { 1308 bodyJson.push(serializer.serializeValue(this.body[i])); 1309 } else { 1310 bodyJson.push(ObjectToProtocolObject_(this.body[i], serializer)); 1311 } 1312 } 1313 } else { 1314 bodyJson = ObjectToProtocolObject_(this.body, serializer); 1315 } 1316 json.body = bodyJson; 1317 json.refs = serializer.serializeReferencedObjects(); 1318 } 1319 if (this.message) { 1320 json.message = this.message; 1321 } 1322 json.running = this.running; 1323 return JSON.stringify(json); 1324 } 1325 1326 1327 DebugCommandProcessor.prototype.createResponse = function(request) { 1328 return new ProtocolMessage(request); 1329 }; 1330 1331 1332 DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request) { 1333 var request; // Current request. 1334 var response; // Generated response. 1335 try { 1336 try { 1337 // Convert the JSON string to an object. 1338 request = %CompileString('(' + json_request + ')')(); 1339 1340 // Create an initial response. 1341 response = this.createResponse(request); 1342 1343 if (!request.type) { 1344 throw new Error('Type not specified'); 1345 } 1346 1347 if (request.type != 'request') { 1348 throw new Error("Illegal type '" + request.type + "' in request"); 1349 } 1350 1351 if (!request.command) { 1352 throw new Error('Command not specified'); 1353 } 1354 1355 if (request.arguments) { 1356 var args = request.arguments; 1357 // TODO(yurys): remove request.arguments.compactFormat check once 1358 // ChromeDevTools are switched to 'inlineRefs' 1359 if (args.inlineRefs || args.compactFormat) { 1360 response.setOption('inlineRefs', true); 1361 } 1362 if (!IS_UNDEFINED(args.maxStringLength)) { 1363 response.setOption('maxStringLength', args.maxStringLength); 1364 } 1365 } 1366 1367 if (request.command == 'continue') { 1368 this.continueRequest_(request, response); 1369 } else if (request.command == 'break') { 1370 this.breakRequest_(request, response); 1371 } else if (request.command == 'setbreakpoint') { 1372 this.setBreakPointRequest_(request, response); 1373 } else if (request.command == 'changebreakpoint') { 1374 this.changeBreakPointRequest_(request, response); 1375 } else if (request.command == 'clearbreakpoint') { 1376 this.clearBreakPointRequest_(request, response); 1377 } else if (request.command == 'clearbreakpointgroup') { 1378 this.clearBreakPointGroupRequest_(request, response); 1379 } else if (request.command == 'disconnect') { 1380 this.disconnectRequest_(request, response); 1381 } else if (request.command == 'setexceptionbreak') { 1382 this.setExceptionBreakRequest_(request, response); 1383 } else if (request.command == 'listbreakpoints') { 1384 this.listBreakpointsRequest_(request, response); 1385 } else if (request.command == 'backtrace') { 1386 this.backtraceRequest_(request, response); 1387 } else if (request.command == 'frame') { 1388 this.frameRequest_(request, response); 1389 } else if (request.command == 'scopes') { 1390 this.scopesRequest_(request, response); 1391 } else if (request.command == 'scope') { 1392 this.scopeRequest_(request, response); 1393 } else if (request.command == 'evaluate') { 1394 this.evaluateRequest_(request, response); 1395 } else if (lol_is_enabled && request.command == 'getobj') { 1396 this.getobjRequest_(request, response); 1397 } else if (request.command == 'lookup') { 1398 this.lookupRequest_(request, response); 1399 } else if (request.command == 'references') { 1400 this.referencesRequest_(request, response); 1401 } else if (request.command == 'source') { 1402 this.sourceRequest_(request, response); 1403 } else if (request.command == 'scripts') { 1404 this.scriptsRequest_(request, response); 1405 } else if (request.command == 'threads') { 1406 this.threadsRequest_(request, response); 1407 } else if (request.command == 'suspend') { 1408 this.suspendRequest_(request, response); 1409 } else if (request.command == 'version') { 1410 this.versionRequest_(request, response); 1411 } else if (request.command == 'profile') { 1412 this.profileRequest_(request, response); 1413 } else if (request.command == 'changelive') { 1414 this.changeLiveRequest_(request, response); 1415 } else if (request.command == 'flags') { 1416 this.debuggerFlagsRequest_(request, response); 1417 } else if (request.command == 'v8flags') { 1418 this.v8FlagsRequest_(request, response); 1419 1420 // GC tools: 1421 } else if (request.command == 'gc') { 1422 this.gcRequest_(request, response); 1423 1424 // LiveObjectList tools: 1425 } else if (lol_is_enabled && request.command == 'lol-capture') { 1426 this.lolCaptureRequest_(request, response); 1427 } else if (lol_is_enabled && request.command == 'lol-delete') { 1428 this.lolDeleteRequest_(request, response); 1429 } else if (lol_is_enabled && request.command == 'lol-diff') { 1430 this.lolDiffRequest_(request, response); 1431 } else if (lol_is_enabled && request.command == 'lol-getid') { 1432 this.lolGetIdRequest_(request, response); 1433 } else if (lol_is_enabled && request.command == 'lol-info') { 1434 this.lolInfoRequest_(request, response); 1435 } else if (lol_is_enabled && request.command == 'lol-reset') { 1436 this.lolResetRequest_(request, response); 1437 } else if (lol_is_enabled && request.command == 'lol-retainers') { 1438 this.lolRetainersRequest_(request, response); 1439 } else if (lol_is_enabled && request.command == 'lol-path') { 1440 this.lolPathRequest_(request, response); 1441 } else if (lol_is_enabled && request.command == 'lol-print') { 1442 this.lolPrintRequest_(request, response); 1443 } else if (lol_is_enabled && request.command == 'lol-stats') { 1444 this.lolStatsRequest_(request, response); 1445 1446 } else { 1447 throw new Error('Unknown command "' + request.command + '" in request'); 1448 } 1449 } catch (e) { 1450 // If there is no response object created one (without command). 1451 if (!response) { 1452 response = this.createResponse(); 1453 } 1454 response.success = false; 1455 response.message = %ToString(e); 1456 } 1457 1458 // Return the response as a JSON encoded string. 1459 try { 1460 if (!IS_UNDEFINED(response.running)) { 1461 // Response controls running state. 1462 this.running_ = response.running; 1463 } 1464 response.running = this.running_; 1465 return response.toJSONProtocol(); 1466 } catch (e) { 1467 // Failed to generate response - return generic error. 1468 return '{"seq":' + response.seq + ',' + 1469 '"request_seq":' + request.seq + ',' + 1470 '"type":"response",' + 1471 '"success":false,' + 1472 '"message":"Internal error: ' + %ToString(e) + '"}'; 1473 } 1474 } catch (e) { 1475 // Failed in one of the catch blocks above - most generic error. 1476 return '{"seq":0,"type":"response","success":false,"message":"Internal error"}'; 1477 } 1478 }; 1479 1480 1481 DebugCommandProcessor.prototype.continueRequest_ = function(request, response) { 1482 // Check for arguments for continue. 1483 if (request.arguments) { 1484 var count = 1; 1485 var action = Debug.StepAction.StepIn; 1486 1487 // Pull out arguments. 1488 var stepaction = request.arguments.stepaction; 1489 var stepcount = request.arguments.stepcount; 1490 1491 // Get the stepcount argument if any. 1492 if (stepcount) { 1493 count = %ToNumber(stepcount); 1494 if (count < 0) { 1495 throw new Error('Invalid stepcount argument "' + stepcount + '".'); 1496 } 1497 } 1498 1499 // Get the stepaction argument. 1500 if (stepaction) { 1501 if (stepaction == 'in') { 1502 action = Debug.StepAction.StepIn; 1503 } else if (stepaction == 'min') { 1504 action = Debug.StepAction.StepMin; 1505 } else if (stepaction == 'next') { 1506 action = Debug.StepAction.StepNext; 1507 } else if (stepaction == 'out') { 1508 action = Debug.StepAction.StepOut; 1509 } else { 1510 throw new Error('Invalid stepaction argument "' + stepaction + '".'); 1511 } 1512 } 1513 1514 // Setup the VM for stepping. 1515 this.exec_state_.prepareStep(action, count); 1516 } 1517 1518 // VM should be running after executing this request. 1519 response.running = true; 1520 }; 1521 1522 1523 DebugCommandProcessor.prototype.breakRequest_ = function(request, response) { 1524 // Ignore as break command does not do anything when broken. 1525 }; 1526 1527 1528 DebugCommandProcessor.prototype.setBreakPointRequest_ = 1529 function(request, response) { 1530 // Check for legal request. 1531 if (!request.arguments) { 1532 response.failed('Missing arguments'); 1533 return; 1534 } 1535 1536 // Pull out arguments. 1537 var type = request.arguments.type; 1538 var target = request.arguments.target; 1539 var line = request.arguments.line; 1540 var column = request.arguments.column; 1541 var enabled = IS_UNDEFINED(request.arguments.enabled) ? 1542 true : request.arguments.enabled; 1543 var condition = request.arguments.condition; 1544 var ignoreCount = request.arguments.ignoreCount; 1545 var groupId = request.arguments.groupId; 1546 1547 // Check for legal arguments. 1548 if (!type || IS_UNDEFINED(target)) { 1549 response.failed('Missing argument "type" or "target"'); 1550 return; 1551 } 1552 if (type != 'function' && type != 'handle' && 1553 type != 'script' && type != 'scriptId') { 1554 response.failed('Illegal type "' + type + '"'); 1555 return; 1556 } 1557 1558 // Either function or script break point. 1559 var break_point_number; 1560 if (type == 'function') { 1561 // Handle function break point. 1562 if (!IS_STRING(target)) { 1563 response.failed('Argument "target" is not a string value'); 1564 return; 1565 } 1566 var f; 1567 try { 1568 // Find the function through a global evaluate. 1569 f = this.exec_state_.evaluateGlobal(target).value(); 1570 } catch (e) { 1571 response.failed('Error: "' + %ToString(e) + 1572 '" evaluating "' + target + '"'); 1573 return; 1574 } 1575 if (!IS_FUNCTION(f)) { 1576 response.failed('"' + target + '" does not evaluate to a function'); 1577 return; 1578 } 1579 1580 // Set function break point. 1581 break_point_number = Debug.setBreakPoint(f, line, column, condition); 1582 } else if (type == 'handle') { 1583 // Find the object pointed by the specified handle. 1584 var handle = parseInt(target, 10); 1585 var mirror = LookupMirror(handle); 1586 if (!mirror) { 1587 return response.failed('Object #' + handle + '# not found'); 1588 } 1589 if (!mirror.isFunction()) { 1590 return response.failed('Object #' + handle + '# is not a function'); 1591 } 1592 1593 // Set function break point. 1594 break_point_number = Debug.setBreakPoint(mirror.value(), 1595 line, column, condition); 1596 } else if (type == 'script') { 1597 // set script break point. 1598 break_point_number = 1599 Debug.setScriptBreakPointByName(target, line, column, condition, 1600 groupId); 1601 } else { // type == 'scriptId. 1602 break_point_number = 1603 Debug.setScriptBreakPointById(target, line, column, condition, groupId); 1604 } 1605 1606 // Set additional break point properties. 1607 var break_point = Debug.findBreakPoint(break_point_number); 1608 if (ignoreCount) { 1609 Debug.changeBreakPointIgnoreCount(break_point_number, ignoreCount); 1610 } 1611 if (!enabled) { 1612 Debug.disableBreakPoint(break_point_number); 1613 } 1614 1615 // Add the break point number to the response. 1616 response.body = { type: type, 1617 breakpoint: break_point_number } 1618 1619 // Add break point information to the response. 1620 if (break_point instanceof ScriptBreakPoint) { 1621 if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) { 1622 response.body.type = 'scriptId'; 1623 response.body.script_id = break_point.script_id(); 1624 } else { 1625 response.body.type = 'scriptName'; 1626 response.body.script_name = break_point.script_name(); 1627 } 1628 response.body.line = break_point.line(); 1629 response.body.column = break_point.column(); 1630 response.body.actual_locations = break_point.actual_locations(); 1631 } else { 1632 response.body.type = 'function'; 1633 response.body.actual_locations = [break_point.actual_location]; 1634 } 1635 }; 1636 1637 1638 DebugCommandProcessor.prototype.changeBreakPointRequest_ = function(request, response) { 1639 // Check for legal request. 1640 if (!request.arguments) { 1641 response.failed('Missing arguments'); 1642 return; 1643 } 1644 1645 // Pull out arguments. 1646 var break_point = %ToNumber(request.arguments.breakpoint); 1647 var enabled = request.arguments.enabled; 1648 var condition = request.arguments.condition; 1649 var ignoreCount = request.arguments.ignoreCount; 1650 1651 // Check for legal arguments. 1652 if (!break_point) { 1653 response.failed('Missing argument "breakpoint"'); 1654 return; 1655 } 1656 1657 // Change enabled state if supplied. 1658 if (!IS_UNDEFINED(enabled)) { 1659 if (enabled) { 1660 Debug.enableBreakPoint(break_point); 1661 } else { 1662 Debug.disableBreakPoint(break_point); 1663 } 1664 } 1665 1666 // Change condition if supplied 1667 if (!IS_UNDEFINED(condition)) { 1668 Debug.changeBreakPointCondition(break_point, condition); 1669 } 1670 1671 // Change ignore count if supplied 1672 if (!IS_UNDEFINED(ignoreCount)) { 1673 Debug.changeBreakPointIgnoreCount(break_point, ignoreCount); 1674 } 1675 } 1676 1677 1678 DebugCommandProcessor.prototype.clearBreakPointGroupRequest_ = function(request, response) { 1679 // Check for legal request. 1680 if (!request.arguments) { 1681 response.failed('Missing arguments'); 1682 return; 1683 } 1684 1685 // Pull out arguments. 1686 var group_id = request.arguments.groupId; 1687 1688 // Check for legal arguments. 1689 if (!group_id) { 1690 response.failed('Missing argument "groupId"'); 1691 return; 1692 } 1693 1694 var cleared_break_points = []; 1695 var new_script_break_points = []; 1696 for (var i = 0; i < script_break_points.length; i++) { 1697 var next_break_point = script_break_points[i]; 1698 if (next_break_point.groupId() == group_id) { 1699 cleared_break_points.push(next_break_point.number()); 1700 next_break_point.clear(); 1701 } else { 1702 new_script_break_points.push(next_break_point); 1703 } 1704 } 1705 script_break_points = new_script_break_points; 1706 1707 // Add the cleared break point numbers to the response. 1708 response.body = { breakpoints: cleared_break_points }; 1709 } 1710 1711 1712 DebugCommandProcessor.prototype.clearBreakPointRequest_ = function(request, response) { 1713 // Check for legal request. 1714 if (!request.arguments) { 1715 response.failed('Missing arguments'); 1716 return; 1717 } 1718 1719 // Pull out arguments. 1720 var break_point = %ToNumber(request.arguments.breakpoint); 1721 1722 // Check for legal arguments. 1723 if (!break_point) { 1724 response.failed('Missing argument "breakpoint"'); 1725 return; 1726 } 1727 1728 // Clear break point. 1729 Debug.clearBreakPoint(break_point); 1730 1731 // Add the cleared break point number to the response. 1732 response.body = { breakpoint: break_point } 1733 } 1734 1735 1736 DebugCommandProcessor.prototype.listBreakpointsRequest_ = function(request, response) { 1737 var array = []; 1738 for (var i = 0; i < script_break_points.length; i++) { 1739 var break_point = script_break_points[i]; 1740 1741 var description = { 1742 number: break_point.number(), 1743 line: break_point.line(), 1744 column: break_point.column(), 1745 groupId: break_point.groupId(), 1746 hit_count: break_point.hit_count(), 1747 active: break_point.active(), 1748 condition: break_point.condition(), 1749 ignoreCount: break_point.ignoreCount(), 1750 actual_locations: break_point.actual_locations() 1751 } 1752 1753 if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) { 1754 description.type = 'scriptId'; 1755 description.script_id = break_point.script_id(); 1756 } else { 1757 description.type = 'scriptName'; 1758 description.script_name = break_point.script_name(); 1759 } 1760 array.push(description); 1761 } 1762 1763 response.body = { 1764 breakpoints: array, 1765 breakOnExceptions: Debug.isBreakOnException(), 1766 breakOnUncaughtExceptions: Debug.isBreakOnUncaughtException() 1767 } 1768 } 1769 1770 1771 DebugCommandProcessor.prototype.disconnectRequest_ = 1772 function(request, response) { 1773 Debug.disableAllBreakPoints(); 1774 this.continueRequest_(request, response); 1775 } 1776 1777 1778 DebugCommandProcessor.prototype.setExceptionBreakRequest_ = 1779 function(request, response) { 1780 // Check for legal request. 1781 if (!request.arguments) { 1782 response.failed('Missing arguments'); 1783 return; 1784 } 1785 1786 // Pull out and check the 'type' argument: 1787 var type = request.arguments.type; 1788 if (!type) { 1789 response.failed('Missing argument "type"'); 1790 return; 1791 } 1792 1793 // Initialize the default value of enable: 1794 var enabled; 1795 if (type == 'all') { 1796 enabled = !Debug.isBreakOnException(); 1797 } else if (type == 'uncaught') { 1798 enabled = !Debug.isBreakOnUncaughtException(); 1799 } 1800 1801 // Pull out and check the 'enabled' argument if present: 1802 if (!IS_UNDEFINED(request.arguments.enabled)) { 1803 enabled = request.arguments.enabled; 1804 if ((enabled != true) && (enabled != false)) { 1805 response.failed('Illegal value for "enabled":"' + enabled + '"'); 1806 } 1807 } 1808 1809 // Now set the exception break state: 1810 if (type == 'all') { 1811 %ChangeBreakOnException(Debug.ExceptionBreak.Caught, enabled); 1812 } else if (type == 'uncaught') { 1813 %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, enabled); 1814 } else { 1815 response.failed('Unknown "type":"' + type + '"'); 1816 } 1817 1818 // Add the cleared break point number to the response. 1819 response.body = { 'type': type, 'enabled': enabled }; 1820 } 1821 1822 1823 DebugCommandProcessor.prototype.backtraceRequest_ = function(request, response) { 1824 // Get the number of frames. 1825 var total_frames = this.exec_state_.frameCount(); 1826 1827 // Create simple response if there are no frames. 1828 if (total_frames == 0) { 1829 response.body = { 1830 totalFrames: total_frames 1831 } 1832 return; 1833 } 1834 1835 // Default frame range to include in backtrace. 1836 var from_index = 0 1837 var to_index = kDefaultBacktraceLength; 1838 1839 // Get the range from the arguments. 1840 if (request.arguments) { 1841 if (request.arguments.fromFrame) { 1842 from_index = request.arguments.fromFrame; 1843 } 1844 if (request.arguments.toFrame) { 1845 to_index = request.arguments.toFrame; 1846 } 1847 if (request.arguments.bottom) { 1848 var tmp_index = total_frames - from_index; 1849 from_index = total_frames - to_index 1850 to_index = tmp_index; 1851 } 1852 if (from_index < 0 || to_index < 0) { 1853 return response.failed('Invalid frame number'); 1854 } 1855 } 1856 1857 // Adjust the index. 1858 to_index = Math.min(total_frames, to_index); 1859 1860 if (to_index <= from_index) { 1861 var error = 'Invalid frame range'; 1862 return response.failed(error); 1863 } 1864 1865 // Create the response body. 1866 var frames = []; 1867 for (var i = from_index; i < to_index; i++) { 1868 frames.push(this.exec_state_.frame(i)); 1869 } 1870 response.body = { 1871 fromFrame: from_index, 1872 toFrame: to_index, 1873 totalFrames: total_frames, 1874 frames: frames 1875 } 1876 }; 1877 1878 1879 DebugCommandProcessor.prototype.frameRequest_ = function(request, response) { 1880 // No frames no source. 1881 if (this.exec_state_.frameCount() == 0) { 1882 return response.failed('No frames'); 1883 } 1884 1885 // With no arguments just keep the selected frame. 1886 if (request.arguments) { 1887 var index = request.arguments.number; 1888 if (index < 0 || this.exec_state_.frameCount() <= index) { 1889 return response.failed('Invalid frame number'); 1890 } 1891 1892 this.exec_state_.setSelectedFrame(request.arguments.number); 1893 } 1894 response.body = this.exec_state_.frame(); 1895 }; 1896 1897 1898 DebugCommandProcessor.prototype.frameForScopeRequest_ = function(request) { 1899 // Get the frame for which the scope or scopes are requested. With no frameNumber 1900 // argument use the currently selected frame. 1901 if (request.arguments && !IS_UNDEFINED(request.arguments.frameNumber)) { 1902 frame_index = request.arguments.frameNumber; 1903 if (frame_index < 0 || this.exec_state_.frameCount() <= frame_index) { 1904 return response.failed('Invalid frame number'); 1905 } 1906 return this.exec_state_.frame(frame_index); 1907 } else { 1908 return this.exec_state_.frame(); 1909 } 1910 } 1911 1912 1913 DebugCommandProcessor.prototype.scopesRequest_ = function(request, response) { 1914 // No frames no scopes. 1915 if (this.exec_state_.frameCount() == 0) { 1916 return response.failed('No scopes'); 1917 } 1918 1919 // Get the frame for which the scopes are requested. 1920 var frame = this.frameForScopeRequest_(request); 1921 1922 // Fill all scopes for this frame. 1923 var total_scopes = frame.scopeCount(); 1924 var scopes = []; 1925 for (var i = 0; i < total_scopes; i++) { 1926 scopes.push(frame.scope(i)); 1927 } 1928 response.body = { 1929 fromScope: 0, 1930 toScope: total_scopes, 1931 totalScopes: total_scopes, 1932 scopes: scopes 1933 } 1934 }; 1935 1936 1937 DebugCommandProcessor.prototype.scopeRequest_ = function(request, response) { 1938 // No frames no scopes. 1939 if (this.exec_state_.frameCount() == 0) { 1940 return response.failed('No scopes'); 1941 } 1942 1943 // Get the frame for which the scope is requested. 1944 var frame = this.frameForScopeRequest_(request); 1945 1946 // With no scope argument just return top scope. 1947 var scope_index = 0; 1948 if (request.arguments && !IS_UNDEFINED(request.arguments.number)) { 1949 scope_index = %ToNumber(request.arguments.number); 1950 if (scope_index < 0 || frame.scopeCount() <= scope_index) { 1951 return response.failed('Invalid scope number'); 1952 } 1953 } 1954 1955 response.body = frame.scope(scope_index); 1956 }; 1957 1958 1959 DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) { 1960 if (!request.arguments) { 1961 return response.failed('Missing arguments'); 1962 } 1963 1964 // Pull out arguments. 1965 var expression = request.arguments.expression; 1966 var frame = request.arguments.frame; 1967 var global = request.arguments.global; 1968 var disable_break = request.arguments.disable_break; 1969 var additional_context = request.arguments.additional_context; 1970 1971 // The expression argument could be an integer so we convert it to a 1972 // string. 1973 try { 1974 expression = String(expression); 1975 } catch(e) { 1976 return response.failed('Failed to convert expression argument to string'); 1977 } 1978 1979 // Check for legal arguments. 1980 if (!IS_UNDEFINED(frame) && global) { 1981 return response.failed('Arguments "frame" and "global" are exclusive'); 1982 } 1983 1984 var additional_context_object; 1985 if (additional_context) { 1986 additional_context_object = {}; 1987 for (var i = 0; i < additional_context.length; i++) { 1988 var mapping = additional_context[i]; 1989 if (!IS_STRING(mapping.name) || !IS_NUMBER(mapping.handle)) { 1990 return response.failed("Context element #" + i + 1991 " must contain name:string and handle:number"); 1992 } 1993 var context_value_mirror = LookupMirror(mapping.handle); 1994 if (!context_value_mirror) { 1995 return response.failed("Context object '" + mapping.name + 1996 "' #" + mapping.handle + "# not found"); 1997 } 1998 additional_context_object[mapping.name] = context_value_mirror.value(); 1999 } 2000 } 2001 2002 // Global evaluate. 2003 if (global) { 2004 // Evaluate in the global context. 2005 response.body = this.exec_state_.evaluateGlobal( 2006 expression, Boolean(disable_break), additional_context_object); 2007 return; 2008 } 2009 2010 // Default value for disable_break is true. 2011 if (IS_UNDEFINED(disable_break)) { 2012 disable_break = true; 2013 } 2014 2015 // No frames no evaluate in frame. 2016 if (this.exec_state_.frameCount() == 0) { 2017 return response.failed('No frames'); 2018 } 2019 2020 // Check whether a frame was specified. 2021 if (!IS_UNDEFINED(frame)) { 2022 var frame_number = %ToNumber(frame); 2023 if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) { 2024 return response.failed('Invalid frame "' + frame + '"'); 2025 } 2026 // Evaluate in the specified frame. 2027 response.body = this.exec_state_.frame(frame_number).evaluate( 2028 expression, Boolean(disable_break), additional_context_object); 2029 return; 2030 } else { 2031 // Evaluate in the selected frame. 2032 response.body = this.exec_state_.frame().evaluate( 2033 expression, Boolean(disable_break), additional_context_object); 2034 return; 2035 } 2036 }; 2037 2038 2039 DebugCommandProcessor.prototype.getobjRequest_ = function(request, response) { 2040 if (!request.arguments) { 2041 return response.failed('Missing arguments'); 2042 } 2043 2044 // Pull out arguments. 2045 var obj_id = request.arguments.obj_id; 2046 2047 // Check for legal arguments. 2048 if (IS_UNDEFINED(obj_id)) { 2049 return response.failed('Argument "obj_id" missing'); 2050 } 2051 2052 // Dump the object. 2053 response.body = MakeMirror(%GetLOLObj(obj_id)); 2054 }; 2055 2056 2057 DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) { 2058 if (!request.arguments) { 2059 return response.failed('Missing arguments'); 2060 } 2061 2062 // Pull out arguments. 2063 var handles = request.arguments.handles; 2064 2065 // Check for legal arguments. 2066 if (IS_UNDEFINED(handles)) { 2067 return response.failed('Argument "handles" missing'); 2068 } 2069 2070 // Set 'includeSource' option for script lookup. 2071 if (!IS_UNDEFINED(request.arguments.includeSource)) { 2072 includeSource = %ToBoolean(request.arguments.includeSource); 2073 response.setOption('includeSource', includeSource); 2074 } 2075 2076 // Lookup handles. 2077 var mirrors = {}; 2078 for (var i = 0; i < handles.length; i++) { 2079 var handle = handles[i]; 2080 var mirror = LookupMirror(handle); 2081 if (!mirror) { 2082 return response.failed('Object #' + handle + '# not found'); 2083 } 2084 mirrors[handle] = mirror; 2085 } 2086 response.body = mirrors; 2087 }; 2088 2089 2090 DebugCommandProcessor.prototype.referencesRequest_ = 2091 function(request, response) { 2092 if (!request.arguments) { 2093 return response.failed('Missing arguments'); 2094 } 2095 2096 // Pull out arguments. 2097 var type = request.arguments.type; 2098 var handle = request.arguments.handle; 2099 2100 // Check for legal arguments. 2101 if (IS_UNDEFINED(type)) { 2102 return response.failed('Argument "type" missing'); 2103 } 2104 if (IS_UNDEFINED(handle)) { 2105 return response.failed('Argument "handle" missing'); 2106 } 2107 if (type != 'referencedBy' && type != 'constructedBy') { 2108 return response.failed('Invalid type "' + type + '"'); 2109 } 2110 2111 // Lookup handle and return objects with references the object. 2112 var mirror = LookupMirror(handle); 2113 if (mirror) { 2114 if (type == 'referencedBy') { 2115 response.body = mirror.referencedBy(); 2116 } else { 2117 response.body = mirror.constructedBy(); 2118 } 2119 } else { 2120 return response.failed('Object #' + handle + '# not found'); 2121 } 2122 }; 2123 2124 2125 DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) { 2126 // No frames no source. 2127 if (this.exec_state_.frameCount() == 0) { 2128 return response.failed('No source'); 2129 } 2130 2131 var from_line; 2132 var to_line; 2133 var frame = this.exec_state_.frame(); 2134 if (request.arguments) { 2135 // Pull out arguments. 2136 from_line = request.arguments.fromLine; 2137 to_line = request.arguments.toLine; 2138 2139 if (!IS_UNDEFINED(request.arguments.frame)) { 2140 var frame_number = %ToNumber(request.arguments.frame); 2141 if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) { 2142 return response.failed('Invalid frame "' + frame + '"'); 2143 } 2144 frame = this.exec_state_.frame(frame_number); 2145 } 2146 } 2147 2148 // Get the script selected. 2149 var script = frame.func().script(); 2150 if (!script) { 2151 return response.failed('No source'); 2152 } 2153 2154 // Get the source slice and fill it into the response. 2155 var slice = script.sourceSlice(from_line, to_line); 2156 if (!slice) { 2157 return response.failed('Invalid line interval'); 2158 } 2159 response.body = {}; 2160 response.body.source = slice.sourceText(); 2161 response.body.fromLine = slice.from_line; 2162 response.body.toLine = slice.to_line; 2163 response.body.fromPosition = slice.from_position; 2164 response.body.toPosition = slice.to_position; 2165 response.body.totalLines = script.lineCount(); 2166 }; 2167 2168 2169 DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) { 2170 var types = ScriptTypeFlag(Debug.ScriptType.Normal); 2171 var includeSource = false; 2172 var idsToInclude = null; 2173 if (request.arguments) { 2174 // Pull out arguments. 2175 if (!IS_UNDEFINED(request.arguments.types)) { 2176 types = %ToNumber(request.arguments.types); 2177 if (isNaN(types) || types < 0) { 2178 return response.failed('Invalid types "' + request.arguments.types + '"'); 2179 } 2180 } 2181 2182 if (!IS_UNDEFINED(request.arguments.includeSource)) { 2183 includeSource = %ToBoolean(request.arguments.includeSource); 2184 response.setOption('includeSource', includeSource); 2185 } 2186 2187 if (IS_ARRAY(request.arguments.ids)) { 2188 idsToInclude = {}; 2189 var ids = request.arguments.ids; 2190 for (var i = 0; i < ids.length; i++) { 2191 idsToInclude[ids[i]] = true; 2192 } 2193 } 2194 2195 var filterStr = null; 2196 var filterNum = null; 2197 if (!IS_UNDEFINED(request.arguments.filter)) { 2198 var num = %ToNumber(request.arguments.filter); 2199 if (!isNaN(num)) { 2200 filterNum = num; 2201 } 2202 filterStr = request.arguments.filter; 2203 } 2204 } 2205 2206 // Collect all scripts in the heap. 2207 var scripts = %DebugGetLoadedScripts(); 2208 2209 response.body = []; 2210 2211 for (var i = 0; i < scripts.length; i++) { 2212 if (idsToInclude && !idsToInclude[scripts[i].id]) { 2213 continue; 2214 } 2215 if (filterStr || filterNum) { 2216 var script = scripts[i]; 2217 var found = false; 2218 if (filterNum && !found) { 2219 if (script.id && script.id === filterNum) { 2220 found = true; 2221 } 2222 } 2223 if (filterStr && !found) { 2224 if (script.name && script.name.indexOf(filterStr) >= 0) { 2225 found = true; 2226 } 2227 } 2228 if (!found) continue; 2229 } 2230 if (types & ScriptTypeFlag(scripts[i].type)) { 2231 response.body.push(MakeMirror(scripts[i])); 2232 } 2233 } 2234 }; 2235 2236 2237 DebugCommandProcessor.prototype.threadsRequest_ = function(request, response) { 2238 // Get the number of threads. 2239 var total_threads = this.exec_state_.threadCount(); 2240 2241 // Get information for all threads. 2242 var threads = []; 2243 for (var i = 0; i < total_threads; i++) { 2244 var details = %GetThreadDetails(this.exec_state_.break_id, i); 2245 var thread_info = { current: details[0], 2246 id: details[1] 2247 } 2248 threads.push(thread_info); 2249 } 2250 2251 // Create the response body. 2252 response.body = { 2253 totalThreads: total_threads, 2254 threads: threads 2255 } 2256 }; 2257 2258 2259 DebugCommandProcessor.prototype.suspendRequest_ = function(request, response) { 2260 response.running = false; 2261 }; 2262 2263 2264 DebugCommandProcessor.prototype.versionRequest_ = function(request, response) { 2265 response.body = { 2266 V8Version: %GetV8Version() 2267 } 2268 }; 2269 2270 2271 DebugCommandProcessor.prototype.profileRequest_ = function(request, response) { 2272 if (!request.arguments) { 2273 return response.failed('Missing arguments'); 2274 } 2275 var modules = parseInt(request.arguments.modules); 2276 if (isNaN(modules)) { 2277 return response.failed('Modules is not an integer'); 2278 } 2279 var tag = parseInt(request.arguments.tag); 2280 if (isNaN(tag)) { 2281 tag = 0; 2282 } 2283 if (request.arguments.command == 'resume') { 2284 %ProfilerResume(modules, tag); 2285 } else if (request.arguments.command == 'pause') { 2286 %ProfilerPause(modules, tag); 2287 } else { 2288 return response.failed('Unknown command'); 2289 } 2290 response.body = {}; 2291 }; 2292 2293 2294 DebugCommandProcessor.prototype.changeLiveRequest_ = function(request, response) { 2295 if (!Debug.LiveEdit) { 2296 return response.failed('LiveEdit feature is not supported'); 2297 } 2298 if (!request.arguments) { 2299 return response.failed('Missing arguments'); 2300 } 2301 var script_id = request.arguments.script_id; 2302 var preview_only = !!request.arguments.preview_only; 2303 2304 var scripts = %DebugGetLoadedScripts(); 2305 2306 var the_script = null; 2307 for (var i = 0; i < scripts.length; i++) { 2308 if (scripts[i].id == script_id) { 2309 the_script = scripts[i]; 2310 } 2311 } 2312 if (!the_script) { 2313 response.failed('Script not found'); 2314 return; 2315 } 2316 2317 var change_log = new Array(); 2318 2319 if (!IS_STRING(request.arguments.new_source)) { 2320 throw "new_source argument expected"; 2321 } 2322 2323 var new_source = request.arguments.new_source; 2324 2325 var result_description = Debug.LiveEdit.SetScriptSource(the_script, 2326 new_source, preview_only, change_log); 2327 response.body = {change_log: change_log, result: result_description}; 2328 2329 if (!preview_only && !this.running_ && result_description.stack_modified) { 2330 response.body.stepin_recommended = true; 2331 } 2332 }; 2333 2334 2335 DebugCommandProcessor.prototype.debuggerFlagsRequest_ = function(request, 2336 response) { 2337 // Check for legal request. 2338 if (!request.arguments) { 2339 response.failed('Missing arguments'); 2340 return; 2341 } 2342 2343 // Pull out arguments. 2344 var flags = request.arguments.flags; 2345 2346 response.body = { flags: [] }; 2347 if (!IS_UNDEFINED(flags)) { 2348 for (var i = 0; i < flags.length; i++) { 2349 var name = flags[i].name; 2350 var debugger_flag = debugger_flags[name]; 2351 if (!debugger_flag) { 2352 continue; 2353 } 2354 if ('value' in flags[i]) { 2355 debugger_flag.setValue(flags[i].value); 2356 } 2357 response.body.flags.push({ name: name, value: debugger_flag.getValue() }); 2358 } 2359 } else { 2360 for (var name in debugger_flags) { 2361 var value = debugger_flags[name].getValue(); 2362 response.body.flags.push({ name: name, value: value }); 2363 } 2364 } 2365 } 2366 2367 2368 DebugCommandProcessor.prototype.v8FlagsRequest_ = function(request, response) { 2369 var flags = request.arguments.flags; 2370 if (!flags) flags = ''; 2371 %SetFlags(flags); 2372 }; 2373 2374 2375 DebugCommandProcessor.prototype.gcRequest_ = function(request, response) { 2376 var type = request.arguments.type; 2377 if (!type) type = 'all'; 2378 2379 var before = %GetHeapUsage(); 2380 %CollectGarbage(type); 2381 var after = %GetHeapUsage(); 2382 2383 response.body = { "before": before, "after": after }; 2384 }; 2385 2386 2387 DebugCommandProcessor.prototype.lolCaptureRequest_ = 2388 function(request, response) { 2389 response.body = %CaptureLOL(); 2390 }; 2391 2392 2393 DebugCommandProcessor.prototype.lolDeleteRequest_ = 2394 function(request, response) { 2395 var id = request.arguments.id; 2396 var result = %DeleteLOL(id); 2397 if (result) { 2398 response.body = { id: id }; 2399 } else { 2400 response.failed('Failed to delete: live object list ' + id + ' not found.'); 2401 } 2402 }; 2403 2404 2405 DebugCommandProcessor.prototype.lolDiffRequest_ = function(request, response) { 2406 var id1 = request.arguments.id1; 2407 var id2 = request.arguments.id2; 2408 var verbose = request.arguments.verbose; 2409 var filter = request.arguments.filter; 2410 if (verbose === true) { 2411 var start = request.arguments.start; 2412 var count = request.arguments.count; 2413 response.body = %DumpLOL(id1, id2, start, count, filter); 2414 } else { 2415 response.body = %SummarizeLOL(id1, id2, filter); 2416 } 2417 }; 2418 2419 2420 DebugCommandProcessor.prototype.lolGetIdRequest_ = function(request, response) { 2421 var address = request.arguments.address; 2422 response.body = {}; 2423 response.body.id = %GetLOLObjId(address); 2424 }; 2425 2426 2427 DebugCommandProcessor.prototype.lolInfoRequest_ = function(request, response) { 2428 var start = request.arguments.start; 2429 var count = request.arguments.count; 2430 response.body = %InfoLOL(start, count); 2431 }; 2432 2433 2434 DebugCommandProcessor.prototype.lolResetRequest_ = function(request, response) { 2435 %ResetLOL(); 2436 }; 2437 2438 2439 DebugCommandProcessor.prototype.lolRetainersRequest_ = 2440 function(request, response) { 2441 var id = request.arguments.id; 2442 var verbose = request.arguments.verbose; 2443 var start = request.arguments.start; 2444 var count = request.arguments.count; 2445 var filter = request.arguments.filter; 2446 2447 response.body = %GetLOLObjRetainers(id, Mirror.prototype, verbose, 2448 start, count, filter); 2449 }; 2450 2451 2452 DebugCommandProcessor.prototype.lolPathRequest_ = function(request, response) { 2453 var id1 = request.arguments.id1; 2454 var id2 = request.arguments.id2; 2455 response.body = {}; 2456 response.body.path = %GetLOLPath(id1, id2, Mirror.prototype); 2457 }; 2458 2459 2460 DebugCommandProcessor.prototype.lolPrintRequest_ = function(request, response) { 2461 var id = request.arguments.id; 2462 response.body = {}; 2463 response.body.dump = %PrintLOLObj(id); 2464 }; 2465 2466 2467 // Check whether the previously processed command caused the VM to become 2468 // running. 2469 DebugCommandProcessor.prototype.isRunning = function() { 2470 return this.running_; 2471 } 2472 2473 2474 DebugCommandProcessor.prototype.systemBreak = function(cmd, args) { 2475 return %SystemBreak(); 2476 }; 2477 2478 2479 function NumberToHex8Str(n) { 2480 var r = ""; 2481 for (var i = 0; i < 8; ++i) { 2482 var c = hexCharArray[n & 0x0F]; // hexCharArray is defined in uri.js 2483 r = c + r; 2484 n = n >>> 4; 2485 } 2486 return r; 2487 }; 2488 2489 2490 /** 2491 * Convert an Object to its debugger protocol representation. The representation 2492 * may be serilized to a JSON object using JSON.stringify(). 2493 * This implementation simply runs through all string property names, converts 2494 * each property value to a protocol value and adds the property to the result 2495 * object. For type "object" the function will be called recursively. Note that 2496 * circular structures will cause infinite recursion. 2497 * @param {Object} object The object to format as protocol object. 2498 * @param {MirrorSerializer} mirror_serializer The serializer to use if any 2499 * mirror objects are encountered. 2500 * @return {Object} Protocol object value. 2501 */ 2502 function ObjectToProtocolObject_(object, mirror_serializer) { 2503 var content = {}; 2504 for (var key in object) { 2505 // Only consider string keys. 2506 if (typeof key == 'string') { 2507 // Format the value based on its type. 2508 var property_value_json = ValueToProtocolValue_(object[key], 2509 mirror_serializer); 2510 // Add the property if relevant. 2511 if (!IS_UNDEFINED(property_value_json)) { 2512 content[key] = property_value_json; 2513 } 2514 } 2515 } 2516 2517 return content; 2518 } 2519 2520 2521 /** 2522 * Convert an array to its debugger protocol representation. It will convert 2523 * each array element to a protocol value. 2524 * @param {Array} array The array to format as protocol array. 2525 * @param {MirrorSerializer} mirror_serializer The serializer to use if any 2526 * mirror objects are encountered. 2527 * @return {Array} Protocol array value. 2528 */ 2529 function ArrayToProtocolArray_(array, mirror_serializer) { 2530 var json = []; 2531 for (var i = 0; i < array.length; i++) { 2532 json.push(ValueToProtocolValue_(array[i], mirror_serializer)); 2533 } 2534 return json; 2535 } 2536 2537 2538 /** 2539 * Convert a value to its debugger protocol representation. 2540 * @param {*} value The value to format as protocol value. 2541 * @param {MirrorSerializer} mirror_serializer The serializer to use if any 2542 * mirror objects are encountered. 2543 * @return {*} Protocol value. 2544 */ 2545 function ValueToProtocolValue_(value, mirror_serializer) { 2546 // Format the value based on its type. 2547 var json; 2548 switch (typeof value) { 2549 case 'object': 2550 if (value instanceof Mirror) { 2551 json = mirror_serializer.serializeValue(value); 2552 } else if (IS_ARRAY(value)){ 2553 json = ArrayToProtocolArray_(value, mirror_serializer); 2554 } else { 2555 json = ObjectToProtocolObject_(value, mirror_serializer); 2556 } 2557 break; 2558 2559 case 'boolean': 2560 case 'string': 2561 case 'number': 2562 json = value; 2563 break 2564 2565 default: 2566 json = null; 2567 } 2568 return json; 2569 } 2570