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