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