1// Copyright 2006-2008 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// jsminify this file, js2c: jsmin 29 30// Default number of frames to include in the response to backtrace request. 31const kDefaultBacktraceLength = 10; 32 33const Debug = {}; 34 35// Regular expression to skip "crud" at the beginning of a source line which is 36// not really code. Currently the regular expression matches whitespace and 37// comments. 38const sourceLineBeginningSkip = /^(?:[ \v\h]*(?:\/\*.*?\*\/)*)*/; 39 40// Debug events which can occour in the V8 JavaScript engine. These originate 41// from the API include file debug.h. 42Debug.DebugEvent = { Break: 1, 43 Exception: 2, 44 NewFunction: 3, 45 BeforeCompile: 4, 46 AfterCompile: 5, 47 ScriptCollected: 6 }; 48 49// Types of exceptions that can be broken upon. 50Debug.ExceptionBreak = { All : 0, 51 Uncaught: 1 }; 52 53// The different types of steps. 54Debug.StepAction = { StepOut: 0, 55 StepNext: 1, 56 StepIn: 2, 57 StepMin: 3, 58 StepInMin: 4 }; 59 60// The different types of scripts matching enum ScriptType in objects.h. 61Debug.ScriptType = { Native: 0, 62 Extension: 1, 63 Normal: 2 }; 64 65// The different types of script compilations matching enum 66// Script::CompilationType in objects.h. 67Debug.ScriptCompilationType = { Host: 0, 68 Eval: 1, 69 JSON: 2 }; 70 71// The different script break point types. 72Debug.ScriptBreakPointType = { ScriptId: 0, 73 ScriptName: 1 }; 74 75function ScriptTypeFlag(type) { 76 return (1 << type); 77} 78 79// Globals. 80var next_response_seq = 0; 81var next_break_point_number = 1; 82var break_points = []; 83var script_break_points = []; 84 85 86// Create a new break point object and add it to the list of break points. 87function MakeBreakPoint(source_position, opt_line, opt_column, opt_script_break_point) { 88 var break_point = new BreakPoint(source_position, opt_line, opt_column, opt_script_break_point); 89 break_points.push(break_point); 90 return break_point; 91} 92 93 94// Object representing a break point. 95// NOTE: This object does not have a reference to the function having break 96// point as this would cause function not to be garbage collected when it is 97// not used any more. We do not want break points to keep functions alive. 98function BreakPoint(source_position, opt_line, opt_column, opt_script_break_point) { 99 this.source_position_ = source_position; 100 this.source_line_ = opt_line; 101 this.source_column_ = opt_column; 102 if (opt_script_break_point) { 103 this.script_break_point_ = opt_script_break_point; 104 } else { 105 this.number_ = next_break_point_number++; 106 } 107 this.hit_count_ = 0; 108 this.active_ = true; 109 this.condition_ = null; 110 this.ignoreCount_ = 0; 111} 112 113 114BreakPoint.prototype.number = function() { 115 return this.number_; 116}; 117 118 119BreakPoint.prototype.func = function() { 120 return this.func_; 121}; 122 123 124BreakPoint.prototype.source_position = function() { 125 return this.source_position_; 126}; 127 128 129BreakPoint.prototype.hit_count = function() { 130 return this.hit_count_; 131}; 132 133 134BreakPoint.prototype.active = function() { 135 if (this.script_break_point()) { 136 return this.script_break_point().active(); 137 } 138 return this.active_; 139}; 140 141 142BreakPoint.prototype.condition = function() { 143 if (this.script_break_point() && this.script_break_point().condition()) { 144 return this.script_break_point().condition(); 145 } 146 return this.condition_; 147}; 148 149 150BreakPoint.prototype.ignoreCount = function() { 151 return this.ignoreCount_; 152}; 153 154 155BreakPoint.prototype.script_break_point = function() { 156 return this.script_break_point_; 157}; 158 159 160BreakPoint.prototype.enable = function() { 161 this.active_ = true; 162}; 163 164 165BreakPoint.prototype.disable = function() { 166 this.active_ = false; 167}; 168 169 170BreakPoint.prototype.setCondition = function(condition) { 171 this.condition_ = condition; 172}; 173 174 175BreakPoint.prototype.setIgnoreCount = function(ignoreCount) { 176 this.ignoreCount_ = ignoreCount; 177}; 178 179 180BreakPoint.prototype.isTriggered = function(exec_state) { 181 // Break point not active - not triggered. 182 if (!this.active()) return false; 183 184 // Check for conditional break point. 185 if (this.condition()) { 186 // If break point has condition try to evaluate it in the top frame. 187 try { 188 var mirror = exec_state.frame(0).evaluate(this.condition()); 189 // If no sensible mirror or non true value break point not triggered. 190 if (!(mirror instanceof ValueMirror) || !%ToBoolean(mirror.value_)) { 191 return false; 192 } 193 } catch (e) { 194 // Exception evaluating condition counts as not triggered. 195 return false; 196 } 197 } 198 199 // Update the hit count. 200 this.hit_count_++; 201 if (this.script_break_point_) { 202 this.script_break_point_.hit_count_++; 203 } 204 205 // If the break point has an ignore count it is not triggered. 206 if (this.ignoreCount_ > 0) { 207 this.ignoreCount_--; 208 return false; 209 } 210 211 // Break point triggered. 212 return true; 213}; 214 215 216// Function called from the runtime when a break point is hit. Returns true if 217// the break point is triggered and supposed to break execution. 218function IsBreakPointTriggered(break_id, break_point) { 219 return break_point.isTriggered(MakeExecutionState(break_id)); 220} 221 222 223// Object representing a script break point. The script is referenced by its 224// script name or script id and the break point is represented as line and 225// column. 226function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column, 227 opt_groupId) { 228 this.type_ = type; 229 if (type == Debug.ScriptBreakPointType.ScriptId) { 230 this.script_id_ = script_id_or_name; 231 } else { // type == Debug.ScriptBreakPointType.ScriptName 232 this.script_name_ = script_id_or_name; 233 } 234 this.line_ = opt_line || 0; 235 this.column_ = opt_column; 236 this.groupId_ = opt_groupId; 237 this.hit_count_ = 0; 238 this.active_ = true; 239 this.condition_ = null; 240 this.ignoreCount_ = 0; 241} 242 243 244ScriptBreakPoint.prototype.number = function() { 245 return this.number_; 246}; 247 248 249ScriptBreakPoint.prototype.groupId = function() { 250 return this.groupId_; 251}; 252 253 254ScriptBreakPoint.prototype.type = function() { 255 return this.type_; 256}; 257 258 259ScriptBreakPoint.prototype.script_id = function() { 260 return this.script_id_; 261}; 262 263 264ScriptBreakPoint.prototype.script_name = function() { 265 return this.script_name_; 266}; 267 268 269ScriptBreakPoint.prototype.line = function() { 270 return this.line_; 271}; 272 273 274ScriptBreakPoint.prototype.column = function() { 275 return this.column_; 276}; 277 278 279ScriptBreakPoint.prototype.hit_count = function() { 280 return this.hit_count_; 281}; 282 283 284ScriptBreakPoint.prototype.active = function() { 285 return this.active_; 286}; 287 288 289ScriptBreakPoint.prototype.condition = function() { 290 return this.condition_; 291}; 292 293 294ScriptBreakPoint.prototype.ignoreCount = function() { 295 return this.ignoreCount_; 296}; 297 298 299ScriptBreakPoint.prototype.enable = function() { 300 this.active_ = true; 301}; 302 303 304ScriptBreakPoint.prototype.disable = function() { 305 this.active_ = false; 306}; 307 308 309ScriptBreakPoint.prototype.setCondition = function(condition) { 310 this.condition_ = condition; 311}; 312 313 314ScriptBreakPoint.prototype.setIgnoreCount = function(ignoreCount) { 315 this.ignoreCount_ = ignoreCount; 316 317 // Set ignore count on all break points created from this script break point. 318 for (var i = 0; i < break_points.length; i++) { 319 if (break_points[i].script_break_point() === this) { 320 break_points[i].setIgnoreCount(ignoreCount); 321 } 322 } 323}; 324 325 326// Check whether a script matches this script break point. Currently this is 327// only based on script name. 328ScriptBreakPoint.prototype.matchesScript = function(script) { 329 if (this.type_ == Debug.ScriptBreakPointType.ScriptId) { 330 return this.script_id_ == script.id; 331 } else { // this.type_ == Debug.ScriptBreakPointType.ScriptName 332 return this.script_name_ == script.name && 333 script.line_offset <= this.line_ && 334 this.line_ < script.line_offset + script.lineCount(); 335 } 336}; 337 338 339// Set the script break point in a script. 340ScriptBreakPoint.prototype.set = function (script) { 341 var column = this.column(); 342 var line = this.line(); 343 // If the column is undefined the break is on the line. To help locate the 344 // first piece of breakable code on the line try to find the column on the 345 // line which contains some source. 346 if (IS_UNDEFINED(column)) { 347 var source_line = script.sourceLine(this.line()); 348 349 // Allocate array for caching the columns where the actual source starts. 350 if (!script.sourceColumnStart_) { 351 script.sourceColumnStart_ = new Array(script.lineCount()); 352 } 353 354 // Fill cache if needed and get column where the actual source starts. 355 if (IS_UNDEFINED(script.sourceColumnStart_[line])) { 356 script.sourceColumnStart_[line] = 357 source_line.match(sourceLineBeginningSkip)[0].length; 358 } 359 column = script.sourceColumnStart_[line]; 360 } 361 362 // Convert the line and column into an absolute position within the script. 363 var pos = Debug.findScriptSourcePosition(script, this.line(), column); 364 365 // If the position is not found in the script (the script might be shorter 366 // than it used to be) just ignore it. 367 if (pos === null) return; 368 369 // Create a break point object and set the break point. 370 break_point = MakeBreakPoint(pos, this.line(), this.column(), this); 371 break_point.setIgnoreCount(this.ignoreCount()); 372 %SetScriptBreakPoint(script, pos, break_point); 373 374 return break_point; 375}; 376 377 378// Clear all the break points created from this script break point 379ScriptBreakPoint.prototype.clear = function () { 380 var remaining_break_points = []; 381 for (var i = 0; i < break_points.length; i++) { 382 if (break_points[i].script_break_point() && 383 break_points[i].script_break_point() === this) { 384 %ClearBreakPoint(break_points[i]); 385 } else { 386 remaining_break_points.push(break_points[i]); 387 } 388 } 389 break_points = remaining_break_points; 390}; 391 392 393// Function called from runtime when a new script is compiled to set any script 394// break points set in this script. 395function UpdateScriptBreakPoints(script) { 396 for (var i = 0; i < script_break_points.length; i++) { 397 if (script_break_points[i].type() == Debug.ScriptBreakPointType.ScriptName && 398 script_break_points[i].matchesScript(script)) { 399 script_break_points[i].set(script); 400 } 401 } 402} 403 404 405Debug.setListener = function(listener, opt_data) { 406 if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) { 407 throw new Error('Parameters have wrong types.'); 408 } 409 %SetDebugEventListener(listener, opt_data); 410}; 411 412 413Debug.breakExecution = function(f) { 414 %Break(); 415}; 416 417Debug.breakLocations = function(f) { 418 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); 419 return %GetBreakLocations(f); 420}; 421 422// Returns a Script object. If the parameter is a function the return value 423// is the script in which the function is defined. If the parameter is a string 424// the return value is the script for which the script name has that string 425// value. If it is a regexp and there is a unique script whose name matches 426// we return that, otherwise undefined. 427Debug.findScript = function(func_or_script_name) { 428 if (IS_FUNCTION(func_or_script_name)) { 429 return %FunctionGetScript(func_or_script_name); 430 } else if (IS_REGEXP(func_or_script_name)) { 431 var scripts = Debug.scripts(); 432 var last_result = null; 433 var result_count = 0; 434 for (var i in scripts) { 435 var script = scripts[i]; 436 if (func_or_script_name.test(script.name)) { 437 last_result = script; 438 result_count++; 439 } 440 } 441 // Return the unique script matching the regexp. If there are more 442 // than one we don't return a value since there is no good way to 443 // decide which one to return. Returning a "random" one, say the 444 // first, would introduce nondeterminism (or something close to it) 445 // because the order is the heap iteration order. 446 if (result_count == 1) { 447 return last_result; 448 } else { 449 return undefined; 450 } 451 } else { 452 return %GetScript(func_or_script_name); 453 } 454}; 455 456// Returns the script source. If the parameter is a function the return value 457// is the script source for the script in which the function is defined. If the 458// parameter is a string the return value is the script for which the script 459// name has that string value. 460Debug.scriptSource = function(func_or_script_name) { 461 return this.findScript(func_or_script_name).source; 462}; 463 464Debug.source = function(f) { 465 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); 466 return %FunctionGetSourceCode(f); 467}; 468 469Debug.disassemble = function(f) { 470 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); 471 return %DebugDisassembleFunction(f); 472}; 473 474Debug.disassembleConstructor = function(f) { 475 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); 476 return %DebugDisassembleConstructor(f); 477}; 478 479Debug.sourcePosition = function(f) { 480 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); 481 return %FunctionGetScriptSourcePosition(f); 482}; 483 484 485Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) { 486 var script = %FunctionGetScript(func); 487 var script_offset = %FunctionGetScriptSourcePosition(func); 488 return script.locationFromLine(opt_line, opt_column, script_offset); 489} 490 491 492// Returns the character position in a script based on a line number and an 493// optional position within that line. 494Debug.findScriptSourcePosition = function(script, opt_line, opt_column) { 495 var location = script.locationFromLine(opt_line, opt_column); 496 return location ? location.position : null; 497} 498 499 500Debug.findBreakPoint = function(break_point_number, remove) { 501 var break_point; 502 for (var i = 0; i < break_points.length; i++) { 503 if (break_points[i].number() == break_point_number) { 504 break_point = break_points[i]; 505 // Remove the break point from the list if requested. 506 if (remove) { 507 break_points.splice(i, 1); 508 } 509 break; 510 } 511 } 512 if (break_point) { 513 return break_point; 514 } else { 515 return this.findScriptBreakPoint(break_point_number, remove); 516 } 517}; 518 519 520Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) { 521 if (!IS_FUNCTION(func)) throw new Error('Parameters have wrong types.'); 522 // Break points in API functions are not supported. 523 if (%FunctionIsAPIFunction(func)) { 524 throw new Error('Cannot set break point in native code.'); 525 } 526 // Find source position relative to start of the function 527 var break_position = 528 this.findFunctionSourceLocation(func, opt_line, opt_column).position; 529 var source_position = break_position - this.sourcePosition(func); 530 // Find the script for the function. 531 var script = %FunctionGetScript(func); 532 // Break in builtin JavaScript code is not supported. 533 if (script.type == Debug.ScriptType.Native) { 534 throw new Error('Cannot set break point in native code.'); 535 } 536 // If the script for the function has a name convert this to a script break 537 // point. 538 if (script && script.id) { 539 // Adjust the source position to be script relative. 540 source_position += %FunctionGetScriptSourcePosition(func); 541 // Find line and column for the position in the script and set a script 542 // break point from that. 543 var location = script.locationFromPosition(source_position, false); 544 return this.setScriptBreakPointById(script.id, 545 location.line, location.column, 546 opt_condition); 547 } else { 548 // Set a break point directly on the function. 549 var break_point = MakeBreakPoint(source_position, opt_line, opt_column); 550 %SetFunctionBreakPoint(func, source_position, break_point); 551 break_point.setCondition(opt_condition); 552 return break_point.number(); 553 } 554}; 555 556 557Debug.enableBreakPoint = function(break_point_number) { 558 var break_point = this.findBreakPoint(break_point_number, false); 559 break_point.enable(); 560}; 561 562 563Debug.disableBreakPoint = function(break_point_number) { 564 var break_point = this.findBreakPoint(break_point_number, false); 565 break_point.disable(); 566}; 567 568 569Debug.changeBreakPointCondition = function(break_point_number, condition) { 570 var break_point = this.findBreakPoint(break_point_number, false); 571 break_point.setCondition(condition); 572}; 573 574 575Debug.changeBreakPointIgnoreCount = function(break_point_number, ignoreCount) { 576 if (ignoreCount < 0) { 577 throw new Error('Invalid argument'); 578 } 579 var break_point = this.findBreakPoint(break_point_number, false); 580 break_point.setIgnoreCount(ignoreCount); 581}; 582 583 584Debug.clearBreakPoint = function(break_point_number) { 585 var break_point = this.findBreakPoint(break_point_number, true); 586 if (break_point) { 587 return %ClearBreakPoint(break_point); 588 } else { 589 break_point = this.findScriptBreakPoint(break_point_number, true); 590 if (!break_point) { 591 throw new Error('Invalid breakpoint'); 592 } 593 } 594}; 595 596 597Debug.clearAllBreakPoints = function() { 598 for (var i = 0; i < break_points.length; i++) { 599 break_point = break_points[i]; 600 %ClearBreakPoint(break_point); 601 } 602 break_points = []; 603}; 604 605 606Debug.findScriptBreakPoint = function(break_point_number, remove) { 607 var script_break_point; 608 for (var i = 0; i < script_break_points.length; i++) { 609 if (script_break_points[i].number() == break_point_number) { 610 script_break_point = script_break_points[i]; 611 // Remove the break point from the list if requested. 612 if (remove) { 613 script_break_point.clear(); 614 script_break_points.splice(i,1); 615 } 616 break; 617 } 618 } 619 return script_break_point; 620} 621 622 623// Sets a breakpoint in a script identified through id or name at the 624// specified source line and column within that line. 625Debug.setScriptBreakPoint = function(type, script_id_or_name, 626 opt_line, opt_column, opt_condition, 627 opt_groupId) { 628 // Create script break point object. 629 var script_break_point = 630 new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column, 631 opt_groupId); 632 633 // Assign number to the new script break point and add it. 634 script_break_point.number_ = next_break_point_number++; 635 script_break_point.setCondition(opt_condition); 636 script_break_points.push(script_break_point); 637 638 // Run through all scripts to see if this script break point matches any 639 // loaded scripts. 640 var scripts = this.scripts(); 641 for (var i = 0; i < scripts.length; i++) { 642 if (script_break_point.matchesScript(scripts[i])) { 643 script_break_point.set(scripts[i]); 644 } 645 } 646 647 return script_break_point.number(); 648} 649 650 651Debug.setScriptBreakPointById = function(script_id, 652 opt_line, opt_column, 653 opt_condition, opt_groupId) { 654 return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId, 655 script_id, opt_line, opt_column, 656 opt_condition, opt_groupId); 657} 658 659 660Debug.setScriptBreakPointByName = function(script_name, 661 opt_line, opt_column, 662 opt_condition, opt_groupId) { 663 return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName, 664 script_name, opt_line, opt_column, 665 opt_condition, opt_groupId); 666} 667 668 669Debug.enableScriptBreakPoint = function(break_point_number) { 670 var script_break_point = this.findScriptBreakPoint(break_point_number, false); 671 script_break_point.enable(); 672}; 673 674 675Debug.disableScriptBreakPoint = function(break_point_number) { 676 var script_break_point = this.findScriptBreakPoint(break_point_number, false); 677 script_break_point.disable(); 678}; 679 680 681Debug.changeScriptBreakPointCondition = function(break_point_number, condition) { 682 var script_break_point = this.findScriptBreakPoint(break_point_number, false); 683 script_break_point.setCondition(condition); 684}; 685 686 687Debug.changeScriptBreakPointIgnoreCount = function(break_point_number, ignoreCount) { 688 if (ignoreCount < 0) { 689 throw new Error('Invalid argument'); 690 } 691 var script_break_point = this.findScriptBreakPoint(break_point_number, false); 692 script_break_point.setIgnoreCount(ignoreCount); 693}; 694 695 696Debug.scriptBreakPoints = function() { 697 return script_break_points; 698} 699 700 701Debug.clearStepping = function() { 702 %ClearStepping(); 703} 704 705Debug.setBreakOnException = function() { 706 return %ChangeBreakOnException(Debug.ExceptionBreak.All, true); 707}; 708 709Debug.clearBreakOnException = function() { 710 return %ChangeBreakOnException(Debug.ExceptionBreak.All, false); 711}; 712 713Debug.setBreakOnUncaughtException = function() { 714 return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true); 715}; 716 717Debug.clearBreakOnUncaughtException = function() { 718 return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false); 719}; 720 721Debug.showBreakPoints = function(f, full) { 722 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); 723 var source = full ? this.scriptSource(f) : this.source(f); 724 var offset = full ? this.sourcePosition(f) : 0; 725 var locations = this.breakLocations(f); 726 if (!locations) return source; 727 locations.sort(function(x, y) { return x - y; }); 728 var result = ""; 729 var prev_pos = 0; 730 var pos; 731 for (var i = 0; i < locations.length; i++) { 732 pos = locations[i] - offset; 733 result += source.slice(prev_pos, pos); 734 result += "[B" + i + "]"; 735 prev_pos = pos; 736 } 737 pos = source.length; 738 result += source.substring(prev_pos, pos); 739 return result; 740}; 741 742 743// Get all the scripts currently loaded. Locating all the scripts is based on 744// scanning the heap. 745Debug.scripts = function() { 746 // Collect all scripts in the heap. 747 return %DebugGetLoadedScripts(); 748} 749 750function MakeExecutionState(break_id) { 751 return new ExecutionState(break_id); 752} 753 754function ExecutionState(break_id) { 755 this.break_id = break_id; 756 this.selected_frame = 0; 757} 758 759ExecutionState.prototype.prepareStep = function(opt_action, opt_count) { 760 var action = Debug.StepAction.StepIn; 761 if (!IS_UNDEFINED(opt_action)) action = %ToNumber(opt_action); 762 var count = opt_count ? %ToNumber(opt_count) : 1; 763 764 return %PrepareStep(this.break_id, action, count); 765} 766 767ExecutionState.prototype.evaluateGlobal = function(source, disable_break) { 768 return MakeMirror( 769 %DebugEvaluateGlobal(this.break_id, source, Boolean(disable_break))); 770}; 771 772ExecutionState.prototype.frameCount = function() { 773 return %GetFrameCount(this.break_id); 774}; 775 776ExecutionState.prototype.threadCount = function() { 777 return %GetThreadCount(this.break_id); 778}; 779 780ExecutionState.prototype.frame = function(opt_index) { 781 // If no index supplied return the selected frame. 782 if (opt_index == null) opt_index = this.selected_frame; 783 return new FrameMirror(this.break_id, opt_index); 784}; 785 786ExecutionState.prototype.cframesValue = function(opt_from_index, opt_to_index) { 787 return %GetCFrames(this.break_id); 788}; 789 790ExecutionState.prototype.setSelectedFrame = function(index) { 791 var i = %ToNumber(index); 792 if (i < 0 || i >= this.frameCount()) throw new Error('Illegal frame index.'); 793 this.selected_frame = i; 794}; 795 796ExecutionState.prototype.selectedFrame = function() { 797 return this.selected_frame; 798}; 799 800ExecutionState.prototype.debugCommandProcessor = function(protocol) { 801 return new DebugCommandProcessor(this, protocol); 802}; 803 804 805function MakeBreakEvent(exec_state, break_points_hit) { 806 return new BreakEvent(exec_state, break_points_hit); 807} 808 809 810function BreakEvent(exec_state, break_points_hit) { 811 this.exec_state_ = exec_state; 812 this.break_points_hit_ = break_points_hit; 813} 814 815 816BreakEvent.prototype.executionState = function() { 817 return this.exec_state_; 818}; 819 820 821BreakEvent.prototype.eventType = function() { 822 return Debug.DebugEvent.Break; 823}; 824 825 826BreakEvent.prototype.func = function() { 827 return this.exec_state_.frame(0).func(); 828}; 829 830 831BreakEvent.prototype.sourceLine = function() { 832 return this.exec_state_.frame(0).sourceLine(); 833}; 834 835 836BreakEvent.prototype.sourceColumn = function() { 837 return this.exec_state_.frame(0).sourceColumn(); 838}; 839 840 841BreakEvent.prototype.sourceLineText = function() { 842 return this.exec_state_.frame(0).sourceLineText(); 843}; 844 845 846BreakEvent.prototype.breakPointsHit = function() { 847 return this.break_points_hit_; 848}; 849 850 851BreakEvent.prototype.toJSONProtocol = function() { 852 var o = { seq: next_response_seq++, 853 type: "event", 854 event: "break", 855 body: { invocationText: this.exec_state_.frame(0).invocationText(), 856 } 857 }; 858 859 // Add script related information to the event if available. 860 var script = this.func().script(); 861 if (script) { 862 o.body.sourceLine = this.sourceLine(), 863 o.body.sourceColumn = this.sourceColumn(), 864 o.body.sourceLineText = this.sourceLineText(), 865 o.body.script = MakeScriptObject_(script, false); 866 } 867 868 // Add an Array of break points hit if any. 869 if (this.breakPointsHit()) { 870 o.body.breakpoints = []; 871 for (var i = 0; i < this.breakPointsHit().length; i++) { 872 // Find the break point number. For break points originating from a 873 // script break point supply the script break point number. 874 var breakpoint = this.breakPointsHit()[i]; 875 var script_break_point = breakpoint.script_break_point(); 876 var number; 877 if (script_break_point) { 878 number = script_break_point.number(); 879 } else { 880 number = breakpoint.number(); 881 } 882 o.body.breakpoints.push(number); 883 } 884 } 885 return JSON.stringify(ObjectToProtocolObject_(o)); 886}; 887 888 889function MakeExceptionEvent(exec_state, exception, uncaught) { 890 return new ExceptionEvent(exec_state, exception, uncaught); 891} 892 893 894function ExceptionEvent(exec_state, exception, uncaught) { 895 this.exec_state_ = exec_state; 896 this.exception_ = exception; 897 this.uncaught_ = uncaught; 898} 899 900 901ExceptionEvent.prototype.executionState = function() { 902 return this.exec_state_; 903}; 904 905 906ExceptionEvent.prototype.eventType = function() { 907 return Debug.DebugEvent.Exception; 908}; 909 910 911ExceptionEvent.prototype.exception = function() { 912 return this.exception_; 913} 914 915 916ExceptionEvent.prototype.uncaught = function() { 917 return this.uncaught_; 918} 919 920 921ExceptionEvent.prototype.func = function() { 922 return this.exec_state_.frame(0).func(); 923}; 924 925 926ExceptionEvent.prototype.sourceLine = function() { 927 return this.exec_state_.frame(0).sourceLine(); 928}; 929 930 931ExceptionEvent.prototype.sourceColumn = function() { 932 return this.exec_state_.frame(0).sourceColumn(); 933}; 934 935 936ExceptionEvent.prototype.sourceLineText = function() { 937 return this.exec_state_.frame(0).sourceLineText(); 938}; 939 940 941ExceptionEvent.prototype.toJSONProtocol = function() { 942 var o = new ProtocolMessage(); 943 o.event = "exception"; 944 o.body = { uncaught: this.uncaught_, 945 exception: MakeMirror(this.exception_) 946 }; 947 948 // Exceptions might happen whithout any JavaScript frames. 949 if (this.exec_state_.frameCount() > 0) { 950 o.body.sourceLine = this.sourceLine(); 951 o.body.sourceColumn = this.sourceColumn(); 952 o.body.sourceLineText = this.sourceLineText(); 953 954 // Add script information to the event if available. 955 var script = this.func().script(); 956 if (script) { 957 o.body.script = MakeScriptObject_(script, false); 958 } 959 } else { 960 o.body.sourceLine = -1; 961 } 962 963 return o.toJSONProtocol(); 964}; 965 966 967function MakeCompileEvent(exec_state, script, before) { 968 return new CompileEvent(exec_state, script, before); 969} 970 971 972function CompileEvent(exec_state, script, before) { 973 this.exec_state_ = exec_state; 974 this.script_ = MakeMirror(script); 975 this.before_ = before; 976} 977 978 979CompileEvent.prototype.executionState = function() { 980 return this.exec_state_; 981}; 982 983 984CompileEvent.prototype.eventType = function() { 985 if (this.before_) { 986 return Debug.DebugEvent.BeforeCompile; 987 } else { 988 return Debug.DebugEvent.AfterCompile; 989 } 990}; 991 992 993CompileEvent.prototype.script = function() { 994 return this.script_; 995}; 996 997 998CompileEvent.prototype.toJSONProtocol = function() { 999 var o = new ProtocolMessage(); 1000 o.running = true; 1001 if (this.before_) { 1002 o.event = "beforeCompile"; 1003 } else { 1004 o.event = "afterCompile"; 1005 } 1006 o.body = {}; 1007 o.body.script = this.script_; 1008 1009 return o.toJSONProtocol(); 1010} 1011 1012 1013function MakeNewFunctionEvent(func) { 1014 return new NewFunctionEvent(func); 1015} 1016 1017 1018function NewFunctionEvent(func) { 1019 this.func = func; 1020} 1021 1022 1023NewFunctionEvent.prototype.eventType = function() { 1024 return Debug.DebugEvent.NewFunction; 1025}; 1026 1027 1028NewFunctionEvent.prototype.name = function() { 1029 return this.func.name; 1030}; 1031 1032 1033NewFunctionEvent.prototype.setBreakPoint = function(p) { 1034 Debug.setBreakPoint(this.func, p || 0); 1035}; 1036 1037 1038function MakeScriptCollectedEvent(exec_state, id) { 1039 return new ScriptCollectedEvent(exec_state, id); 1040} 1041 1042 1043function ScriptCollectedEvent(exec_state, id) { 1044 this.exec_state_ = exec_state; 1045 this.id_ = id; 1046} 1047 1048 1049ScriptCollectedEvent.prototype.id = function() { 1050 return this.id_; 1051}; 1052 1053 1054ScriptCollectedEvent.prototype.executionState = function() { 1055 return this.exec_state_; 1056}; 1057 1058 1059ScriptCollectedEvent.prototype.toJSONProtocol = function() { 1060 var o = new ProtocolMessage(); 1061 o.running = true; 1062 o.event = "scriptCollected"; 1063 o.body = {}; 1064 o.body.script = { id: this.id() }; 1065 return o.toJSONProtocol(); 1066} 1067 1068 1069function MakeScriptObject_(script, include_source) { 1070 var o = { id: script.id(), 1071 name: script.name(), 1072 lineOffset: script.lineOffset(), 1073 columnOffset: script.columnOffset(), 1074 lineCount: script.lineCount(), 1075 }; 1076 if (!IS_UNDEFINED(script.data())) { 1077 o.data = script.data(); 1078 } 1079 if (include_source) { 1080 o.source = script.source(); 1081 } 1082 return o; 1083}; 1084 1085 1086function DebugCommandProcessor(exec_state) { 1087 this.exec_state_ = exec_state; 1088 this.running_ = false; 1089}; 1090 1091 1092DebugCommandProcessor.prototype.processDebugRequest = function (request) { 1093 return this.processDebugJSONRequest(request); 1094} 1095 1096 1097function ProtocolMessage(request) { 1098 // Update sequence number. 1099 this.seq = next_response_seq++; 1100 1101 if (request) { 1102 // If message is based on a request this is a response. Fill the initial 1103 // response from the request. 1104 this.type = 'response'; 1105 this.request_seq = request.seq; 1106 this.command = request.command; 1107 } else { 1108 // If message is not based on a request it is a dabugger generated event. 1109 this.type = 'event'; 1110 } 1111 this.success = true; 1112 this.running = false; 1113} 1114 1115 1116ProtocolMessage.prototype.setOption = function(name, value) { 1117 if (!this.options_) { 1118 this.options_ = {}; 1119 } 1120 this.options_[name] = value; 1121} 1122 1123 1124ProtocolMessage.prototype.failed = function(message) { 1125 this.success = false; 1126 this.message = message; 1127} 1128 1129 1130ProtocolMessage.prototype.toJSONProtocol = function() { 1131 // Encode the protocol header. 1132 var json = {}; 1133 json.seq= this.seq; 1134 if (this.request_seq) { 1135 json.request_seq = this.request_seq; 1136 } 1137 json.type = this.type; 1138 if (this.event) { 1139 json.event = this.event; 1140 } 1141 if (this.command) { 1142 json.command = this.command; 1143 } 1144 if (this.success) { 1145 json.success = this.success; 1146 } else { 1147 json.success = false; 1148 } 1149 if (this.body) { 1150 // Encode the body part. 1151 var bodyJson; 1152 var serializer = MakeMirrorSerializer(true, this.options_); 1153 if (this.body instanceof Mirror) { 1154 bodyJson = serializer.serializeValue(this.body); 1155 } else if (this.body instanceof Array) { 1156 bodyJson = []; 1157 for (var i = 0; i < this.body.length; i++) { 1158 if (this.body[i] instanceof Mirror) { 1159 bodyJson.push(serializer.serializeValue(this.body[i])); 1160 } else { 1161 bodyJson.push(ObjectToProtocolObject_(this.body[i], serializer)); 1162 } 1163 } 1164 } else { 1165 bodyJson = ObjectToProtocolObject_(this.body, serializer); 1166 } 1167 json.body = bodyJson; 1168 json.refs = serializer.serializeReferencedObjects(); 1169 } 1170 if (this.message) { 1171 json.message = this.message; 1172 } 1173 if (this.running) { 1174 json.running = true; 1175 } else { 1176 json.running = false; 1177 } 1178 return JSON.stringify(json); 1179} 1180 1181 1182DebugCommandProcessor.prototype.createResponse = function(request) { 1183 return new ProtocolMessage(request); 1184}; 1185 1186 1187DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request) { 1188 var request; // Current request. 1189 var response; // Generated response. 1190 try { 1191 try { 1192 // Convert the JSON string to an object. 1193 request = %CompileString('(' + json_request + ')', false)(); 1194 1195 // Create an initial response. 1196 response = this.createResponse(request); 1197 1198 if (!request.type) { 1199 throw new Error('Type not specified'); 1200 } 1201 1202 if (request.type != 'request') { 1203 throw new Error("Illegal type '" + request.type + "' in request"); 1204 } 1205 1206 if (!request.command) { 1207 throw new Error('Command not specified'); 1208 } 1209 1210 // TODO(yurys): remove request.arguments.compactFormat check once 1211 // ChromeDevTools are switched to 'inlineRefs' 1212 if (request.arguments && (request.arguments.inlineRefs || 1213 request.arguments.compactFormat)) { 1214 response.setOption('inlineRefs', true); 1215 } 1216 1217 if (request.command == 'continue') { 1218 this.continueRequest_(request, response); 1219 } else if (request.command == 'break') { 1220 this.breakRequest_(request, response); 1221 } else if (request.command == 'setbreakpoint') { 1222 this.setBreakPointRequest_(request, response); 1223 } else if (request.command == 'changebreakpoint') { 1224 this.changeBreakPointRequest_(request, response); 1225 } else if (request.command == 'clearbreakpoint') { 1226 this.clearBreakPointRequest_(request, response); 1227 } else if (request.command == 'clearbreakpointgroup') { 1228 this.clearBreakPointGroupRequest_(request, response); 1229 } else if (request.command == 'backtrace') { 1230 this.backtraceRequest_(request, response); 1231 } else if (request.command == 'frame') { 1232 this.frameRequest_(request, response); 1233 } else if (request.command == 'scopes') { 1234 this.scopesRequest_(request, response); 1235 } else if (request.command == 'scope') { 1236 this.scopeRequest_(request, response); 1237 } else if (request.command == 'evaluate') { 1238 this.evaluateRequest_(request, response); 1239 } else if (request.command == 'lookup') { 1240 this.lookupRequest_(request, response); 1241 } else if (request.command == 'references') { 1242 this.referencesRequest_(request, response); 1243 } else if (request.command == 'source') { 1244 this.sourceRequest_(request, response); 1245 } else if (request.command == 'scripts') { 1246 this.scriptsRequest_(request, response); 1247 } else if (request.command == 'threads') { 1248 this.threadsRequest_(request, response); 1249 } else { 1250 throw new Error('Unknown command "' + request.command + '" in request'); 1251 } 1252 } catch (e) { 1253 // If there is no response object created one (without command). 1254 if (!response) { 1255 response = this.createResponse(); 1256 } 1257 response.success = false; 1258 response.message = %ToString(e); 1259 } 1260 1261 // Return the response as a JSON encoded string. 1262 try { 1263 this.running_ = response.running; // Store the running state. 1264 return response.toJSONProtocol(); 1265 } catch (e) { 1266 // Failed to generate response - return generic error. 1267 return '{"seq":' + response.seq + ',' + 1268 '"request_seq":' + request.seq + ',' + 1269 '"type":"response",' + 1270 '"success":false,' + 1271 '"message":"Internal error: ' + %ToString(e) + '"}'; 1272 } 1273 } catch (e) { 1274 // Failed in one of the catch blocks above - most generic error. 1275 return '{"seq":0,"type":"response","success":false,"message":"Internal error"}'; 1276 } 1277}; 1278 1279 1280DebugCommandProcessor.prototype.continueRequest_ = function(request, response) { 1281 // Check for arguments for continue. 1282 if (request.arguments) { 1283 var count = 1; 1284 var action = Debug.StepAction.StepIn; 1285 1286 // Pull out arguments. 1287 var stepaction = request.arguments.stepaction; 1288 var stepcount = request.arguments.stepcount; 1289 1290 // Get the stepcount argument if any. 1291 if (stepcount) { 1292 count = %ToNumber(stepcount); 1293 if (count < 0) { 1294 throw new Error('Invalid stepcount argument "' + stepcount + '".'); 1295 } 1296 } 1297 1298 // Get the stepaction argument. 1299 if (stepaction) { 1300 if (stepaction == 'in') { 1301 action = Debug.StepAction.StepIn; 1302 } else if (stepaction == 'min') { 1303 action = Debug.StepAction.StepMin; 1304 } else if (stepaction == 'next') { 1305 action = Debug.StepAction.StepNext; 1306 } else if (stepaction == 'out') { 1307 action = Debug.StepAction.StepOut; 1308 } else { 1309 throw new Error('Invalid stepaction argument "' + stepaction + '".'); 1310 } 1311 } 1312 1313 // Setup the VM for stepping. 1314 this.exec_state_.prepareStep(action, count); 1315 } 1316 1317 // VM should be running after executing this request. 1318 response.running = true; 1319}; 1320 1321 1322DebugCommandProcessor.prototype.breakRequest_ = function(request, response) { 1323 // Ignore as break command does not do anything when broken. 1324}; 1325 1326 1327DebugCommandProcessor.prototype.setBreakPointRequest_ = 1328 function(request, response) { 1329 // Check for legal request. 1330 if (!request.arguments) { 1331 response.failed('Missing arguments'); 1332 return; 1333 } 1334 1335 // Pull out arguments. 1336 var type = request.arguments.type; 1337 var target = request.arguments.target; 1338 var line = request.arguments.line; 1339 var column = request.arguments.column; 1340 var enabled = IS_UNDEFINED(request.arguments.enabled) ? 1341 true : request.arguments.enabled; 1342 var condition = request.arguments.condition; 1343 var ignoreCount = request.arguments.ignoreCount; 1344 var groupId = request.arguments.groupId; 1345 1346 // Check for legal arguments. 1347 if (!type || IS_UNDEFINED(target)) { 1348 response.failed('Missing argument "type" or "target"'); 1349 return; 1350 } 1351 if (type != 'function' && type != 'handle' && 1352 type != 'script' && type != 'scriptId') { 1353 response.failed('Illegal type "' + type + '"'); 1354 return; 1355 } 1356 1357 // Either function or script break point. 1358 var break_point_number; 1359 if (type == 'function') { 1360 // Handle function break point. 1361 if (!IS_STRING(target)) { 1362 response.failed('Argument "target" is not a string value'); 1363 return; 1364 } 1365 var f; 1366 try { 1367 // Find the function through a global evaluate. 1368 f = this.exec_state_.evaluateGlobal(target).value(); 1369 } catch (e) { 1370 response.failed('Error: "' + %ToString(e) + 1371 '" evaluating "' + target + '"'); 1372 return; 1373 } 1374 if (!IS_FUNCTION(f)) { 1375 response.failed('"' + target + '" does not evaluate to a function'); 1376 return; 1377 } 1378 1379 // Set function break point. 1380 break_point_number = Debug.setBreakPoint(f, line, column, condition); 1381 } else if (type == 'handle') { 1382 // Find the object pointed by the specified handle. 1383 var handle = parseInt(target, 10); 1384 var mirror = LookupMirror(handle); 1385 if (!mirror) { 1386 return response.failed('Object #' + handle + '# not found'); 1387 } 1388 if (!mirror.isFunction()) { 1389 return response.failed('Object #' + handle + '# is not a function'); 1390 } 1391 1392 // Set function break point. 1393 break_point_number = Debug.setBreakPoint(mirror.value(), 1394 line, column, condition); 1395 } else if (type == 'script') { 1396 // set script break point. 1397 break_point_number = 1398 Debug.setScriptBreakPointByName(target, line, column, condition, 1399 groupId); 1400 } else { // type == 'scriptId. 1401 break_point_number = 1402 Debug.setScriptBreakPointById(target, line, column, condition, groupId); 1403 } 1404 1405 // Set additional break point properties. 1406 var break_point = Debug.findBreakPoint(break_point_number); 1407 if (ignoreCount) { 1408 Debug.changeBreakPointIgnoreCount(break_point_number, ignoreCount); 1409 } 1410 if (!enabled) { 1411 Debug.disableBreakPoint(break_point_number); 1412 } 1413 1414 // Add the break point number to the response. 1415 response.body = { type: type, 1416 breakpoint: break_point_number } 1417 1418 // Add break point information to the response. 1419 if (break_point instanceof ScriptBreakPoint) { 1420 if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) { 1421 response.body.type = 'scriptId'; 1422 response.body.script_id = break_point.script_id(); 1423 } else { 1424 response.body.type = 'scriptName'; 1425 response.body.script_name = break_point.script_name(); 1426 } 1427 response.body.line = break_point.line(); 1428 response.body.column = break_point.column(); 1429 } else { 1430 response.body.type = 'function'; 1431 } 1432}; 1433 1434 1435DebugCommandProcessor.prototype.changeBreakPointRequest_ = function(request, response) { 1436 // Check for legal request. 1437 if (!request.arguments) { 1438 response.failed('Missing arguments'); 1439 return; 1440 } 1441 1442 // Pull out arguments. 1443 var break_point = %ToNumber(request.arguments.breakpoint); 1444 var enabled = request.arguments.enabled; 1445 var condition = request.arguments.condition; 1446 var ignoreCount = request.arguments.ignoreCount; 1447 1448 // Check for legal arguments. 1449 if (!break_point) { 1450 response.failed('Missing argument "breakpoint"'); 1451 return; 1452 } 1453 1454 // Change enabled state if supplied. 1455 if (!IS_UNDEFINED(enabled)) { 1456 if (enabled) { 1457 Debug.enableBreakPoint(break_point); 1458 } else { 1459 Debug.disableBreakPoint(break_point); 1460 } 1461 } 1462 1463 // Change condition if supplied 1464 if (!IS_UNDEFINED(condition)) { 1465 Debug.changeBreakPointCondition(break_point, condition); 1466 } 1467 1468 // Change ignore count if supplied 1469 if (!IS_UNDEFINED(ignoreCount)) { 1470 Debug.changeBreakPointIgnoreCount(break_point, ignoreCount); 1471 } 1472} 1473 1474 1475DebugCommandProcessor.prototype.clearBreakPointGroupRequest_ = function(request, response) { 1476 // Check for legal request. 1477 if (!request.arguments) { 1478 response.failed('Missing arguments'); 1479 return; 1480 } 1481 1482 // Pull out arguments. 1483 var group_id = request.arguments.groupId; 1484 1485 // Check for legal arguments. 1486 if (!group_id) { 1487 response.failed('Missing argument "groupId"'); 1488 return; 1489 } 1490 1491 var cleared_break_points = []; 1492 var new_script_break_points = []; 1493 for (var i = 0; i < script_break_points.length; i++) { 1494 var next_break_point = script_break_points[i]; 1495 if (next_break_point.groupId() == group_id) { 1496 cleared_break_points.push(next_break_point.number()); 1497 next_break_point.clear(); 1498 } else { 1499 new_script_break_points.push(next_break_point); 1500 } 1501 } 1502 script_break_points = new_script_break_points; 1503 1504 // Add the cleared break point numbers to the response. 1505 response.body = { breakpoints: cleared_break_points }; 1506} 1507 1508 1509DebugCommandProcessor.prototype.clearBreakPointRequest_ = function(request, response) { 1510 // Check for legal request. 1511 if (!request.arguments) { 1512 response.failed('Missing arguments'); 1513 return; 1514 } 1515 1516 // Pull out arguments. 1517 var break_point = %ToNumber(request.arguments.breakpoint); 1518 1519 // Check for legal arguments. 1520 if (!break_point) { 1521 response.failed('Missing argument "breakpoint"'); 1522 return; 1523 } 1524 1525 // Clear break point. 1526 Debug.clearBreakPoint(break_point); 1527 1528 // Add the cleared break point number to the response. 1529 response.body = { breakpoint: break_point } 1530} 1531 1532 1533DebugCommandProcessor.prototype.backtraceRequest_ = function(request, response) { 1534 // Get the number of frames. 1535 var total_frames = this.exec_state_.frameCount(); 1536 1537 // Create simple response if there are no frames. 1538 if (total_frames == 0) { 1539 response.body = { 1540 totalFrames: total_frames 1541 } 1542 return; 1543 } 1544 1545 // Default frame range to include in backtrace. 1546 var from_index = 0 1547 var to_index = kDefaultBacktraceLength; 1548 1549 // Get the range from the arguments. 1550 if (request.arguments) { 1551 if (request.arguments.fromFrame) { 1552 from_index = request.arguments.fromFrame; 1553 } 1554 if (request.arguments.toFrame) { 1555 to_index = request.arguments.toFrame; 1556 } 1557 if (request.arguments.bottom) { 1558 var tmp_index = total_frames - from_index; 1559 from_index = total_frames - to_index 1560 to_index = tmp_index; 1561 } 1562 if (from_index < 0 || to_index < 0) { 1563 return response.failed('Invalid frame number'); 1564 } 1565 } 1566 1567 // Adjust the index. 1568 to_index = Math.min(total_frames, to_index); 1569 1570 if (to_index <= from_index) { 1571 var error = 'Invalid frame range'; 1572 return response.failed(error); 1573 } 1574 1575 // Create the response body. 1576 var frames = []; 1577 for (var i = from_index; i < to_index; i++) { 1578 frames.push(this.exec_state_.frame(i)); 1579 } 1580 response.body = { 1581 fromFrame: from_index, 1582 toFrame: to_index, 1583 totalFrames: total_frames, 1584 frames: frames 1585 } 1586}; 1587 1588 1589DebugCommandProcessor.prototype.backtracec = function(cmd, args) { 1590 return this.exec_state_.cframesValue(); 1591}; 1592 1593 1594DebugCommandProcessor.prototype.frameRequest_ = function(request, response) { 1595 // No frames no source. 1596 if (this.exec_state_.frameCount() == 0) { 1597 return response.failed('No frames'); 1598 } 1599 1600 // With no arguments just keep the selected frame. 1601 if (request.arguments) { 1602 var index = request.arguments.number; 1603 if (index < 0 || this.exec_state_.frameCount() <= index) { 1604 return response.failed('Invalid frame number'); 1605 } 1606 1607 this.exec_state_.setSelectedFrame(request.arguments.number); 1608 } 1609 response.body = this.exec_state_.frame(); 1610}; 1611 1612 1613DebugCommandProcessor.prototype.frameForScopeRequest_ = function(request) { 1614 // Get the frame for which the scope or scopes are requested. With no frameNumber 1615 // argument use the currently selected frame. 1616 if (request.arguments && !IS_UNDEFINED(request.arguments.frameNumber)) { 1617 frame_index = request.arguments.frameNumber; 1618 if (frame_index < 0 || this.exec_state_.frameCount() <= frame_index) { 1619 return response.failed('Invalid frame number'); 1620 } 1621 return this.exec_state_.frame(frame_index); 1622 } else { 1623 return this.exec_state_.frame(); 1624 } 1625} 1626 1627 1628DebugCommandProcessor.prototype.scopesRequest_ = function(request, response) { 1629 // No frames no scopes. 1630 if (this.exec_state_.frameCount() == 0) { 1631 return response.failed('No scopes'); 1632 } 1633 1634 // Get the frame for which the scopes are requested. 1635 var frame = this.frameForScopeRequest_(request); 1636 1637 // Fill all scopes for this frame. 1638 var total_scopes = frame.scopeCount(); 1639 var scopes = []; 1640 for (var i = 0; i < total_scopes; i++) { 1641 scopes.push(frame.scope(i)); 1642 } 1643 response.body = { 1644 fromScope: 0, 1645 toScope: total_scopes, 1646 totalScopes: total_scopes, 1647 scopes: scopes 1648 } 1649}; 1650 1651 1652DebugCommandProcessor.prototype.scopeRequest_ = function(request, response) { 1653 // No frames no scopes. 1654 if (this.exec_state_.frameCount() == 0) { 1655 return response.failed('No scopes'); 1656 } 1657 1658 // Get the frame for which the scope is requested. 1659 var frame = this.frameForScopeRequest_(request); 1660 1661 // With no scope argument just return top scope. 1662 var scope_index = 0; 1663 if (request.arguments && !IS_UNDEFINED(request.arguments.number)) { 1664 scope_index = %ToNumber(request.arguments.number); 1665 if (scope_index < 0 || frame.scopeCount() <= scope_index) { 1666 return response.failed('Invalid scope number'); 1667 } 1668 } 1669 1670 response.body = frame.scope(scope_index); 1671}; 1672 1673 1674DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) { 1675 if (!request.arguments) { 1676 return response.failed('Missing arguments'); 1677 } 1678 1679 // Pull out arguments. 1680 var expression = request.arguments.expression; 1681 var frame = request.arguments.frame; 1682 var global = request.arguments.global; 1683 var disable_break = request.arguments.disable_break; 1684 1685 // The expression argument could be an integer so we convert it to a 1686 // string. 1687 try { 1688 expression = String(expression); 1689 } catch(e) { 1690 return response.failed('Failed to convert expression argument to string'); 1691 } 1692 1693 // Check for legal arguments. 1694 if (!IS_UNDEFINED(frame) && global) { 1695 return response.failed('Arguments "frame" and "global" are exclusive'); 1696 } 1697 1698 // Global evaluate. 1699 if (global) { 1700 // Evaluate in the global context. 1701 response.body = 1702 this.exec_state_.evaluateGlobal(expression), Boolean(disable_break); 1703 return; 1704 } 1705 1706 // Default value for disable_break is true. 1707 if (IS_UNDEFINED(disable_break)) { 1708 disable_break = true; 1709 } 1710 1711 // No frames no evaluate in frame. 1712 if (this.exec_state_.frameCount() == 0) { 1713 return response.failed('No frames'); 1714 } 1715 1716 // Check whether a frame was specified. 1717 if (!IS_UNDEFINED(frame)) { 1718 var frame_number = %ToNumber(frame); 1719 if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) { 1720 return response.failed('Invalid frame "' + frame + '"'); 1721 } 1722 // Evaluate in the specified frame. 1723 response.body = this.exec_state_.frame(frame_number).evaluate( 1724 expression, Boolean(disable_break)); 1725 return; 1726 } else { 1727 // Evaluate in the selected frame. 1728 response.body = this.exec_state_.frame().evaluate( 1729 expression, Boolean(disable_break)); 1730 return; 1731 } 1732}; 1733 1734 1735DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) { 1736 if (!request.arguments) { 1737 return response.failed('Missing arguments'); 1738 } 1739 1740 // Pull out arguments. 1741 var handles = request.arguments.handles; 1742 1743 // Check for legal arguments. 1744 if (IS_UNDEFINED(handles)) { 1745 return response.failed('Argument "handles" missing'); 1746 } 1747 1748 // Set 'includeSource' option for script lookup. 1749 if (!IS_UNDEFINED(request.arguments.includeSource)) { 1750 includeSource = %ToBoolean(request.arguments.includeSource); 1751 response.setOption('includeSource', includeSource); 1752 } 1753 1754 // Lookup handles. 1755 var mirrors = {}; 1756 for (var i = 0; i < handles.length; i++) { 1757 var handle = handles[i]; 1758 var mirror = LookupMirror(handle); 1759 if (!mirror) { 1760 return response.failed('Object #' + handle + '# not found'); 1761 } 1762 mirrors[handle] = mirror; 1763 } 1764 response.body = mirrors; 1765}; 1766 1767 1768DebugCommandProcessor.prototype.referencesRequest_ = 1769 function(request, response) { 1770 if (!request.arguments) { 1771 return response.failed('Missing arguments'); 1772 } 1773 1774 // Pull out arguments. 1775 var type = request.arguments.type; 1776 var handle = request.arguments.handle; 1777 1778 // Check for legal arguments. 1779 if (IS_UNDEFINED(type)) { 1780 return response.failed('Argument "type" missing'); 1781 } 1782 if (IS_UNDEFINED(handle)) { 1783 return response.failed('Argument "handle" missing'); 1784 } 1785 if (type != 'referencedBy' && type != 'constructedBy') { 1786 return response.failed('Invalid type "' + type + '"'); 1787 } 1788 1789 // Lookup handle and return objects with references the object. 1790 var mirror = LookupMirror(handle); 1791 if (mirror) { 1792 if (type == 'referencedBy') { 1793 response.body = mirror.referencedBy(); 1794 } else { 1795 response.body = mirror.constructedBy(); 1796 } 1797 } else { 1798 return response.failed('Object #' + handle + '# not found'); 1799 } 1800}; 1801 1802 1803DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) { 1804 // No frames no source. 1805 if (this.exec_state_.frameCount() == 0) { 1806 return response.failed('No source'); 1807 } 1808 1809 var from_line; 1810 var to_line; 1811 var frame = this.exec_state_.frame(); 1812 if (request.arguments) { 1813 // Pull out arguments. 1814 from_line = request.arguments.fromLine; 1815 to_line = request.arguments.toLine; 1816 1817 if (!IS_UNDEFINED(request.arguments.frame)) { 1818 var frame_number = %ToNumber(request.arguments.frame); 1819 if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) { 1820 return response.failed('Invalid frame "' + frame + '"'); 1821 } 1822 frame = this.exec_state_.frame(frame_number); 1823 } 1824 } 1825 1826 // Get the script selected. 1827 var script = frame.func().script(); 1828 if (!script) { 1829 return response.failed('No source'); 1830 } 1831 1832 // Get the source slice and fill it into the response. 1833 var slice = script.sourceSlice(from_line, to_line); 1834 if (!slice) { 1835 return response.failed('Invalid line interval'); 1836 } 1837 response.body = {}; 1838 response.body.source = slice.sourceText(); 1839 response.body.fromLine = slice.from_line; 1840 response.body.toLine = slice.to_line; 1841 response.body.fromPosition = slice.from_position; 1842 response.body.toPosition = slice.to_position; 1843 response.body.totalLines = script.lineCount(); 1844}; 1845 1846 1847DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) { 1848 var types = ScriptTypeFlag(Debug.ScriptType.Normal); 1849 var includeSource = false; 1850 var idsToInclude = null; 1851 if (request.arguments) { 1852 // Pull out arguments. 1853 if (!IS_UNDEFINED(request.arguments.types)) { 1854 types = %ToNumber(request.arguments.types); 1855 if (isNaN(types) || types < 0) { 1856 return response.failed('Invalid types "' + request.arguments.types + '"'); 1857 } 1858 } 1859 1860 if (!IS_UNDEFINED(request.arguments.includeSource)) { 1861 includeSource = %ToBoolean(request.arguments.includeSource); 1862 response.setOption('includeSource', includeSource); 1863 } 1864 1865 if (IS_ARRAY(request.arguments.ids)) { 1866 idsToInclude = {}; 1867 var ids = request.arguments.ids; 1868 for (var i = 0; i < ids.length; i++) { 1869 idsToInclude[ids[i]] = true; 1870 } 1871 } 1872 } 1873 1874 // Collect all scripts in the heap. 1875 var scripts = %DebugGetLoadedScripts(); 1876 1877 response.body = []; 1878 1879 for (var i = 0; i < scripts.length; i++) { 1880 if (idsToInclude && !idsToInclude[scripts[i].id]) { 1881 continue; 1882 } 1883 if (types & ScriptTypeFlag(scripts[i].type)) { 1884 response.body.push(MakeMirror(scripts[i])); 1885 } 1886 } 1887}; 1888 1889 1890DebugCommandProcessor.prototype.threadsRequest_ = function(request, response) { 1891 // Get the number of threads. 1892 var total_threads = this.exec_state_.threadCount(); 1893 1894 // Get information for all threads. 1895 var threads = []; 1896 for (var i = 0; i < total_threads; i++) { 1897 var details = %GetThreadDetails(this.exec_state_.break_id, i); 1898 var thread_info = { current: details[0], 1899 id: details[1] 1900 } 1901 threads.push(thread_info); 1902 } 1903 1904 // Create the response body. 1905 response.body = { 1906 totalThreads: total_threads, 1907 threads: threads 1908 } 1909}; 1910 1911 1912// Check whether the previously processed command caused the VM to become 1913// running. 1914DebugCommandProcessor.prototype.isRunning = function() { 1915 return this.running_; 1916} 1917 1918 1919DebugCommandProcessor.prototype.systemBreak = function(cmd, args) { 1920 return %SystemBreak(); 1921}; 1922 1923 1924function NumberToHex8Str(n) { 1925 var r = ""; 1926 for (var i = 0; i < 8; ++i) { 1927 var c = hexCharArray[n & 0x0F]; // hexCharArray is defined in uri.js 1928 r = c + r; 1929 n = n >>> 4; 1930 } 1931 return r; 1932}; 1933 1934DebugCommandProcessor.prototype.formatCFrames = function(cframes_value) { 1935 var result = ""; 1936 if (cframes_value == null || cframes_value.length == 0) { 1937 result += "(stack empty)"; 1938 } else { 1939 for (var i = 0; i < cframes_value.length; ++i) { 1940 if (i != 0) result += "\n"; 1941 result += this.formatCFrame(cframes_value[i]); 1942 } 1943 } 1944 return result; 1945}; 1946 1947 1948DebugCommandProcessor.prototype.formatCFrame = function(cframe_value) { 1949 var result = ""; 1950 result += "0x" + NumberToHex8Str(cframe_value.address); 1951 if (!IS_UNDEFINED(cframe_value.text)) { 1952 result += " " + cframe_value.text; 1953 } 1954 return result; 1955} 1956 1957 1958/** 1959 * Convert an Object to its debugger protocol representation. The representation 1960 * may be serilized to a JSON object using JSON.stringify(). 1961 * This implementation simply runs through all string property names, converts 1962 * each property value to a protocol value and adds the property to the result 1963 * object. For type "object" the function will be called recursively. Note that 1964 * circular structures will cause infinite recursion. 1965 * @param {Object} object The object to format as protocol object. 1966 * @param {MirrorSerializer} mirror_serializer The serializer to use if any 1967 * mirror objects are encountered. 1968 * @return {Object} Protocol object value. 1969 */ 1970function ObjectToProtocolObject_(object, mirror_serializer) { 1971 var content = {}; 1972 for (var key in object) { 1973 // Only consider string keys. 1974 if (typeof key == 'string') { 1975 // Format the value based on its type. 1976 var property_value_json = ValueToProtocolValue_(object[key], 1977 mirror_serializer); 1978 // Add the property if relevant. 1979 if (!IS_UNDEFINED(property_value_json)) { 1980 content[key] = property_value_json; 1981 } 1982 } 1983 } 1984 1985 return content; 1986} 1987 1988 1989/** 1990 * Convert an array to its debugger protocol representation. It will convert 1991 * each array element to a protocol value. 1992 * @param {Array} array The array to format as protocol array. 1993 * @param {MirrorSerializer} mirror_serializer The serializer to use if any 1994 * mirror objects are encountered. 1995 * @return {Array} Protocol array value. 1996 */ 1997function ArrayToProtocolArray_(array, mirror_serializer) { 1998 var json = []; 1999 for (var i = 0; i < array.length; i++) { 2000 json.push(ValueToProtocolValue_(array[i], mirror_serializer)); 2001 } 2002 return json; 2003} 2004 2005 2006/** 2007 * Convert a value to its debugger protocol representation. 2008 * @param {*} value The value to format as protocol value. 2009 * @param {MirrorSerializer} mirror_serializer The serializer to use if any 2010 * mirror objects are encountered. 2011 * @return {*} Protocol value. 2012 */ 2013function ValueToProtocolValue_(value, mirror_serializer) { 2014 // Format the value based on its type. 2015 var json; 2016 switch (typeof value) { 2017 case 'object': 2018 if (value instanceof Mirror) { 2019 json = mirror_serializer.serializeValue(value); 2020 } else if (IS_ARRAY(value)){ 2021 json = ArrayToProtocolArray_(value, mirror_serializer); 2022 } else { 2023 json = ObjectToProtocolObject_(value, mirror_serializer); 2024 } 2025 break; 2026 2027 case 'boolean': 2028 case 'string': 2029 case 'number': 2030 json = value; 2031 break 2032 2033 default: 2034 json = null; 2035 } 2036 return json; 2037} 2038