1// Copyright 2012 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5(function (global, utils) { 6"use strict"; 7 8// ---------------------------------------------------------------------------- 9// Imports 10 11var FrameMirror = global.FrameMirror; 12var GlobalArray = global.Array; 13var GlobalRegExp = global.RegExp; 14var IsNaN = global.isNaN; 15var MakeMirror = global.MakeMirror; 16var MathMin = global.Math.min; 17var Mirror = global.Mirror; 18var ValueMirror = global.ValueMirror; 19 20//---------------------------------------------------------------------------- 21 22// Default number of frames to include in the response to backtrace request. 23var kDefaultBacktraceLength = 10; 24 25var Debug = {}; 26 27// Regular expression to skip "crud" at the beginning of a source line which is 28// not really code. Currently the regular expression matches whitespace and 29// comments. 30var sourceLineBeginningSkip = /^(?:\s*(?:\/\*.*?\*\/)*)*/; 31 32// Debug events which can occour in the V8 JavaScript engine. These originate 33// from the API include file debug.h. 34Debug.DebugEvent = { Break: 1, 35 Exception: 2, 36 AfterCompile: 3, 37 CompileError: 4, 38 AsyncTaskEvent: 5 }; 39 40// Types of exceptions that can be broken upon. 41Debug.ExceptionBreak = { Caught : 0, 42 Uncaught: 1 }; 43 44// The different types of steps. 45Debug.StepAction = { StepOut: 0, 46 StepNext: 1, 47 StepIn: 2 }; 48 49// The different types of scripts matching enum ScriptType in objects.h. 50Debug.ScriptType = { Native: 0, 51 Extension: 1, 52 Normal: 2, 53 Wasm: 3}; 54 55// The different types of script compilations matching enum 56// Script::CompilationType in objects.h. 57Debug.ScriptCompilationType = { Host: 0, 58 Eval: 1, 59 JSON: 2 }; 60 61// The different script break point types. 62Debug.ScriptBreakPointType = { ScriptId: 0, 63 ScriptName: 1, 64 ScriptRegExp: 2 }; 65 66// The different types of breakpoint position alignments. 67// Must match BreakPositionAlignment in debug.h. 68Debug.BreakPositionAlignment = { 69 Statement: 0, 70 BreakPosition: 1 71}; 72 73function ScriptTypeFlag(type) { 74 return (1 << type); 75} 76 77// Globals. 78var next_response_seq = 0; 79var next_break_point_number = 1; 80var break_points = []; 81var script_break_points = []; 82var debugger_flags = { 83 breakPointsActive: { 84 value: true, 85 getValue: function() { return this.value; }, 86 setValue: function(value) { 87 this.value = !!value; 88 %SetBreakPointsActive(this.value); 89 } 90 }, 91 breakOnCaughtException: { 92 getValue: function() { return Debug.isBreakOnException(); }, 93 setValue: function(value) { 94 if (value) { 95 Debug.setBreakOnException(); 96 } else { 97 Debug.clearBreakOnException(); 98 } 99 } 100 }, 101 breakOnUncaughtException: { 102 getValue: function() { return Debug.isBreakOnUncaughtException(); }, 103 setValue: function(value) { 104 if (value) { 105 Debug.setBreakOnUncaughtException(); 106 } else { 107 Debug.clearBreakOnUncaughtException(); 108 } 109 } 110 }, 111}; 112 113 114// Create a new break point object and add it to the list of break points. 115function MakeBreakPoint(source_position, opt_script_break_point) { 116 var break_point = new BreakPoint(source_position, opt_script_break_point); 117 break_points.push(break_point); 118 return break_point; 119} 120 121 122// Object representing a break point. 123// NOTE: This object does not have a reference to the function having break 124// point as this would cause function not to be garbage collected when it is 125// not used any more. We do not want break points to keep functions alive. 126function BreakPoint(source_position, opt_script_break_point) { 127 this.source_position_ = source_position; 128 if (opt_script_break_point) { 129 this.script_break_point_ = opt_script_break_point; 130 } else { 131 this.number_ = next_break_point_number++; 132 } 133 this.active_ = true; 134 this.condition_ = null; 135} 136 137 138BreakPoint.prototype.number = function() { 139 return this.number_; 140}; 141 142 143BreakPoint.prototype.func = function() { 144 return this.func_; 145}; 146 147 148BreakPoint.prototype.source_position = function() { 149 return this.source_position_; 150}; 151 152 153BreakPoint.prototype.active = function() { 154 if (this.script_break_point()) { 155 return this.script_break_point().active(); 156 } 157 return this.active_; 158}; 159 160 161BreakPoint.prototype.condition = function() { 162 if (this.script_break_point() && this.script_break_point().condition()) { 163 return this.script_break_point().condition(); 164 } 165 return this.condition_; 166}; 167 168 169BreakPoint.prototype.script_break_point = function() { 170 return this.script_break_point_; 171}; 172 173 174BreakPoint.prototype.enable = function() { 175 this.active_ = true; 176}; 177 178 179BreakPoint.prototype.disable = function() { 180 this.active_ = false; 181}; 182 183 184BreakPoint.prototype.setCondition = function(condition) { 185 this.condition_ = condition; 186}; 187 188 189BreakPoint.prototype.isTriggered = function(exec_state) { 190 // Break point not active - not triggered. 191 if (!this.active()) return false; 192 193 // Check for conditional break point. 194 if (this.condition()) { 195 // If break point has condition try to evaluate it in the top frame. 196 try { 197 var mirror = exec_state.frame(0).evaluate(this.condition()); 198 // If no sensible mirror or non true value break point not triggered. 199 if (!(mirror instanceof ValueMirror) || !mirror.value_) { 200 return false; 201 } 202 } catch (e) { 203 // Exception evaluating condition counts as not triggered. 204 return false; 205 } 206 } 207 208 // Break point triggered. 209 return true; 210}; 211 212 213// Function called from the runtime when a break point is hit. Returns true if 214// the break point is triggered and supposed to break execution. 215function IsBreakPointTriggered(break_id, break_point) { 216 return break_point.isTriggered(MakeExecutionState(break_id)); 217} 218 219 220// Object representing a script break point. The script is referenced by its 221// script name or script id and the break point is represented as line and 222// column. 223function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column, 224 opt_groupId, opt_position_alignment) { 225 this.type_ = type; 226 if (type == Debug.ScriptBreakPointType.ScriptId) { 227 this.script_id_ = script_id_or_name; 228 } else if (type == Debug.ScriptBreakPointType.ScriptName) { 229 this.script_name_ = script_id_or_name; 230 } else if (type == Debug.ScriptBreakPointType.ScriptRegExp) { 231 this.script_regexp_object_ = new GlobalRegExp(script_id_or_name); 232 } else { 233 throw %make_error(kDebugger, "Unexpected breakpoint type " + type); 234 } 235 this.line_ = opt_line || 0; 236 this.column_ = opt_column; 237 this.groupId_ = opt_groupId; 238 this.position_alignment_ = IS_UNDEFINED(opt_position_alignment) 239 ? Debug.BreakPositionAlignment.Statement : opt_position_alignment; 240 this.active_ = true; 241 this.condition_ = null; 242 this.break_points_ = []; 243} 244 245 246ScriptBreakPoint.prototype.number = function() { 247 return this.number_; 248}; 249 250 251ScriptBreakPoint.prototype.groupId = function() { 252 return this.groupId_; 253}; 254 255 256ScriptBreakPoint.prototype.type = function() { 257 return this.type_; 258}; 259 260 261ScriptBreakPoint.prototype.script_id = function() { 262 return this.script_id_; 263}; 264 265 266ScriptBreakPoint.prototype.script_name = function() { 267 return this.script_name_; 268}; 269 270 271ScriptBreakPoint.prototype.script_regexp_object = function() { 272 return this.script_regexp_object_; 273}; 274 275 276ScriptBreakPoint.prototype.line = function() { 277 return this.line_; 278}; 279 280 281ScriptBreakPoint.prototype.column = function() { 282 return this.column_; 283}; 284 285 286ScriptBreakPoint.prototype.actual_locations = function() { 287 var locations = []; 288 for (var i = 0; i < this.break_points_.length; i++) { 289 locations.push(this.break_points_[i].actual_location); 290 } 291 return locations; 292}; 293 294 295ScriptBreakPoint.prototype.update_positions = function(line, column) { 296 this.line_ = line; 297 this.column_ = column; 298}; 299 300 301ScriptBreakPoint.prototype.active = function() { 302 return this.active_; 303}; 304 305 306ScriptBreakPoint.prototype.condition = function() { 307 return this.condition_; 308}; 309 310 311ScriptBreakPoint.prototype.enable = function() { 312 this.active_ = true; 313}; 314 315 316ScriptBreakPoint.prototype.disable = function() { 317 this.active_ = false; 318}; 319 320 321ScriptBreakPoint.prototype.setCondition = function(condition) { 322 this.condition_ = condition; 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 { 332 // We might want to account columns here as well. 333 if (!(script.line_offset <= this.line_ && 334 this.line_ < script.line_offset + %ScriptLineCount(script))) { 335 return false; 336 } 337 if (this.type_ == Debug.ScriptBreakPointType.ScriptName) { 338 return this.script_name_ == script.nameOrSourceURL(); 339 } else if (this.type_ == Debug.ScriptBreakPointType.ScriptRegExp) { 340 return this.script_regexp_object_.test(script.nameOrSourceURL()); 341 } else { 342 throw %make_error(kDebugger, "Unexpected breakpoint type " + this.type_); 343 } 344 } 345}; 346 347 348// Set the script break point in a script. 349ScriptBreakPoint.prototype.set = function (script) { 350 var column = this.column(); 351 var line = this.line(); 352 // If the column is undefined the break is on the line. To help locate the 353 // first piece of breakable code on the line try to find the column on the 354 // line which contains some source. 355 if (IS_UNDEFINED(column)) { 356 var source_line = %ScriptSourceLine(script, line || script.line_offset); 357 358 // Allocate array for caching the columns where the actual source starts. 359 if (!script.sourceColumnStart_) { 360 script.sourceColumnStart_ = new GlobalArray(%ScriptLineCount(script)); 361 } 362 363 // Fill cache if needed and get column where the actual source starts. 364 if (IS_UNDEFINED(script.sourceColumnStart_[line])) { 365 script.sourceColumnStart_[line] = 366 source_line.match(sourceLineBeginningSkip)[0].length; 367 } 368 column = script.sourceColumnStart_[line]; 369 } 370 371 // Convert the line and column into an absolute position within the script. 372 var position = Debug.findScriptSourcePosition(script, this.line(), column); 373 374 // If the position is not found in the script (the script might be shorter 375 // than it used to be) just ignore it. 376 if (IS_NULL(position)) return; 377 378 // Create a break point object and set the break point. 379 var break_point = MakeBreakPoint(position, this); 380 var actual_position = %SetScriptBreakPoint(script, position, 381 this.position_alignment_, 382 break_point); 383 if (IS_UNDEFINED(actual_position)) { 384 actual_position = position; 385 } 386 var actual_location = script.locationFromPosition(actual_position, true); 387 break_point.actual_location = { line: actual_location.line, 388 column: actual_location.column, 389 script_id: script.id }; 390 this.break_points_.push(break_point); 391 return break_point; 392}; 393 394 395// Clear all the break points created from this script break point 396ScriptBreakPoint.prototype.clear = function () { 397 var remaining_break_points = []; 398 for (var i = 0; i < break_points.length; i++) { 399 if (break_points[i].script_break_point() && 400 break_points[i].script_break_point() === this) { 401 %ClearBreakPoint(break_points[i]); 402 } else { 403 remaining_break_points.push(break_points[i]); 404 } 405 } 406 break_points = remaining_break_points; 407 this.break_points_ = []; 408}; 409 410 411Debug.setListener = function(listener, opt_data) { 412 if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) { 413 throw %make_type_error(kDebuggerType); 414 } 415 %SetDebugEventListener(listener, opt_data); 416}; 417 418 419// Returns a Script object. If the parameter is a function the return value 420// is the script in which the function is defined. If the parameter is a string 421// the return value is the script for which the script name has that string 422// value. If it is a regexp and there is a unique script whose name matches 423// we return that, otherwise undefined. 424Debug.findScript = function(func_or_script_name) { 425 if (IS_FUNCTION(func_or_script_name)) { 426 return %FunctionGetScript(func_or_script_name); 427 } else if (%IsRegExp(func_or_script_name)) { 428 var scripts = this.scripts(); 429 var last_result = null; 430 var result_count = 0; 431 for (var i in scripts) { 432 var script = scripts[i]; 433 if (func_or_script_name.test(script.name)) { 434 last_result = script; 435 result_count++; 436 } 437 } 438 // Return the unique script matching the regexp. If there are more 439 // than one we don't return a value since there is no good way to 440 // decide which one to return. Returning a "random" one, say the 441 // first, would introduce nondeterminism (or something close to it) 442 // because the order is the heap iteration order. 443 if (result_count == 1) { 444 return last_result; 445 } else { 446 return UNDEFINED; 447 } 448 } else { 449 return %GetScript(func_or_script_name); 450 } 451}; 452 453// Returns the script source. If the parameter is a function the return value 454// is the script source for the script in which the function is defined. If the 455// parameter is a string the return value is the script for which the script 456// name has that string value. 457Debug.scriptSource = function(func_or_script_name) { 458 return this.findScript(func_or_script_name).source; 459}; 460 461 462Debug.source = function(f) { 463 if (!IS_FUNCTION(f)) throw %make_type_error(kDebuggerType); 464 return %FunctionGetSourceCode(f); 465}; 466 467 468Debug.sourcePosition = function(f) { 469 if (!IS_FUNCTION(f)) throw %make_type_error(kDebuggerType); 470 return %FunctionGetScriptSourcePosition(f); 471}; 472 473 474Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) { 475 var script = %FunctionGetScript(func); 476 var script_offset = %FunctionGetScriptSourcePosition(func); 477 return %ScriptLocationFromLine(script, opt_line, opt_column, script_offset); 478}; 479 480 481// Returns the character position in a script based on a line number and an 482// optional position within that line. 483Debug.findScriptSourcePosition = function(script, opt_line, opt_column) { 484 var location = %ScriptLocationFromLine(script, opt_line, opt_column, 0); 485 return location ? location.position : null; 486}; 487 488 489Debug.findBreakPoint = function(break_point_number, remove) { 490 var break_point; 491 for (var i = 0; i < break_points.length; i++) { 492 if (break_points[i].number() == break_point_number) { 493 break_point = break_points[i]; 494 // Remove the break point from the list if requested. 495 if (remove) { 496 break_points.splice(i, 1); 497 } 498 break; 499 } 500 } 501 if (break_point) { 502 return break_point; 503 } else { 504 return this.findScriptBreakPoint(break_point_number, remove); 505 } 506}; 507 508Debug.findBreakPointActualLocations = function(break_point_number) { 509 for (var i = 0; i < script_break_points.length; i++) { 510 if (script_break_points[i].number() == break_point_number) { 511 return script_break_points[i].actual_locations(); 512 } 513 } 514 for (var i = 0; i < break_points.length; i++) { 515 if (break_points[i].number() == break_point_number) { 516 return [break_points[i].actual_location]; 517 } 518 } 519 return []; 520}; 521 522Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) { 523 if (!IS_FUNCTION(func)) throw %make_type_error(kDebuggerType); 524 // Break points in API functions are not supported. 525 if (%FunctionIsAPIFunction(func)) { 526 throw %make_error(kDebugger, 'Cannot set break point in native code.'); 527 } 528 // Find source position. 529 var source_position = 530 this.findFunctionSourceLocation(func, opt_line, opt_column).position; 531 // Find the script for the function. 532 var script = %FunctionGetScript(func); 533 // Break in builtin JavaScript code is not supported. 534 if (script.type == Debug.ScriptType.Native) { 535 throw %make_error(kDebugger, 'Cannot set break point in native code.'); 536 } 537 // If the script for the function has a name convert this to a script break 538 // point. 539 if (script && script.id) { 540 // Find line and column for the position in the script and set a script 541 // break point from that. 542 var location = script.locationFromPosition(source_position, false); 543 return this.setScriptBreakPointById(script.id, 544 location.line, location.column, 545 opt_condition); 546 } else { 547 // Set a break point directly on the function. 548 var break_point = MakeBreakPoint(source_position); 549 var actual_position = 550 %SetFunctionBreakPoint(func, source_position, break_point); 551 var actual_location = script.locationFromPosition(actual_position, true); 552 break_point.actual_location = { line: actual_location.line, 553 column: actual_location.column, 554 script_id: script.id }; 555 break_point.setCondition(opt_condition); 556 return break_point.number(); 557 } 558}; 559 560 561Debug.setBreakPointByScriptIdAndPosition = function(script_id, position, 562 condition, enabled, 563 opt_position_alignment) 564{ 565 var break_point = MakeBreakPoint(position); 566 break_point.setCondition(condition); 567 if (!enabled) { 568 break_point.disable(); 569 } 570 var script = scriptById(script_id); 571 if (script) { 572 var position_alignment = IS_UNDEFINED(opt_position_alignment) 573 ? Debug.BreakPositionAlignment.Statement : opt_position_alignment; 574 break_point.actual_position = %SetScriptBreakPoint(script, position, 575 position_alignment, break_point); 576 } 577 return break_point; 578}; 579 580 581Debug.enableBreakPoint = function(break_point_number) { 582 var break_point = this.findBreakPoint(break_point_number, false); 583 // Only enable if the breakpoint hasn't been deleted: 584 if (break_point) { 585 break_point.enable(); 586 } 587}; 588 589 590Debug.disableBreakPoint = function(break_point_number) { 591 var break_point = this.findBreakPoint(break_point_number, false); 592 // Only enable if the breakpoint hasn't been deleted: 593 if (break_point) { 594 break_point.disable(); 595 } 596}; 597 598 599Debug.changeBreakPointCondition = function(break_point_number, condition) { 600 var break_point = this.findBreakPoint(break_point_number, false); 601 break_point.setCondition(condition); 602}; 603 604 605Debug.clearBreakPoint = function(break_point_number) { 606 var break_point = this.findBreakPoint(break_point_number, true); 607 if (break_point) { 608 return %ClearBreakPoint(break_point); 609 } else { 610 break_point = this.findScriptBreakPoint(break_point_number, true); 611 if (!break_point) throw %make_error(kDebugger, 'Invalid breakpoint'); 612 } 613}; 614 615 616Debug.clearAllBreakPoints = function() { 617 for (var i = 0; i < break_points.length; i++) { 618 var break_point = break_points[i]; 619 %ClearBreakPoint(break_point); 620 } 621 break_points = []; 622}; 623 624 625Debug.disableAllBreakPoints = function() { 626 // Disable all user defined breakpoints: 627 for (var i = 1; i < next_break_point_number; i++) { 628 Debug.disableBreakPoint(i); 629 } 630 // Disable all exception breakpoints: 631 %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false); 632 %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false); 633}; 634 635 636Debug.findScriptBreakPoint = function(break_point_number, remove) { 637 var script_break_point; 638 for (var i = 0; i < script_break_points.length; i++) { 639 if (script_break_points[i].number() == break_point_number) { 640 script_break_point = script_break_points[i]; 641 // Remove the break point from the list if requested. 642 if (remove) { 643 script_break_point.clear(); 644 script_break_points.splice(i,1); 645 } 646 break; 647 } 648 } 649 return script_break_point; 650}; 651 652 653// Sets a breakpoint in a script identified through id or name at the 654// specified source line and column within that line. 655Debug.setScriptBreakPoint = function(type, script_id_or_name, 656 opt_line, opt_column, opt_condition, 657 opt_groupId, opt_position_alignment) { 658 // Create script break point object. 659 var script_break_point = 660 new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column, 661 opt_groupId, opt_position_alignment); 662 663 // Assign number to the new script break point and add it. 664 script_break_point.number_ = next_break_point_number++; 665 script_break_point.setCondition(opt_condition); 666 script_break_points.push(script_break_point); 667 668 // Run through all scripts to see if this script break point matches any 669 // loaded scripts. 670 var scripts = this.scripts(); 671 for (var i = 0; i < scripts.length; i++) { 672 if (script_break_point.matchesScript(scripts[i])) { 673 script_break_point.set(scripts[i]); 674 } 675 } 676 677 return script_break_point.number(); 678}; 679 680 681Debug.setScriptBreakPointById = function(script_id, 682 opt_line, opt_column, 683 opt_condition, opt_groupId, 684 opt_position_alignment) { 685 return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId, 686 script_id, opt_line, opt_column, 687 opt_condition, opt_groupId, 688 opt_position_alignment); 689}; 690 691 692Debug.setScriptBreakPointByName = function(script_name, 693 opt_line, opt_column, 694 opt_condition, opt_groupId) { 695 return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName, 696 script_name, opt_line, opt_column, 697 opt_condition, opt_groupId); 698}; 699 700 701Debug.setScriptBreakPointByRegExp = function(script_regexp, 702 opt_line, opt_column, 703 opt_condition, opt_groupId) { 704 return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptRegExp, 705 script_regexp, opt_line, opt_column, 706 opt_condition, opt_groupId); 707}; 708 709 710Debug.enableScriptBreakPoint = function(break_point_number) { 711 var script_break_point = this.findScriptBreakPoint(break_point_number, false); 712 script_break_point.enable(); 713}; 714 715 716Debug.disableScriptBreakPoint = function(break_point_number) { 717 var script_break_point = this.findScriptBreakPoint(break_point_number, false); 718 script_break_point.disable(); 719}; 720 721 722Debug.changeScriptBreakPointCondition = function( 723 break_point_number, condition) { 724 var script_break_point = this.findScriptBreakPoint(break_point_number, false); 725 script_break_point.setCondition(condition); 726}; 727 728 729Debug.scriptBreakPoints = function() { 730 return script_break_points; 731}; 732 733 734Debug.clearStepping = function() { 735 %ClearStepping(); 736}; 737 738Debug.setBreakOnException = function() { 739 return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, true); 740}; 741 742Debug.clearBreakOnException = function() { 743 return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false); 744}; 745 746Debug.isBreakOnException = function() { 747 return !!%IsBreakOnException(Debug.ExceptionBreak.Caught); 748}; 749 750Debug.setBreakOnUncaughtException = function() { 751 return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true); 752}; 753 754Debug.clearBreakOnUncaughtException = function() { 755 return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false); 756}; 757 758Debug.isBreakOnUncaughtException = function() { 759 return !!%IsBreakOnException(Debug.ExceptionBreak.Uncaught); 760}; 761 762Debug.showBreakPoints = function(f, full, opt_position_alignment) { 763 if (!IS_FUNCTION(f)) throw %make_error(kDebuggerType); 764 var source = full ? this.scriptSource(f) : this.source(f); 765 var offset = full ? 0 : this.sourcePosition(f); 766 var position_alignment = IS_UNDEFINED(opt_position_alignment) 767 ? Debug.BreakPositionAlignment.Statement : opt_position_alignment; 768 var locations = %GetBreakLocations(f, position_alignment); 769 if (!locations) return source; 770 locations.sort(function(x, y) { return x - y; }); 771 var result = ""; 772 var prev_pos = 0; 773 var pos; 774 for (var i = 0; i < locations.length; i++) { 775 pos = locations[i] - offset; 776 result += source.slice(prev_pos, pos); 777 result += "[B" + i + "]"; 778 prev_pos = pos; 779 } 780 pos = source.length; 781 result += source.substring(prev_pos, pos); 782 return result; 783}; 784 785 786// Get all the scripts currently loaded. Locating all the scripts is based on 787// scanning the heap. 788Debug.scripts = function() { 789 // Collect all scripts in the heap. 790 return %DebugGetLoadedScripts(); 791}; 792 793 794// Get a specific script currently loaded. This is based on scanning the heap. 795// TODO(clemensh): Create a runtime function for this. 796function scriptById(scriptId) { 797 var scripts = Debug.scripts(); 798 for (var script of scripts) { 799 if (script.id == scriptId) return script; 800 } 801 return UNDEFINED; 802}; 803 804 805Debug.debuggerFlags = function() { 806 return debugger_flags; 807}; 808 809Debug.MakeMirror = MakeMirror; 810 811function MakeExecutionState(break_id) { 812 return new ExecutionState(break_id); 813} 814 815function ExecutionState(break_id) { 816 this.break_id = break_id; 817 this.selected_frame = 0; 818} 819 820ExecutionState.prototype.prepareStep = function(action) { 821 if (action === Debug.StepAction.StepIn || 822 action === Debug.StepAction.StepOut || 823 action === Debug.StepAction.StepNext) { 824 return %PrepareStep(this.break_id, action); 825 } 826 throw %make_type_error(kDebuggerType); 827}; 828 829ExecutionState.prototype.evaluateGlobal = function(source) { 830 return MakeMirror(%DebugEvaluateGlobal(this.break_id, source)); 831}; 832 833ExecutionState.prototype.frameCount = function() { 834 return %GetFrameCount(this.break_id); 835}; 836 837ExecutionState.prototype.frame = function(opt_index) { 838 // If no index supplied return the selected frame. 839 if (opt_index == null) opt_index = this.selected_frame; 840 if (opt_index < 0 || opt_index >= this.frameCount()) { 841 throw %make_type_error(kDebuggerFrame); 842 } 843 return new FrameMirror(this.break_id, opt_index); 844}; 845 846ExecutionState.prototype.setSelectedFrame = function(index) { 847 var i = TO_NUMBER(index); 848 if (i < 0 || i >= this.frameCount()) { 849 throw %make_type_error(kDebuggerFrame); 850 } 851 this.selected_frame = i; 852}; 853 854ExecutionState.prototype.selectedFrame = function() { 855 return this.selected_frame; 856}; 857 858function MakeBreakEvent(break_id, break_points_hit) { 859 return new BreakEvent(break_id, break_points_hit); 860} 861 862 863function BreakEvent(break_id, break_points_hit) { 864 this.frame_ = new FrameMirror(break_id, 0); 865 this.break_points_hit_ = break_points_hit; 866} 867 868 869BreakEvent.prototype.eventType = function() { 870 return Debug.DebugEvent.Break; 871}; 872 873 874BreakEvent.prototype.func = function() { 875 return this.frame_.func(); 876}; 877 878 879BreakEvent.prototype.sourceLine = function() { 880 return this.frame_.sourceLine(); 881}; 882 883 884BreakEvent.prototype.sourceColumn = function() { 885 return this.frame_.sourceColumn(); 886}; 887 888 889BreakEvent.prototype.sourceLineText = function() { 890 return this.frame_.sourceLineText(); 891}; 892 893 894BreakEvent.prototype.breakPointsHit = function() { 895 return this.break_points_hit_; 896}; 897 898 899function MakeExceptionEvent(break_id, exception, uncaught, promise) { 900 return new ExceptionEvent(break_id, exception, uncaught, promise); 901} 902 903 904function ExceptionEvent(break_id, exception, uncaught, promise) { 905 this.exec_state_ = new ExecutionState(break_id); 906 this.exception_ = exception; 907 this.uncaught_ = uncaught; 908 this.promise_ = promise; 909} 910 911 912ExceptionEvent.prototype.eventType = function() { 913 return Debug.DebugEvent.Exception; 914}; 915 916 917ExceptionEvent.prototype.exception = function() { 918 return this.exception_; 919}; 920 921 922ExceptionEvent.prototype.uncaught = function() { 923 return this.uncaught_; 924}; 925 926 927ExceptionEvent.prototype.promise = function() { 928 return this.promise_; 929}; 930 931 932ExceptionEvent.prototype.func = function() { 933 return this.exec_state_.frame(0).func(); 934}; 935 936 937ExceptionEvent.prototype.sourceLine = function() { 938 return this.exec_state_.frame(0).sourceLine(); 939}; 940 941 942ExceptionEvent.prototype.sourceColumn = function() { 943 return this.exec_state_.frame(0).sourceColumn(); 944}; 945 946 947ExceptionEvent.prototype.sourceLineText = function() { 948 return this.exec_state_.frame(0).sourceLineText(); 949}; 950 951 952function MakeCompileEvent(script, type) { 953 return new CompileEvent(script, type); 954} 955 956 957function CompileEvent(script, type) { 958 this.script_ = MakeMirror(script); 959 this.type_ = type; 960} 961 962 963CompileEvent.prototype.eventType = function() { 964 return this.type_; 965}; 966 967 968CompileEvent.prototype.script = function() { 969 return this.script_; 970}; 971 972 973function MakeScriptObject_(script, include_source) { 974 var o = { id: script.id(), 975 name: script.name(), 976 lineOffset: script.lineOffset(), 977 columnOffset: script.columnOffset(), 978 lineCount: script.lineCount(), 979 }; 980 if (!IS_UNDEFINED(script.data())) { 981 o.data = script.data(); 982 } 983 if (include_source) { 984 o.source = script.source(); 985 } 986 return o; 987} 988 989 990function MakeAsyncTaskEvent(type, id) { 991 return new AsyncTaskEvent(type, id); 992} 993 994 995function AsyncTaskEvent(type, id) { 996 this.type_ = type; 997 this.id_ = id; 998} 999 1000 1001AsyncTaskEvent.prototype.type = function() { 1002 return this.type_; 1003} 1004 1005 1006AsyncTaskEvent.prototype.id = function() { 1007 return this.id_; 1008} 1009 1010// ------------------------------------------------------------------- 1011// Exports 1012 1013utils.InstallConstants(global, [ 1014 "Debug", Debug, 1015 "BreakEvent", BreakEvent, 1016 "CompileEvent", CompileEvent, 1017 "BreakPoint", BreakPoint, 1018]); 1019 1020// Functions needed by the debugger runtime. 1021utils.InstallFunctions(utils, DONT_ENUM, [ 1022 "MakeExecutionState", MakeExecutionState, 1023 "MakeExceptionEvent", MakeExceptionEvent, 1024 "MakeBreakEvent", MakeBreakEvent, 1025 "MakeCompileEvent", MakeCompileEvent, 1026 "MakeAsyncTaskEvent", MakeAsyncTaskEvent, 1027 "IsBreakPointTriggered", IsBreakPointTriggered, 1028]); 1029 1030}) 1031