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.
6 // Default number of frames to include in the response to backtrace request.
7 var kDefaultBacktraceLength = 10;
11 // Regular expression to skip "crud" at the beginning of a source line which is
12 // not really code. Currently the regular expression matches whitespace and
14 var sourceLineBeginningSkip = /^(?:\s*(?:\/\*.*?\*\/)*)*/;
16 // Debug events which can occour in the V8 JavaScript engine. These originate
17 // from the API include file debug.h.
18 Debug.DebugEvent = { Break: 1,
28 // Types of exceptions that can be broken upon.
29 Debug.ExceptionBreak = { Caught : 0,
32 // The different types of steps.
33 Debug.StepAction = { StepOut: 0,
40 // The different types of scripts matching enum ScriptType in objects.h.
41 Debug.ScriptType = { Native: 0,
45 // The different types of script compilations matching enum
46 // Script::CompilationType in objects.h.
47 Debug.ScriptCompilationType = { Host: 0,
51 // The different script break point types.
52 Debug.ScriptBreakPointType = { ScriptId: 0,
56 // The different types of breakpoint position alignments.
57 // Must match BreakPositionAlignment in debug.h.
58 Debug.BreakPositionAlignment = {
63 function ScriptTypeFlag(type) {
68 var next_response_seq = 0;
69 var next_break_point_number = 1;
70 var break_points = [];
71 var script_break_points = [];
72 var debugger_flags = {
75 getValue: function() { return this.value; },
76 setValue: function(value) {
78 %SetDisableBreak(!this.value);
81 breakOnCaughtException: {
82 getValue: function() { return Debug.isBreakOnException(); },
83 setValue: function(value) {
85 Debug.setBreakOnException();
87 Debug.clearBreakOnException();
91 breakOnUncaughtException: {
92 getValue: function() { return Debug.isBreakOnUncaughtException(); },
93 setValue: function(value) {
95 Debug.setBreakOnUncaughtException();
97 Debug.clearBreakOnUncaughtException();
104 // Create a new break point object and add it to the list of break points.
105 function MakeBreakPoint(source_position, opt_script_break_point) {
106 var break_point = new BreakPoint(source_position, opt_script_break_point);
107 break_points.push(break_point);
112 // Object representing a break point.
113 // NOTE: This object does not have a reference to the function having break
114 // point as this would cause function not to be garbage collected when it is
115 // not used any more. We do not want break points to keep functions alive.
116 function BreakPoint(source_position, opt_script_break_point) {
117 this.source_position_ = source_position;
118 if (opt_script_break_point) {
119 this.script_break_point_ = opt_script_break_point;
121 this.number_ = next_break_point_number++;
125 this.condition_ = null;
126 this.ignoreCount_ = 0;
130 BreakPoint.prototype.number = function() {
135 BreakPoint.prototype.func = function() {
140 BreakPoint.prototype.source_position = function() {
141 return this.source_position_;
145 BreakPoint.prototype.hit_count = function() {
146 return this.hit_count_;
150 BreakPoint.prototype.active = function() {
151 if (this.script_break_point()) {
152 return this.script_break_point().active();
158 BreakPoint.prototype.condition = function() {
159 if (this.script_break_point() && this.script_break_point().condition()) {
160 return this.script_break_point().condition();
162 return this.condition_;
166 BreakPoint.prototype.ignoreCount = function() {
167 return this.ignoreCount_;
171 BreakPoint.prototype.script_break_point = function() {
172 return this.script_break_point_;
176 BreakPoint.prototype.enable = function() {
181 BreakPoint.prototype.disable = function() {
182 this.active_ = false;
186 BreakPoint.prototype.setCondition = function(condition) {
187 this.condition_ = condition;
191 BreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
192 this.ignoreCount_ = ignoreCount;
196 BreakPoint.prototype.isTriggered = function(exec_state) {
197 // Break point not active - not triggered.
198 if (!this.active()) return false;
200 // Check for conditional break point.
201 if (this.condition()) {
202 // If break point has condition try to evaluate it in the top frame.
204 var mirror = exec_state.frame(0).evaluate(this.condition());
205 // If no sensible mirror or non true value break point not triggered.
206 if (!(mirror instanceof ValueMirror) || !%ToBoolean(mirror.value_)) {
210 // Exception evaluating condition counts as not triggered.
215 // Update the hit count.
217 if (this.script_break_point_) {
218 this.script_break_point_.hit_count_++;
221 // If the break point has an ignore count it is not triggered.
222 if (this.ignoreCount_ > 0) {
227 // Break point triggered.
232 // Function called from the runtime when a break point is hit. Returns true if
233 // the break point is triggered and supposed to break execution.
234 function IsBreakPointTriggered(break_id, break_point) {
235 return break_point.isTriggered(MakeExecutionState(break_id));
239 // Object representing a script break point. The script is referenced by its
240 // script name or script id and the break point is represented as line and
242 function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
243 opt_groupId, opt_position_alignment) {
245 if (type == Debug.ScriptBreakPointType.ScriptId) {
246 this.script_id_ = script_id_or_name;
247 } else if (type == Debug.ScriptBreakPointType.ScriptName) {
248 this.script_name_ = script_id_or_name;
249 } else if (type == Debug.ScriptBreakPointType.ScriptRegExp) {
250 this.script_regexp_object_ = new RegExp(script_id_or_name);
252 throw new Error("Unexpected breakpoint type " + type);
254 this.line_ = opt_line || 0;
255 this.column_ = opt_column;
256 this.groupId_ = opt_groupId;
257 this.position_alignment_ = IS_UNDEFINED(opt_position_alignment)
258 ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
261 this.condition_ = null;
262 this.ignoreCount_ = 0;
263 this.break_points_ = [];
267 //Creates a clone of script breakpoint that is linked to another script.
268 ScriptBreakPoint.prototype.cloneForOtherScript = function (other_script) {
269 var copy = new ScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
270 other_script.id, this.line_, this.column_, this.groupId_,
271 this.position_alignment_);
272 copy.number_ = next_break_point_number++;
273 script_break_points.push(copy);
275 copy.hit_count_ = this.hit_count_;
276 copy.active_ = this.active_;
277 copy.condition_ = this.condition_;
278 copy.ignoreCount_ = this.ignoreCount_;
283 ScriptBreakPoint.prototype.number = function() {
288 ScriptBreakPoint.prototype.groupId = function() {
289 return this.groupId_;
293 ScriptBreakPoint.prototype.type = function() {
298 ScriptBreakPoint.prototype.script_id = function() {
299 return this.script_id_;
303 ScriptBreakPoint.prototype.script_name = function() {
304 return this.script_name_;
308 ScriptBreakPoint.prototype.script_regexp_object = function() {
309 return this.script_regexp_object_;
313 ScriptBreakPoint.prototype.line = function() {
318 ScriptBreakPoint.prototype.column = function() {
323 ScriptBreakPoint.prototype.actual_locations = function() {
325 for (var i = 0; i < this.break_points_.length; i++) {
326 locations.push(this.break_points_[i].actual_location);
332 ScriptBreakPoint.prototype.update_positions = function(line, column) {
334 this.column_ = column;
338 ScriptBreakPoint.prototype.hit_count = function() {
339 return this.hit_count_;
343 ScriptBreakPoint.prototype.active = function() {
348 ScriptBreakPoint.prototype.condition = function() {
349 return this.condition_;
353 ScriptBreakPoint.prototype.ignoreCount = function() {
354 return this.ignoreCount_;
358 ScriptBreakPoint.prototype.enable = function() {
363 ScriptBreakPoint.prototype.disable = function() {
364 this.active_ = false;
368 ScriptBreakPoint.prototype.setCondition = function(condition) {
369 this.condition_ = condition;
373 ScriptBreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
374 this.ignoreCount_ = ignoreCount;
376 // Set ignore count on all break points created from this script break point.
377 for (var i = 0; i < this.break_points_.length; i++) {
378 this.break_points_[i].setIgnoreCount(ignoreCount);
383 // Check whether a script matches this script break point. Currently this is
384 // only based on script name.
385 ScriptBreakPoint.prototype.matchesScript = function(script) {
386 if (this.type_ == Debug.ScriptBreakPointType.ScriptId) {
387 return this.script_id_ == script.id;
389 // We might want to account columns here as well.
390 if (!(script.line_offset <= this.line_ &&
391 this.line_ < script.line_offset + script.lineCount())) {
394 if (this.type_ == Debug.ScriptBreakPointType.ScriptName) {
395 return this.script_name_ == script.nameOrSourceURL();
396 } else if (this.type_ == Debug.ScriptBreakPointType.ScriptRegExp) {
397 return this.script_regexp_object_.test(script.nameOrSourceURL());
399 throw new Error("Unexpected breakpoint type " + this.type_);
405 // Set the script break point in a script.
406 ScriptBreakPoint.prototype.set = function (script) {
407 var column = this.column();
408 var line = this.line();
409 // If the column is undefined the break is on the line. To help locate the
410 // first piece of breakable code on the line try to find the column on the
411 // line which contains some source.
412 if (IS_UNDEFINED(column)) {
413 var source_line = script.sourceLine(this.line());
415 // Allocate array for caching the columns where the actual source starts.
416 if (!script.sourceColumnStart_) {
417 script.sourceColumnStart_ = new Array(script.lineCount());
420 // Fill cache if needed and get column where the actual source starts.
421 if (IS_UNDEFINED(script.sourceColumnStart_[line])) {
422 script.sourceColumnStart_[line] =
423 source_line.match(sourceLineBeginningSkip)[0].length;
425 column = script.sourceColumnStart_[line];
428 // Convert the line and column into an absolute position within the script.
429 var position = Debug.findScriptSourcePosition(script, this.line(), column);
431 // If the position is not found in the script (the script might be shorter
432 // than it used to be) just ignore it.
433 if (IS_NULL(position)) return;
435 // Create a break point object and set the break point.
436 var break_point = MakeBreakPoint(position, this);
437 break_point.setIgnoreCount(this.ignoreCount());
438 var actual_position = %SetScriptBreakPoint(script, position,
439 this.position_alignment_,
441 if (IS_UNDEFINED(actual_position)) {
442 actual_position = position;
444 var actual_location = script.locationFromPosition(actual_position, true);
445 break_point.actual_location = { line: actual_location.line,
446 column: actual_location.column,
447 script_id: script.id };
448 this.break_points_.push(break_point);
453 // Clear all the break points created from this script break point
454 ScriptBreakPoint.prototype.clear = function () {
455 var remaining_break_points = [];
456 for (var i = 0; i < break_points.length; i++) {
457 if (break_points[i].script_break_point() &&
458 break_points[i].script_break_point() === this) {
459 %ClearBreakPoint(break_points[i]);
461 remaining_break_points.push(break_points[i]);
464 break_points = remaining_break_points;
465 this.break_points_ = [];
469 // Function called from runtime when a new script is compiled to set any script
470 // break points set in this script.
471 function UpdateScriptBreakPoints(script) {
472 for (var i = 0; i < script_break_points.length; i++) {
473 var break_point = script_break_points[i];
474 if ((break_point.type() == Debug.ScriptBreakPointType.ScriptName ||
475 break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) &&
476 break_point.matchesScript(script)) {
477 break_point.set(script);
483 function GetScriptBreakPoints(script) {
485 for (var i = 0; i < script_break_points.length; i++) {
486 if (script_break_points[i].matchesScript(script)) {
487 result.push(script_break_points[i]);
494 Debug.setListener = function(listener, opt_data) {
495 if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) {
496 throw new Error('Parameters have wrong types.');
498 %SetDebugEventListener(listener, opt_data);
502 Debug.breakExecution = function(f) {
506 Debug.breakLocations = function(f, opt_position_aligment) {
507 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
508 var position_aligment = IS_UNDEFINED(opt_position_aligment)
509 ? Debug.BreakPositionAlignment.Statement : opt_position_aligment;
510 return %GetBreakLocations(f, position_aligment);
513 // Returns a Script object. If the parameter is a function the return value
514 // is the script in which the function is defined. If the parameter is a string
515 // the return value is the script for which the script name has that string
516 // value. If it is a regexp and there is a unique script whose name matches
517 // we return that, otherwise undefined.
518 Debug.findScript = function(func_or_script_name) {
519 if (IS_FUNCTION(func_or_script_name)) {
520 return %FunctionGetScript(func_or_script_name);
521 } else if (IS_REGEXP(func_or_script_name)) {
522 var scripts = Debug.scripts();
523 var last_result = null;
524 var result_count = 0;
525 for (var i in scripts) {
526 var script = scripts[i];
527 if (func_or_script_name.test(script.name)) {
528 last_result = script;
532 // Return the unique script matching the regexp. If there are more
533 // than one we don't return a value since there is no good way to
534 // decide which one to return. Returning a "random" one, say the
535 // first, would introduce nondeterminism (or something close to it)
536 // because the order is the heap iteration order.
537 if (result_count == 1) {
543 return %GetScript(func_or_script_name);
547 // Returns the script source. If the parameter is a function the return value
548 // is the script source for the script in which the function is defined. If the
549 // parameter is a string the return value is the script for which the script
550 // name has that string value.
551 Debug.scriptSource = function(func_or_script_name) {
552 return this.findScript(func_or_script_name).source;
555 Debug.source = function(f) {
556 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
557 return %FunctionGetSourceCode(f);
560 Debug.disassemble = function(f) {
561 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
562 return %DebugDisassembleFunction(f);
565 Debug.disassembleConstructor = function(f) {
566 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
567 return %DebugDisassembleConstructor(f);
570 Debug.ExecuteInDebugContext = function(f, without_debugger) {
571 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
572 return %ExecuteInDebugContext(f, !!without_debugger);
575 Debug.sourcePosition = function(f) {
576 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
577 return %FunctionGetScriptSourcePosition(f);
581 Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) {
582 var script = %FunctionGetScript(func);
583 var script_offset = %FunctionGetScriptSourcePosition(func);
584 return script.locationFromLine(opt_line, opt_column, script_offset);
588 // Returns the character position in a script based on a line number and an
589 // optional position within that line.
590 Debug.findScriptSourcePosition = function(script, opt_line, opt_column) {
591 var location = script.locationFromLine(opt_line, opt_column);
592 return location ? location.position : null;
596 Debug.findBreakPoint = function(break_point_number, remove) {
598 for (var i = 0; i < break_points.length; i++) {
599 if (break_points[i].number() == break_point_number) {
600 break_point = break_points[i];
601 // Remove the break point from the list if requested.
603 break_points.splice(i, 1);
611 return this.findScriptBreakPoint(break_point_number, remove);
615 Debug.findBreakPointActualLocations = function(break_point_number) {
616 for (var i = 0; i < script_break_points.length; i++) {
617 if (script_break_points[i].number() == break_point_number) {
618 return script_break_points[i].actual_locations();
621 for (var i = 0; i < break_points.length; i++) {
622 if (break_points[i].number() == break_point_number) {
623 return [break_points[i].actual_location];
629 Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) {
630 if (!IS_FUNCTION(func)) throw new Error('Parameters have wrong types.');
631 // Break points in API functions are not supported.
632 if (%FunctionIsAPIFunction(func)) {
633 throw new Error('Cannot set break point in native code.');
635 // Find source position relative to start of the function
637 this.findFunctionSourceLocation(func, opt_line, opt_column).position;
638 var source_position = break_position - this.sourcePosition(func);
639 // Find the script for the function.
640 var script = %FunctionGetScript(func);
641 // Break in builtin JavaScript code is not supported.
642 if (script.type == Debug.ScriptType.Native) {
643 throw new Error('Cannot set break point in native code.');
645 // If the script for the function has a name convert this to a script break
647 if (script && script.id) {
648 // Adjust the source position to be script relative.
649 source_position += %FunctionGetScriptSourcePosition(func);
650 // Find line and column for the position in the script and set a script
651 // break point from that.
652 var location = script.locationFromPosition(source_position, false);
653 return this.setScriptBreakPointById(script.id,
654 location.line, location.column,
657 // Set a break point directly on the function.
658 var break_point = MakeBreakPoint(source_position);
659 var actual_position =
660 %SetFunctionBreakPoint(func, source_position, break_point);
661 actual_position += this.sourcePosition(func);
662 var actual_location = script.locationFromPosition(actual_position, true);
663 break_point.actual_location = { line: actual_location.line,
664 column: actual_location.column,
665 script_id: script.id };
666 break_point.setCondition(opt_condition);
667 return break_point.number();
672 Debug.setBreakPointByScriptIdAndPosition = function(script_id, position,
674 opt_position_alignment)
676 var break_point = MakeBreakPoint(position);
677 break_point.setCondition(condition);
679 break_point.disable();
681 var scripts = this.scripts();
682 var position_alignment = IS_UNDEFINED(opt_position_alignment)
683 ? Debug.BreakPositionAlignment.Statement : opt_position_alignment;
684 for (var i = 0; i < scripts.length; i++) {
685 if (script_id == scripts[i].id) {
686 break_point.actual_position = %SetScriptBreakPoint(scripts[i], position,
687 position_alignment, break_point);
695 Debug.enableBreakPoint = function(break_point_number) {
696 var break_point = this.findBreakPoint(break_point_number, false);
697 // Only enable if the breakpoint hasn't been deleted:
699 break_point.enable();
704 Debug.disableBreakPoint = function(break_point_number) {
705 var break_point = this.findBreakPoint(break_point_number, false);
706 // Only enable if the breakpoint hasn't been deleted:
708 break_point.disable();
713 Debug.changeBreakPointCondition = function(break_point_number, condition) {
714 var break_point = this.findBreakPoint(break_point_number, false);
715 break_point.setCondition(condition);
719 Debug.changeBreakPointIgnoreCount = function(break_point_number, ignoreCount) {
720 if (ignoreCount < 0) {
721 throw new Error('Invalid argument');
723 var break_point = this.findBreakPoint(break_point_number, false);
724 break_point.setIgnoreCount(ignoreCount);
728 Debug.clearBreakPoint = function(break_point_number) {
729 var break_point = this.findBreakPoint(break_point_number, true);
731 return %ClearBreakPoint(break_point);
733 break_point = this.findScriptBreakPoint(break_point_number, true);
735 throw new Error('Invalid breakpoint');
741 Debug.clearAllBreakPoints = function() {
742 for (var i = 0; i < break_points.length; i++) {
743 var break_point = break_points[i];
744 %ClearBreakPoint(break_point);
750 Debug.disableAllBreakPoints = function() {
751 // Disable all user defined breakpoints:
752 for (var i = 1; i < next_break_point_number; i++) {
753 Debug.disableBreakPoint(i);
755 // Disable all exception breakpoints:
756 %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
757 %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
761 Debug.findScriptBreakPoint = function(break_point_number, remove) {
762 var script_break_point;
763 for (var i = 0; i < script_break_points.length; i++) {
764 if (script_break_points[i].number() == break_point_number) {
765 script_break_point = script_break_points[i];
766 // Remove the break point from the list if requested.
768 script_break_point.clear();
769 script_break_points.splice(i,1);
774 return script_break_point;
778 // Sets a breakpoint in a script identified through id or name at the
779 // specified source line and column within that line.
780 Debug.setScriptBreakPoint = function(type, script_id_or_name,
781 opt_line, opt_column, opt_condition,
782 opt_groupId, opt_position_alignment) {
783 // Create script break point object.
784 var script_break_point =
785 new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
786 opt_groupId, opt_position_alignment);
788 // Assign number to the new script break point and add it.
789 script_break_point.number_ = next_break_point_number++;
790 script_break_point.setCondition(opt_condition);
791 script_break_points.push(script_break_point);
793 // Run through all scripts to see if this script break point matches any
795 var scripts = this.scripts();
796 for (var i = 0; i < scripts.length; i++) {
797 if (script_break_point.matchesScript(scripts[i])) {
798 script_break_point.set(scripts[i]);
802 return script_break_point.number();
806 Debug.setScriptBreakPointById = function(script_id,
807 opt_line, opt_column,
808 opt_condition, opt_groupId,
809 opt_position_alignment) {
810 return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
811 script_id, opt_line, opt_column,
812 opt_condition, opt_groupId,
813 opt_position_alignment);
817 Debug.setScriptBreakPointByName = function(script_name,
818 opt_line, opt_column,
819 opt_condition, opt_groupId) {
820 return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName,
821 script_name, opt_line, opt_column,
822 opt_condition, opt_groupId);
826 Debug.setScriptBreakPointByRegExp = function(script_regexp,
827 opt_line, opt_column,
828 opt_condition, opt_groupId) {
829 return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptRegExp,
830 script_regexp, opt_line, opt_column,
831 opt_condition, opt_groupId);
835 Debug.enableScriptBreakPoint = function(break_point_number) {
836 var script_break_point = this.findScriptBreakPoint(break_point_number, false);
837 script_break_point.enable();
841 Debug.disableScriptBreakPoint = function(break_point_number) {
842 var script_break_point = this.findScriptBreakPoint(break_point_number, false);
843 script_break_point.disable();
847 Debug.changeScriptBreakPointCondition = function(
848 break_point_number, condition) {
849 var script_break_point = this.findScriptBreakPoint(break_point_number, false);
850 script_break_point.setCondition(condition);
854 Debug.changeScriptBreakPointIgnoreCount = function(
855 break_point_number, ignoreCount) {
856 if (ignoreCount < 0) {
857 throw new Error('Invalid argument');
859 var script_break_point = this.findScriptBreakPoint(break_point_number, false);
860 script_break_point.setIgnoreCount(ignoreCount);
864 Debug.scriptBreakPoints = function() {
865 return script_break_points;
869 Debug.clearStepping = function() {
873 Debug.setBreakOnException = function() {
874 return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, true);
877 Debug.clearBreakOnException = function() {
878 return %ChangeBreakOnException(Debug.ExceptionBreak.Caught, false);
881 Debug.isBreakOnException = function() {
882 return !!%IsBreakOnException(Debug.ExceptionBreak.Caught);
885 Debug.setBreakOnUncaughtException = function() {
886 return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true);
889 Debug.clearBreakOnUncaughtException = function() {
890 return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
893 Debug.isBreakOnUncaughtException = function() {
894 return !!%IsBreakOnException(Debug.ExceptionBreak.Uncaught);
897 Debug.showBreakPoints = function(f, full, opt_position_alignment) {
898 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
899 var source = full ? this.scriptSource(f) : this.source(f);
900 var offset = full ? this.sourcePosition(f) : 0;
901 var locations = this.breakLocations(f, opt_position_alignment);
902 if (!locations) return source;
903 locations.sort(function(x, y) { return x - y; });
907 for (var i = 0; i < locations.length; i++) {
908 pos = locations[i] - offset;
909 result += source.slice(prev_pos, pos);
910 result += "[B" + i + "]";
914 result += source.substring(prev_pos, pos);
919 // Get all the scripts currently loaded. Locating all the scripts is based on
920 // scanning the heap.
921 Debug.scripts = function() {
922 // Collect all scripts in the heap.
923 return %DebugGetLoadedScripts();
927 Debug.debuggerFlags = function() {
928 return debugger_flags;
931 Debug.MakeMirror = MakeMirror;
933 function MakeExecutionState(break_id) {
934 return new ExecutionState(break_id);
937 function ExecutionState(break_id) {
938 this.break_id = break_id;
939 this.selected_frame = 0;
942 ExecutionState.prototype.prepareStep = function(opt_action, opt_count,
944 var action = Debug.StepAction.StepIn;
945 if (!IS_UNDEFINED(opt_action)) action = %ToNumber(opt_action);
946 var count = opt_count ? %ToNumber(opt_count) : 1;
948 if (!IS_UNDEFINED(opt_callframe)) {
949 callFrameId = opt_callframe.details_.frameId();
952 return %PrepareStep(this.break_id, action, count, callFrameId);
955 ExecutionState.prototype.evaluateGlobal = function(source, disable_break,
956 opt_additional_context) {
957 return MakeMirror(%DebugEvaluateGlobal(this.break_id, source,
958 Boolean(disable_break),
959 opt_additional_context));
962 ExecutionState.prototype.frameCount = function() {
963 return %GetFrameCount(this.break_id);
966 ExecutionState.prototype.threadCount = function() {
967 return %GetThreadCount(this.break_id);
970 ExecutionState.prototype.frame = function(opt_index) {
971 // If no index supplied return the selected frame.
972 if (opt_index == null) opt_index = this.selected_frame;
973 if (opt_index < 0 || opt_index >= this.frameCount()) {
974 throw new Error('Illegal frame index.');
976 return new FrameMirror(this.break_id, opt_index);
979 ExecutionState.prototype.setSelectedFrame = function(index) {
980 var i = %ToNumber(index);
981 if (i < 0 || i >= this.frameCount()) throw new Error('Illegal frame index.');
982 this.selected_frame = i;
985 ExecutionState.prototype.selectedFrame = function() {
986 return this.selected_frame;
989 ExecutionState.prototype.debugCommandProcessor = function(opt_is_running) {
990 return new DebugCommandProcessor(this, opt_is_running);
994 function MakeBreakEvent(break_id, break_points_hit) {
995 return new BreakEvent(break_id, break_points_hit);
999 function BreakEvent(break_id, break_points_hit) {
1000 this.frame_ = new FrameMirror(break_id, 0);
1001 this.break_points_hit_ = break_points_hit;
1005 BreakEvent.prototype.eventType = function() {
1006 return Debug.DebugEvent.Break;
1010 BreakEvent.prototype.func = function() {
1011 return this.frame_.func();
1015 BreakEvent.prototype.sourceLine = function() {
1016 return this.frame_.sourceLine();
1020 BreakEvent.prototype.sourceColumn = function() {
1021 return this.frame_.sourceColumn();
1025 BreakEvent.prototype.sourceLineText = function() {
1026 return this.frame_.sourceLineText();
1030 BreakEvent.prototype.breakPointsHit = function() {
1031 return this.break_points_hit_;
1035 BreakEvent.prototype.toJSONProtocol = function() {
1036 var o = { seq: next_response_seq++,
1039 body: { invocationText: this.frame_.invocationText() }
1042 // Add script related information to the event if available.
1043 var script = this.func().script();
1045 o.body.sourceLine = this.sourceLine(),
1046 o.body.sourceColumn = this.sourceColumn(),
1047 o.body.sourceLineText = this.sourceLineText(),
1048 o.body.script = MakeScriptObject_(script, false);
1051 // Add an Array of break points hit if any.
1052 if (this.breakPointsHit()) {
1053 o.body.breakpoints = [];
1054 for (var i = 0; i < this.breakPointsHit().length; i++) {
1055 // Find the break point number. For break points originating from a
1056 // script break point supply the script break point number.
1057 var breakpoint = this.breakPointsHit()[i];
1058 var script_break_point = breakpoint.script_break_point();
1060 if (script_break_point) {
1061 number = script_break_point.number();
1063 number = breakpoint.number();
1065 o.body.breakpoints.push(number);
1068 return JSON.stringify(ObjectToProtocolObject_(o));
1072 function MakeExceptionEvent(break_id, exception, uncaught, promise) {
1073 return new ExceptionEvent(break_id, exception, uncaught, promise);
1077 function ExceptionEvent(break_id, exception, uncaught, promise) {
1078 this.exec_state_ = new ExecutionState(break_id);
1079 this.exception_ = exception;
1080 this.uncaught_ = uncaught;
1081 this.promise_ = promise;
1085 ExceptionEvent.prototype.eventType = function() {
1086 return Debug.DebugEvent.Exception;
1090 ExceptionEvent.prototype.exception = function() {
1091 return this.exception_;
1095 ExceptionEvent.prototype.uncaught = function() {
1096 return this.uncaught_;
1100 ExceptionEvent.prototype.promise = function() {
1101 return this.promise_;
1105 ExceptionEvent.prototype.func = function() {
1106 return this.exec_state_.frame(0).func();
1110 ExceptionEvent.prototype.sourceLine = function() {
1111 return this.exec_state_.frame(0).sourceLine();
1115 ExceptionEvent.prototype.sourceColumn = function() {
1116 return this.exec_state_.frame(0).sourceColumn();
1120 ExceptionEvent.prototype.sourceLineText = function() {
1121 return this.exec_state_.frame(0).sourceLineText();
1125 ExceptionEvent.prototype.toJSONProtocol = function() {
1126 var o = new ProtocolMessage();
1127 o.event = "exception";
1128 o.body = { uncaught: this.uncaught_,
1129 exception: MakeMirror(this.exception_)
1132 // Exceptions might happen whithout any JavaScript frames.
1133 if (this.exec_state_.frameCount() > 0) {
1134 o.body.sourceLine = this.sourceLine();
1135 o.body.sourceColumn = this.sourceColumn();
1136 o.body.sourceLineText = this.sourceLineText();
1138 // Add script information to the event if available.
1139 var script = this.func().script();
1141 o.body.script = MakeScriptObject_(script, false);
1144 o.body.sourceLine = -1;
1147 return o.toJSONProtocol();
1151 function MakeCompileEvent(script, type) {
1152 return new CompileEvent(script, type);
1156 function CompileEvent(script, type) {
1157 this.script_ = MakeMirror(script);
1162 CompileEvent.prototype.eventType = function() {
1167 CompileEvent.prototype.script = function() {
1168 return this.script_;
1172 CompileEvent.prototype.toJSONProtocol = function() {
1173 var o = new ProtocolMessage();
1175 switch (this.type_) {
1176 case Debug.DebugEvent.BeforeCompile:
1177 o.event = "beforeCompile";
1179 case Debug.DebugEvent.AfterCompile:
1180 o.event = "afterCompile";
1182 case Debug.DebugEvent.CompileError:
1183 o.event = "compileError";
1187 o.body.script = this.script_;
1189 return o.toJSONProtocol();
1193 function MakeScriptObject_(script, include_source) {
1194 var o = { id: script.id(),
1195 name: script.name(),
1196 lineOffset: script.lineOffset(),
1197 columnOffset: script.columnOffset(),
1198 lineCount: script.lineCount(),
1200 if (!IS_UNDEFINED(script.data())) {
1201 o.data = script.data();
1203 if (include_source) {
1204 o.source = script.source();
1210 function MakePromiseEvent(event_data) {
1211 return new PromiseEvent(event_data);
1215 function PromiseEvent(event_data) {
1216 this.promise_ = event_data.promise;
1217 this.parentPromise_ = event_data.parentPromise;
1218 this.status_ = event_data.status;
1219 this.value_ = event_data.value;
1223 PromiseEvent.prototype.promise = function() {
1224 return MakeMirror(this.promise_);
1228 PromiseEvent.prototype.parentPromise = function() {
1229 return MakeMirror(this.parentPromise_);
1233 PromiseEvent.prototype.status = function() {
1234 return this.status_;
1238 PromiseEvent.prototype.value = function() {
1239 return MakeMirror(this.value_);
1243 function MakeAsyncTaskEvent(event_data) {
1244 return new AsyncTaskEvent(event_data);
1248 function AsyncTaskEvent(event_data) {
1249 this.type_ = event_data.type;
1250 this.name_ = event_data.name;
1251 this.id_ = event_data.id;
1255 AsyncTaskEvent.prototype.type = function() {
1260 AsyncTaskEvent.prototype.name = function() {
1265 AsyncTaskEvent.prototype.id = function() {
1270 function DebugCommandProcessor(exec_state, opt_is_running) {
1271 this.exec_state_ = exec_state;
1272 this.running_ = opt_is_running || false;
1276 DebugCommandProcessor.prototype.processDebugRequest = function (request) {
1277 return this.processDebugJSONRequest(request);
1281 function ProtocolMessage(request) {
1282 // Update sequence number.
1283 this.seq = next_response_seq++;
1286 // If message is based on a request this is a response. Fill the initial
1287 // response from the request.
1288 this.type = 'response';
1289 this.request_seq = request.seq;
1290 this.command = request.command;
1292 // If message is not based on a request it is a dabugger generated event.
1293 this.type = 'event';
1295 this.success = true;
1296 // Handler may set this field to control debugger state.
1297 this.running = undefined;
1301 ProtocolMessage.prototype.setOption = function(name, value) {
1302 if (!this.options_) {
1305 this.options_[name] = value;
1309 ProtocolMessage.prototype.failed = function(message, opt_details) {
1310 this.success = false;
1311 this.message = message;
1312 if (IS_OBJECT(opt_details)) {
1313 this.error_details = opt_details;
1318 ProtocolMessage.prototype.toJSONProtocol = function() {
1319 // Encode the protocol header.
1322 if (this.request_seq) {
1323 json.request_seq = this.request_seq;
1325 json.type = this.type;
1327 json.event = this.event;
1330 json.command = this.command;
1333 json.success = this.success;
1335 json.success = false;
1338 // Encode the body part.
1340 var serializer = MakeMirrorSerializer(true, this.options_);
1341 if (this.body instanceof Mirror) {
1342 bodyJson = serializer.serializeValue(this.body);
1343 } else if (this.body instanceof Array) {
1345 for (var i = 0; i < this.body.length; i++) {
1346 if (this.body[i] instanceof Mirror) {
1347 bodyJson.push(serializer.serializeValue(this.body[i]));
1349 bodyJson.push(ObjectToProtocolObject_(this.body[i], serializer));
1353 bodyJson = ObjectToProtocolObject_(this.body, serializer);
1355 json.body = bodyJson;
1356 json.refs = serializer.serializeReferencedObjects();
1359 json.message = this.message;
1361 if (this.error_details) {
1362 json.error_details = this.error_details;
1364 json.running = this.running;
1365 return JSON.stringify(json);
1369 DebugCommandProcessor.prototype.createResponse = function(request) {
1370 return new ProtocolMessage(request);
1374 DebugCommandProcessor.prototype.processDebugJSONRequest = function(
1376 var request; // Current request.
1377 var response; // Generated response.
1380 // Convert the JSON string to an object.
1381 request = JSON.parse(json_request);
1383 // Create an initial response.
1384 response = this.createResponse(request);
1386 if (!request.type) {
1387 throw new Error('Type not specified');
1390 if (request.type != 'request') {
1391 throw new Error("Illegal type '" + request.type + "' in request");
1394 if (!request.command) {
1395 throw new Error('Command not specified');
1398 if (request.arguments) {
1399 var args = request.arguments;
1400 // TODO(yurys): remove request.arguments.compactFormat check once
1401 // ChromeDevTools are switched to 'inlineRefs'
1402 if (args.inlineRefs || args.compactFormat) {
1403 response.setOption('inlineRefs', true);
1405 if (!IS_UNDEFINED(args.maxStringLength)) {
1406 response.setOption('maxStringLength', args.maxStringLength);
1410 var key = request.command.toLowerCase();
1411 var handler = DebugCommandProcessor.prototype.dispatch_[key];
1412 if (IS_FUNCTION(handler)) {
1413 %_CallFunction(this, request, response, handler);
1415 throw new Error('Unknown command "' + request.command + '" in request');
1418 // If there is no response object created one (without command).
1420 response = this.createResponse();
1422 response.success = false;
1423 response.message = %ToString(e);
1426 // Return the response as a JSON encoded string.
1428 if (!IS_UNDEFINED(response.running)) {
1429 // Response controls running state.
1430 this.running_ = response.running;
1432 response.running = this.running_;
1433 return response.toJSONProtocol();
1435 // Failed to generate response - return generic error.
1436 return '{"seq":' + response.seq + ',' +
1437 '"request_seq":' + request.seq + ',' +
1438 '"type":"response",' +
1439 '"success":false,' +
1440 '"message":"Internal error: ' + %ToString(e) + '"}';
1443 // Failed in one of the catch blocks above - most generic error.
1444 return '{"seq":0,"type":"response","success":false,"message":"Internal error"}';
1449 DebugCommandProcessor.prototype.continueRequest_ = function(request, response) {
1450 // Check for arguments for continue.
1451 if (request.arguments) {
1453 var action = Debug.StepAction.StepIn;
1455 // Pull out arguments.
1456 var stepaction = request.arguments.stepaction;
1457 var stepcount = request.arguments.stepcount;
1459 // Get the stepcount argument if any.
1461 count = %ToNumber(stepcount);
1463 throw new Error('Invalid stepcount argument "' + stepcount + '".');
1467 // Get the stepaction argument.
1469 if (stepaction == 'in') {
1470 action = Debug.StepAction.StepIn;
1471 } else if (stepaction == 'min') {
1472 action = Debug.StepAction.StepMin;
1473 } else if (stepaction == 'next') {
1474 action = Debug.StepAction.StepNext;
1475 } else if (stepaction == 'out') {
1476 action = Debug.StepAction.StepOut;
1478 throw new Error('Invalid stepaction argument "' + stepaction + '".');
1482 // Set up the VM for stepping.
1483 this.exec_state_.prepareStep(action, count);
1486 // VM should be running after executing this request.
1487 response.running = true;
1491 DebugCommandProcessor.prototype.breakRequest_ = function(request, response) {
1492 // Ignore as break command does not do anything when broken.
1496 DebugCommandProcessor.prototype.setBreakPointRequest_ =
1497 function(request, response) {
1498 // Check for legal request.
1499 if (!request.arguments) {
1500 response.failed('Missing arguments');
1504 // Pull out arguments.
1505 var type = request.arguments.type;
1506 var target = request.arguments.target;
1507 var line = request.arguments.line;
1508 var column = request.arguments.column;
1509 var enabled = IS_UNDEFINED(request.arguments.enabled) ?
1510 true : request.arguments.enabled;
1511 var condition = request.arguments.condition;
1512 var ignoreCount = request.arguments.ignoreCount;
1513 var groupId = request.arguments.groupId;
1515 // Check for legal arguments.
1516 if (!type || IS_UNDEFINED(target)) {
1517 response.failed('Missing argument "type" or "target"');
1521 // Either function or script break point.
1522 var break_point_number;
1523 if (type == 'function') {
1524 // Handle function break point.
1525 if (!IS_STRING(target)) {
1526 response.failed('Argument "target" is not a string value');
1531 // Find the function through a global evaluate.
1532 f = this.exec_state_.evaluateGlobal(target).value();
1534 response.failed('Error: "' + %ToString(e) +
1535 '" evaluating "' + target + '"');
1538 if (!IS_FUNCTION(f)) {
1539 response.failed('"' + target + '" does not evaluate to a function');
1543 // Set function break point.
1544 break_point_number = Debug.setBreakPoint(f, line, column, condition);
1545 } else if (type == 'handle') {
1546 // Find the object pointed by the specified handle.
1547 var handle = parseInt(target, 10);
1548 var mirror = LookupMirror(handle);
1550 return response.failed('Object #' + handle + '# not found');
1552 if (!mirror.isFunction()) {
1553 return response.failed('Object #' + handle + '# is not a function');
1556 // Set function break point.
1557 break_point_number = Debug.setBreakPoint(mirror.value(),
1558 line, column, condition);
1559 } else if (type == 'script') {
1560 // set script break point.
1561 break_point_number =
1562 Debug.setScriptBreakPointByName(target, line, column, condition,
1564 } else if (type == 'scriptId') {
1565 break_point_number =
1566 Debug.setScriptBreakPointById(target, line, column, condition, groupId);
1567 } else if (type == 'scriptRegExp') {
1568 break_point_number =
1569 Debug.setScriptBreakPointByRegExp(target, line, column, condition,
1572 response.failed('Illegal type "' + type + '"');
1576 // Set additional break point properties.
1577 var break_point = Debug.findBreakPoint(break_point_number);
1579 Debug.changeBreakPointIgnoreCount(break_point_number, ignoreCount);
1582 Debug.disableBreakPoint(break_point_number);
1585 // Add the break point number to the response.
1586 response.body = { type: type,
1587 breakpoint: break_point_number };
1589 // Add break point information to the response.
1590 if (break_point instanceof ScriptBreakPoint) {
1591 if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
1592 response.body.type = 'scriptId';
1593 response.body.script_id = break_point.script_id();
1594 } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptName) {
1595 response.body.type = 'scriptName';
1596 response.body.script_name = break_point.script_name();
1597 } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) {
1598 response.body.type = 'scriptRegExp';
1599 response.body.script_regexp = break_point.script_regexp_object().source;
1601 throw new Error("Internal error: Unexpected breakpoint type: " +
1602 break_point.type());
1604 response.body.line = break_point.line();
1605 response.body.column = break_point.column();
1606 response.body.actual_locations = break_point.actual_locations();
1608 response.body.type = 'function';
1609 response.body.actual_locations = [break_point.actual_location];
1614 DebugCommandProcessor.prototype.changeBreakPointRequest_ = function(
1615 request, response) {
1616 // Check for legal request.
1617 if (!request.arguments) {
1618 response.failed('Missing arguments');
1622 // Pull out arguments.
1623 var break_point = %ToNumber(request.arguments.breakpoint);
1624 var enabled = request.arguments.enabled;
1625 var condition = request.arguments.condition;
1626 var ignoreCount = request.arguments.ignoreCount;
1628 // Check for legal arguments.
1630 response.failed('Missing argument "breakpoint"');
1634 // Change enabled state if supplied.
1635 if (!IS_UNDEFINED(enabled)) {
1637 Debug.enableBreakPoint(break_point);
1639 Debug.disableBreakPoint(break_point);
1643 // Change condition if supplied
1644 if (!IS_UNDEFINED(condition)) {
1645 Debug.changeBreakPointCondition(break_point, condition);
1648 // Change ignore count if supplied
1649 if (!IS_UNDEFINED(ignoreCount)) {
1650 Debug.changeBreakPointIgnoreCount(break_point, ignoreCount);
1655 DebugCommandProcessor.prototype.clearBreakPointGroupRequest_ = function(
1656 request, response) {
1657 // Check for legal request.
1658 if (!request.arguments) {
1659 response.failed('Missing arguments');
1663 // Pull out arguments.
1664 var group_id = request.arguments.groupId;
1666 // Check for legal arguments.
1668 response.failed('Missing argument "groupId"');
1672 var cleared_break_points = [];
1673 var new_script_break_points = [];
1674 for (var i = 0; i < script_break_points.length; i++) {
1675 var next_break_point = script_break_points[i];
1676 if (next_break_point.groupId() == group_id) {
1677 cleared_break_points.push(next_break_point.number());
1678 next_break_point.clear();
1680 new_script_break_points.push(next_break_point);
1683 script_break_points = new_script_break_points;
1685 // Add the cleared break point numbers to the response.
1686 response.body = { breakpoints: cleared_break_points };
1690 DebugCommandProcessor.prototype.clearBreakPointRequest_ = function(
1691 request, response) {
1692 // Check for legal request.
1693 if (!request.arguments) {
1694 response.failed('Missing arguments');
1698 // Pull out arguments.
1699 var break_point = %ToNumber(request.arguments.breakpoint);
1701 // Check for legal arguments.
1703 response.failed('Missing argument "breakpoint"');
1707 // Clear break point.
1708 Debug.clearBreakPoint(break_point);
1710 // Add the cleared break point number to the response.
1711 response.body = { breakpoint: break_point };
1715 DebugCommandProcessor.prototype.listBreakpointsRequest_ = function(
1716 request, response) {
1718 for (var i = 0; i < script_break_points.length; i++) {
1719 var break_point = script_break_points[i];
1722 number: break_point.number(),
1723 line: break_point.line(),
1724 column: break_point.column(),
1725 groupId: break_point.groupId(),
1726 hit_count: break_point.hit_count(),
1727 active: break_point.active(),
1728 condition: break_point.condition(),
1729 ignoreCount: break_point.ignoreCount(),
1730 actual_locations: break_point.actual_locations()
1733 if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
1734 description.type = 'scriptId';
1735 description.script_id = break_point.script_id();
1736 } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptName) {
1737 description.type = 'scriptName';
1738 description.script_name = break_point.script_name();
1739 } else if (break_point.type() == Debug.ScriptBreakPointType.ScriptRegExp) {
1740 description.type = 'scriptRegExp';
1741 description.script_regexp = break_point.script_regexp_object().source;
1743 throw new Error("Internal error: Unexpected breakpoint type: " +
1744 break_point.type());
1746 array.push(description);
1751 breakOnExceptions: Debug.isBreakOnException(),
1752 breakOnUncaughtExceptions: Debug.isBreakOnUncaughtException()
1757 DebugCommandProcessor.prototype.disconnectRequest_ =
1758 function(request, response) {
1759 Debug.disableAllBreakPoints();
1760 this.continueRequest_(request, response);
1764 DebugCommandProcessor.prototype.setExceptionBreakRequest_ =
1765 function(request, response) {
1766 // Check for legal request.
1767 if (!request.arguments) {
1768 response.failed('Missing arguments');
1772 // Pull out and check the 'type' argument:
1773 var type = request.arguments.type;
1775 response.failed('Missing argument "type"');
1779 // Initialize the default value of enable:
1781 if (type == 'all') {
1782 enabled = !Debug.isBreakOnException();
1783 } else if (type == 'uncaught') {
1784 enabled = !Debug.isBreakOnUncaughtException();
1787 // Pull out and check the 'enabled' argument if present:
1788 if (!IS_UNDEFINED(request.arguments.enabled)) {
1789 enabled = request.arguments.enabled;
1790 if ((enabled != true) && (enabled != false)) {
1791 response.failed('Illegal value for "enabled":"' + enabled + '"');
1795 // Now set the exception break state:
1796 if (type == 'all') {
1797 %ChangeBreakOnException(Debug.ExceptionBreak.Caught, enabled);
1798 } else if (type == 'uncaught') {
1799 %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, enabled);
1801 response.failed('Unknown "type":"' + type + '"');
1804 // Add the cleared break point number to the response.
1805 response.body = { 'type': type, 'enabled': enabled };
1809 DebugCommandProcessor.prototype.backtraceRequest_ = function(
1810 request, response) {
1811 // Get the number of frames.
1812 var total_frames = this.exec_state_.frameCount();
1814 // Create simple response if there are no frames.
1815 if (total_frames == 0) {
1817 totalFrames: total_frames
1822 // Default frame range to include in backtrace.
1824 var to_index = kDefaultBacktraceLength;
1826 // Get the range from the arguments.
1827 if (request.arguments) {
1828 if (request.arguments.fromFrame) {
1829 from_index = request.arguments.fromFrame;
1831 if (request.arguments.toFrame) {
1832 to_index = request.arguments.toFrame;
1834 if (request.arguments.bottom) {
1835 var tmp_index = total_frames - from_index;
1836 from_index = total_frames - to_index;
1837 to_index = tmp_index;
1839 if (from_index < 0 || to_index < 0) {
1840 return response.failed('Invalid frame number');
1844 // Adjust the index.
1845 to_index = Math.min(total_frames, to_index);
1847 if (to_index <= from_index) {
1848 var error = 'Invalid frame range';
1849 return response.failed(error);
1852 // Create the response body.
1854 for (var i = from_index; i < to_index; i++) {
1855 frames.push(this.exec_state_.frame(i));
1858 fromFrame: from_index,
1860 totalFrames: total_frames,
1866 DebugCommandProcessor.prototype.frameRequest_ = function(request, response) {
1867 // No frames no source.
1868 if (this.exec_state_.frameCount() == 0) {
1869 return response.failed('No frames');
1872 // With no arguments just keep the selected frame.
1873 if (request.arguments) {
1874 var index = request.arguments.number;
1875 if (index < 0 || this.exec_state_.frameCount() <= index) {
1876 return response.failed('Invalid frame number');
1879 this.exec_state_.setSelectedFrame(request.arguments.number);
1881 response.body = this.exec_state_.frame();
1885 DebugCommandProcessor.prototype.resolveFrameFromScopeDescription_ =
1886 function(scope_description) {
1887 // Get the frame for which the scope or scopes are requested.
1888 // With no frameNumber argument use the currently selected frame.
1889 if (scope_description && !IS_UNDEFINED(scope_description.frameNumber)) {
1890 var frame_index = scope_description.frameNumber;
1891 if (frame_index < 0 || this.exec_state_.frameCount() <= frame_index) {
1892 throw new Error('Invalid frame number');
1894 return this.exec_state_.frame(frame_index);
1896 return this.exec_state_.frame();
1901 // Gets scope host object from request. It is either a function
1902 // ('functionHandle' argument must be specified) or a stack frame
1903 // ('frameNumber' may be specified and the current frame is taken by default).
1904 DebugCommandProcessor.prototype.resolveScopeHolder_ =
1905 function(scope_description) {
1906 if (scope_description && "functionHandle" in scope_description) {
1907 if (!IS_NUMBER(scope_description.functionHandle)) {
1908 throw new Error('Function handle must be a number');
1910 var function_mirror = LookupMirror(scope_description.functionHandle);
1911 if (!function_mirror) {
1912 throw new Error('Failed to find function object by handle');
1914 if (!function_mirror.isFunction()) {
1915 throw new Error('Value of non-function type is found by handle');
1917 return function_mirror;
1919 // No frames no scopes.
1920 if (this.exec_state_.frameCount() == 0) {
1921 throw new Error('No scopes');
1924 // Get the frame for which the scopes are requested.
1925 var frame = this.resolveFrameFromScopeDescription_(scope_description);
1931 DebugCommandProcessor.prototype.scopesRequest_ = function(request, response) {
1932 var scope_holder = this.resolveScopeHolder_(request.arguments);
1934 // Fill all scopes for this frame or function.
1935 var total_scopes = scope_holder.scopeCount();
1937 for (var i = 0; i < total_scopes; i++) {
1938 scopes.push(scope_holder.scope(i));
1942 toScope: total_scopes,
1943 totalScopes: total_scopes,
1949 DebugCommandProcessor.prototype.scopeRequest_ = function(request, response) {
1950 // Get the frame or function for which the scope is requested.
1951 var scope_holder = this.resolveScopeHolder_(request.arguments);
1953 // With no scope argument just return top scope.
1954 var scope_index = 0;
1955 if (request.arguments && !IS_UNDEFINED(request.arguments.number)) {
1956 scope_index = %ToNumber(request.arguments.number);
1957 if (scope_index < 0 || scope_holder.scopeCount() <= scope_index) {
1958 return response.failed('Invalid scope number');
1962 response.body = scope_holder.scope(scope_index);
1966 // Reads value from protocol description. Description may be in form of type
1967 // (for singletons), raw value (primitive types supported in JSON),
1968 // string value description plus type (for primitive values) or handle id.
1969 // Returns raw value or throws exception.
1970 DebugCommandProcessor.resolveValue_ = function(value_description) {
1971 if ("handle" in value_description) {
1972 var value_mirror = LookupMirror(value_description.handle);
1973 if (!value_mirror) {
1974 throw new Error("Failed to resolve value by handle, ' #" +
1975 value_description.handle + "# not found");
1977 return value_mirror.value();
1978 } else if ("stringDescription" in value_description) {
1979 if (value_description.type == BOOLEAN_TYPE) {
1980 return Boolean(value_description.stringDescription);
1981 } else if (value_description.type == NUMBER_TYPE) {
1982 return Number(value_description.stringDescription);
1983 } if (value_description.type == STRING_TYPE) {
1984 return String(value_description.stringDescription);
1986 throw new Error("Unknown type");
1988 } else if ("value" in value_description) {
1989 return value_description.value;
1990 } else if (value_description.type == UNDEFINED_TYPE) {
1992 } else if (value_description.type == NULL_TYPE) {
1995 throw new Error("Failed to parse value description");
2000 DebugCommandProcessor.prototype.setVariableValueRequest_ =
2001 function(request, response) {
2002 if (!request.arguments) {
2003 response.failed('Missing arguments');
2007 if (IS_UNDEFINED(request.arguments.name)) {
2008 response.failed('Missing variable name');
2010 var variable_name = request.arguments.name;
2012 var scope_description = request.arguments.scope;
2014 // Get the frame or function for which the scope is requested.
2015 var scope_holder = this.resolveScopeHolder_(scope_description);
2017 if (IS_UNDEFINED(scope_description.number)) {
2018 response.failed('Missing scope number');
2020 var scope_index = %ToNumber(scope_description.number);
2022 var scope = scope_holder.scope(scope_index);
2025 DebugCommandProcessor.resolveValue_(request.arguments.newValue);
2027 scope.setVariableValue(variable_name, new_value);
2029 var new_value_mirror = MakeMirror(new_value);
2032 newValue: new_value_mirror
2037 DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) {
2038 if (!request.arguments) {
2039 return response.failed('Missing arguments');
2042 // Pull out arguments.
2043 var expression = request.arguments.expression;
2044 var frame = request.arguments.frame;
2045 var global = request.arguments.global;
2046 var disable_break = request.arguments.disable_break;
2047 var additional_context = request.arguments.additional_context;
2049 // The expression argument could be an integer so we convert it to a
2052 expression = String(expression);
2054 return response.failed('Failed to convert expression argument to string');
2057 // Check for legal arguments.
2058 if (!IS_UNDEFINED(frame) && global) {
2059 return response.failed('Arguments "frame" and "global" are exclusive');
2062 var additional_context_object;
2063 if (additional_context) {
2064 additional_context_object = {};
2065 for (var i = 0; i < additional_context.length; i++) {
2066 var mapping = additional_context[i];
2068 if (!IS_STRING(mapping.name)) {
2069 return response.failed("Context element #" + i +
2070 " doesn't contain name:string property");
2073 var raw_value = DebugCommandProcessor.resolveValue_(mapping);
2074 additional_context_object[mapping.name] = raw_value;
2080 // Evaluate in the native context.
2081 response.body = this.exec_state_.evaluateGlobal(
2082 expression, Boolean(disable_break), additional_context_object);
2086 // Default value for disable_break is true.
2087 if (IS_UNDEFINED(disable_break)) {
2088 disable_break = true;
2091 // No frames no evaluate in frame.
2092 if (this.exec_state_.frameCount() == 0) {
2093 return response.failed('No frames');
2096 // Check whether a frame was specified.
2097 if (!IS_UNDEFINED(frame)) {
2098 var frame_number = %ToNumber(frame);
2099 if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
2100 return response.failed('Invalid frame "' + frame + '"');
2102 // Evaluate in the specified frame.
2103 response.body = this.exec_state_.frame(frame_number).evaluate(
2104 expression, Boolean(disable_break), additional_context_object);
2107 // Evaluate in the selected frame.
2108 response.body = this.exec_state_.frame().evaluate(
2109 expression, Boolean(disable_break), additional_context_object);
2115 DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) {
2116 if (!request.arguments) {
2117 return response.failed('Missing arguments');
2120 // Pull out arguments.
2121 var handles = request.arguments.handles;
2123 // Check for legal arguments.
2124 if (IS_UNDEFINED(handles)) {
2125 return response.failed('Argument "handles" missing');
2128 // Set 'includeSource' option for script lookup.
2129 if (!IS_UNDEFINED(request.arguments.includeSource)) {
2130 var includeSource = %ToBoolean(request.arguments.includeSource);
2131 response.setOption('includeSource', includeSource);
2136 for (var i = 0; i < handles.length; i++) {
2137 var handle = handles[i];
2138 var mirror = LookupMirror(handle);
2140 return response.failed('Object #' + handle + '# not found');
2142 mirrors[handle] = mirror;
2144 response.body = mirrors;
2148 DebugCommandProcessor.prototype.referencesRequest_ =
2149 function(request, response) {
2150 if (!request.arguments) {
2151 return response.failed('Missing arguments');
2154 // Pull out arguments.
2155 var type = request.arguments.type;
2156 var handle = request.arguments.handle;
2158 // Check for legal arguments.
2159 if (IS_UNDEFINED(type)) {
2160 return response.failed('Argument "type" missing');
2162 if (IS_UNDEFINED(handle)) {
2163 return response.failed('Argument "handle" missing');
2165 if (type != 'referencedBy' && type != 'constructedBy') {
2166 return response.failed('Invalid type "' + type + '"');
2169 // Lookup handle and return objects with references the object.
2170 var mirror = LookupMirror(handle);
2172 if (type == 'referencedBy') {
2173 response.body = mirror.referencedBy();
2175 response.body = mirror.constructedBy();
2178 return response.failed('Object #' + handle + '# not found');
2183 DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) {
2184 // No frames no source.
2185 if (this.exec_state_.frameCount() == 0) {
2186 return response.failed('No source');
2191 var frame = this.exec_state_.frame();
2192 if (request.arguments) {
2193 // Pull out arguments.
2194 from_line = request.arguments.fromLine;
2195 to_line = request.arguments.toLine;
2197 if (!IS_UNDEFINED(request.arguments.frame)) {
2198 var frame_number = %ToNumber(request.arguments.frame);
2199 if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
2200 return response.failed('Invalid frame "' + frame + '"');
2202 frame = this.exec_state_.frame(frame_number);
2206 // Get the script selected.
2207 var script = frame.func().script();
2209 return response.failed('No source');
2212 // Get the source slice and fill it into the response.
2213 var slice = script.sourceSlice(from_line, to_line);
2215 return response.failed('Invalid line interval');
2218 response.body.source = slice.sourceText();
2219 response.body.fromLine = slice.from_line;
2220 response.body.toLine = slice.to_line;
2221 response.body.fromPosition = slice.from_position;
2222 response.body.toPosition = slice.to_position;
2223 response.body.totalLines = script.lineCount();
2227 DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) {
2228 var types = ScriptTypeFlag(Debug.ScriptType.Normal);
2229 var includeSource = false;
2230 var idsToInclude = null;
2231 if (request.arguments) {
2232 // Pull out arguments.
2233 if (!IS_UNDEFINED(request.arguments.types)) {
2234 types = %ToNumber(request.arguments.types);
2235 if (isNaN(types) || types < 0) {
2236 return response.failed('Invalid types "' +
2237 request.arguments.types + '"');
2241 if (!IS_UNDEFINED(request.arguments.includeSource)) {
2242 includeSource = %ToBoolean(request.arguments.includeSource);
2243 response.setOption('includeSource', includeSource);
2246 if (IS_ARRAY(request.arguments.ids)) {
2248 var ids = request.arguments.ids;
2249 for (var i = 0; i < ids.length; i++) {
2250 idsToInclude[ids[i]] = true;
2254 var filterStr = null;
2255 var filterNum = null;
2256 if (!IS_UNDEFINED(request.arguments.filter)) {
2257 var num = %ToNumber(request.arguments.filter);
2261 filterStr = request.arguments.filter;
2265 // Collect all scripts in the heap.
2266 var scripts = %DebugGetLoadedScripts();
2270 for (var i = 0; i < scripts.length; i++) {
2271 if (idsToInclude && !idsToInclude[scripts[i].id]) {
2274 if (filterStr || filterNum) {
2275 var script = scripts[i];
2277 if (filterNum && !found) {
2278 if (script.id && script.id === filterNum) {
2282 if (filterStr && !found) {
2283 if (script.name && script.name.indexOf(filterStr) >= 0) {
2287 if (!found) continue;
2289 if (types & ScriptTypeFlag(scripts[i].type)) {
2290 response.body.push(MakeMirror(scripts[i]));
2296 DebugCommandProcessor.prototype.threadsRequest_ = function(request, response) {
2297 // Get the number of threads.
2298 var total_threads = this.exec_state_.threadCount();
2300 // Get information for all threads.
2302 for (var i = 0; i < total_threads; i++) {
2303 var details = %GetThreadDetails(this.exec_state_.break_id, i);
2304 var thread_info = { current: details[0],
2307 threads.push(thread_info);
2310 // Create the response body.
2312 totalThreads: total_threads,
2318 DebugCommandProcessor.prototype.suspendRequest_ = function(request, response) {
2319 response.running = false;
2323 DebugCommandProcessor.prototype.versionRequest_ = function(request, response) {
2325 V8Version: %GetV8Version()
2330 DebugCommandProcessor.prototype.changeLiveRequest_ = function(
2331 request, response) {
2332 if (!request.arguments) {
2333 return response.failed('Missing arguments');
2335 var script_id = request.arguments.script_id;
2336 var preview_only = !!request.arguments.preview_only;
2338 var scripts = %DebugGetLoadedScripts();
2340 var the_script = null;
2341 for (var i = 0; i < scripts.length; i++) {
2342 if (scripts[i].id == script_id) {
2343 the_script = scripts[i];
2347 response.failed('Script not found');
2351 var change_log = new Array();
2353 if (!IS_STRING(request.arguments.new_source)) {
2354 throw "new_source argument expected";
2357 var new_source = request.arguments.new_source;
2359 var result_description;
2361 result_description = Debug.LiveEdit.SetScriptSource(the_script,
2362 new_source, preview_only, change_log);
2364 if (e instanceof Debug.LiveEdit.Failure && "details" in e) {
2365 response.failed(e.message, e.details);
2370 response.body = {change_log: change_log, result: result_description};
2372 if (!preview_only && !this.running_ && result_description.stack_modified) {
2373 response.body.stepin_recommended = true;
2378 DebugCommandProcessor.prototype.restartFrameRequest_ = function(
2379 request, response) {
2380 if (!request.arguments) {
2381 return response.failed('Missing arguments');
2383 var frame = request.arguments.frame;
2385 // No frames to evaluate in frame.
2386 if (this.exec_state_.frameCount() == 0) {
2387 return response.failed('No frames');
2391 // Check whether a frame was specified.
2392 if (!IS_UNDEFINED(frame)) {
2393 var frame_number = %ToNumber(frame);
2394 if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
2395 return response.failed('Invalid frame "' + frame + '"');
2397 // Restart specified frame.
2398 frame_mirror = this.exec_state_.frame(frame_number);
2400 // Restart selected frame.
2401 frame_mirror = this.exec_state_.frame();
2404 var result_description = Debug.LiveEdit.RestartFrame(frame_mirror);
2405 response.body = {result: result_description};
2409 DebugCommandProcessor.prototype.debuggerFlagsRequest_ = function(request,
2411 // Check for legal request.
2412 if (!request.arguments) {
2413 response.failed('Missing arguments');
2417 // Pull out arguments.
2418 var flags = request.arguments.flags;
2420 response.body = { flags: [] };
2421 if (!IS_UNDEFINED(flags)) {
2422 for (var i = 0; i < flags.length; i++) {
2423 var name = flags[i].name;
2424 var debugger_flag = debugger_flags[name];
2425 if (!debugger_flag) {
2428 if ('value' in flags[i]) {
2429 debugger_flag.setValue(flags[i].value);
2431 response.body.flags.push({ name: name, value: debugger_flag.getValue() });
2434 for (var name in debugger_flags) {
2435 var value = debugger_flags[name].getValue();
2436 response.body.flags.push({ name: name, value: value });
2442 DebugCommandProcessor.prototype.v8FlagsRequest_ = function(request, response) {
2443 var flags = request.arguments.flags;
2444 if (!flags) flags = '';
2449 DebugCommandProcessor.prototype.gcRequest_ = function(request, response) {
2450 var type = request.arguments.type;
2451 if (!type) type = 'all';
2453 var before = %GetHeapUsage();
2454 %CollectGarbage(type);
2455 var after = %GetHeapUsage();
2457 response.body = { "before": before, "after": after };
2461 DebugCommandProcessor.prototype.dispatch_ = (function() {
2462 var proto = DebugCommandProcessor.prototype;
2464 "continue": proto.continueRequest_,
2465 "break" : proto.breakRequest_,
2466 "setbreakpoint" : proto.setBreakPointRequest_,
2467 "changebreakpoint": proto.changeBreakPointRequest_,
2468 "clearbreakpoint": proto.clearBreakPointRequest_,
2469 "clearbreakpointgroup": proto.clearBreakPointGroupRequest_,
2470 "disconnect": proto.disconnectRequest_,
2471 "setexceptionbreak": proto.setExceptionBreakRequest_,
2472 "listbreakpoints": proto.listBreakpointsRequest_,
2473 "backtrace": proto.backtraceRequest_,
2474 "frame": proto.frameRequest_,
2475 "scopes": proto.scopesRequest_,
2476 "scope": proto.scopeRequest_,
2477 "setvariablevalue": proto.setVariableValueRequest_,
2478 "evaluate": proto.evaluateRequest_,
2479 "lookup": proto.lookupRequest_,
2480 "references": proto.referencesRequest_,
2481 "source": proto.sourceRequest_,
2482 "scripts": proto.scriptsRequest_,
2483 "threads": proto.threadsRequest_,
2484 "suspend": proto.suspendRequest_,
2485 "version": proto.versionRequest_,
2486 "changelive": proto.changeLiveRequest_,
2487 "restartframe": proto.restartFrameRequest_,
2488 "flags": proto.debuggerFlagsRequest_,
2489 "v8flag": proto.v8FlagsRequest_,
2490 "gc": proto.gcRequest_,
2495 // Check whether the previously processed command caused the VM to become
2497 DebugCommandProcessor.prototype.isRunning = function() {
2498 return this.running_;
2502 DebugCommandProcessor.prototype.systemBreak = function(cmd, args) {
2503 return %SystemBreak();
2508 * Convert an Object to its debugger protocol representation. The representation
2509 * may be serilized to a JSON object using JSON.stringify().
2510 * This implementation simply runs through all string property names, converts
2511 * each property value to a protocol value and adds the property to the result
2512 * object. For type "object" the function will be called recursively. Note that
2513 * circular structures will cause infinite recursion.
2514 * @param {Object} object The object to format as protocol object.
2515 * @param {MirrorSerializer} mirror_serializer The serializer to use if any
2516 * mirror objects are encountered.
2517 * @return {Object} Protocol object value.
2519 function ObjectToProtocolObject_(object, mirror_serializer) {
2521 for (var key in object) {
2522 // Only consider string keys.
2523 if (typeof key == 'string') {
2524 // Format the value based on its type.
2525 var property_value_json = ValueToProtocolValue_(object[key],
2527 // Add the property if relevant.
2528 if (!IS_UNDEFINED(property_value_json)) {
2529 content[key] = property_value_json;
2539 * Convert an array to its debugger protocol representation. It will convert
2540 * each array element to a protocol value.
2541 * @param {Array} array The array to format as protocol array.
2542 * @param {MirrorSerializer} mirror_serializer The serializer to use if any
2543 * mirror objects are encountered.
2544 * @return {Array} Protocol array value.
2546 function ArrayToProtocolArray_(array, mirror_serializer) {
2548 for (var i = 0; i < array.length; i++) {
2549 json.push(ValueToProtocolValue_(array[i], mirror_serializer));
2556 * Convert a value to its debugger protocol representation.
2557 * @param {*} value The value to format as protocol value.
2558 * @param {MirrorSerializer} mirror_serializer The serializer to use if any
2559 * mirror objects are encountered.
2560 * @return {*} Protocol value.
2562 function ValueToProtocolValue_(value, mirror_serializer) {
2563 // Format the value based on its type.
2565 switch (typeof value) {
2567 if (value instanceof Mirror) {
2568 json = mirror_serializer.serializeValue(value);
2569 } else if (IS_ARRAY(value)){
2570 json = ArrayToProtocolArray_(value, mirror_serializer);
2572 json = ObjectToProtocolObject_(value, mirror_serializer);
2589 CommandProcessorResolveValue: DebugCommandProcessor.resolveValue_