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