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