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