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