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