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