blob: d5e91cbdd25c4334d531504d066767e3432e6027 [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.
48Debug.ExceptionBreak = { All : 0,
49 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 = [];
82
83
84// Create a new break point object and add it to the list of break points.
85function MakeBreakPoint(source_position, opt_line, opt_column, opt_script_break_point) {
86 var break_point = new BreakPoint(source_position, opt_line, opt_column, opt_script_break_point);
87 break_points.push(break_point);
88 return break_point;
89}
90
91
92// Object representing a break point.
93// NOTE: This object does not have a reference to the function having break
94// point as this would cause function not to be garbage collected when it is
95// not used any more. We do not want break points to keep functions alive.
96function BreakPoint(source_position, opt_line, opt_column, opt_script_break_point) {
97 this.source_position_ = source_position;
98 this.source_line_ = opt_line;
99 this.source_column_ = opt_column;
100 if (opt_script_break_point) {
101 this.script_break_point_ = opt_script_break_point;
102 } else {
103 this.number_ = next_break_point_number++;
104 }
105 this.hit_count_ = 0;
106 this.active_ = true;
107 this.condition_ = null;
108 this.ignoreCount_ = 0;
109}
110
111
112BreakPoint.prototype.number = function() {
113 return this.number_;
114};
115
116
117BreakPoint.prototype.func = function() {
118 return this.func_;
119};
120
121
122BreakPoint.prototype.source_position = function() {
123 return this.source_position_;
124};
125
126
127BreakPoint.prototype.hit_count = function() {
128 return this.hit_count_;
129};
130
131
132BreakPoint.prototype.active = function() {
133 if (this.script_break_point()) {
134 return this.script_break_point().active();
135 }
136 return this.active_;
137};
138
139
140BreakPoint.prototype.condition = function() {
141 if (this.script_break_point() && this.script_break_point().condition()) {
142 return this.script_break_point().condition();
143 }
144 return this.condition_;
145};
146
147
148BreakPoint.prototype.ignoreCount = function() {
149 return this.ignoreCount_;
150};
151
152
153BreakPoint.prototype.script_break_point = function() {
154 return this.script_break_point_;
155};
156
157
158BreakPoint.prototype.enable = function() {
159 this.active_ = true;
160};
161
162
163BreakPoint.prototype.disable = function() {
164 this.active_ = false;
165};
166
167
168BreakPoint.prototype.setCondition = function(condition) {
169 this.condition_ = condition;
170};
171
172
173BreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
174 this.ignoreCount_ = ignoreCount;
175};
176
177
178BreakPoint.prototype.isTriggered = function(exec_state) {
179 // Break point not active - not triggered.
180 if (!this.active()) return false;
181
182 // Check for conditional break point.
183 if (this.condition()) {
184 // If break point has condition try to evaluate it in the top frame.
185 try {
186 var mirror = exec_state.frame(0).evaluate(this.condition());
187 // If no sensible mirror or non true value break point not triggered.
188 if (!(mirror instanceof ValueMirror) || !%ToBoolean(mirror.value_)) {
189 return false;
190 }
191 } catch (e) {
192 // Exception evaluating condition counts as not triggered.
193 return false;
194 }
195 }
196
197 // Update the hit count.
198 this.hit_count_++;
199 if (this.script_break_point_) {
200 this.script_break_point_.hit_count_++;
201 }
202
203 // If the break point has an ignore count it is not triggered.
204 if (this.ignoreCount_ > 0) {
205 this.ignoreCount_--;
206 return false;
207 }
208
209 // Break point triggered.
210 return true;
211};
212
213
214// Function called from the runtime when a break point is hit. Returns true if
215// the break point is triggered and supposed to break execution.
216function IsBreakPointTriggered(break_id, break_point) {
217 return break_point.isTriggered(MakeExecutionState(break_id));
218}
219
220
221// Object representing a script break point. The script is referenced by its
222// script name or script id and the break point is represented as line and
223// column.
224function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
225 opt_groupId) {
226 this.type_ = type;
227 if (type == Debug.ScriptBreakPointType.ScriptId) {
228 this.script_id_ = script_id_or_name;
229 } else { // type == Debug.ScriptBreakPointType.ScriptName
230 this.script_name_ = script_id_or_name;
231 }
232 this.line_ = opt_line || 0;
233 this.column_ = opt_column;
234 this.groupId_ = opt_groupId;
235 this.hit_count_ = 0;
236 this.active_ = true;
237 this.condition_ = null;
238 this.ignoreCount_ = 0;
239}
240
241
Steve Block6ded16b2010-05-10 14:33:55 +0100242//Creates a clone of script breakpoint that is linked to another script.
243ScriptBreakPoint.prototype.cloneForOtherScript = function (other_script) {
244 var copy = new ScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
245 other_script.id, this.line_, this.column_, this.groupId_);
246 copy.number_ = next_break_point_number++;
247 script_break_points.push(copy);
248
249 copy.hit_count_ = this.hit_count_;
250 copy.active_ = this.active_;
251 copy.condition_ = this.condition_;
252 copy.ignoreCount_ = this.ignoreCount_;
253 return copy;
254}
255
256
Andrei Popescu31002712010-02-23 13:46:05 +0000257ScriptBreakPoint.prototype.number = function() {
258 return this.number_;
259};
260
261
262ScriptBreakPoint.prototype.groupId = function() {
263 return this.groupId_;
264};
265
266
267ScriptBreakPoint.prototype.type = function() {
268 return this.type_;
269};
270
271
272ScriptBreakPoint.prototype.script_id = function() {
273 return this.script_id_;
274};
275
276
277ScriptBreakPoint.prototype.script_name = function() {
278 return this.script_name_;
279};
280
281
282ScriptBreakPoint.prototype.line = function() {
283 return this.line_;
284};
285
286
287ScriptBreakPoint.prototype.column = function() {
288 return this.column_;
289};
290
291
Steve Block6ded16b2010-05-10 14:33:55 +0100292ScriptBreakPoint.prototype.update_positions = function(line, column) {
293 this.line_ = line;
294 this.column_ = column;
295}
296
297
Andrei Popescu31002712010-02-23 13:46:05 +0000298ScriptBreakPoint.prototype.hit_count = function() {
299 return this.hit_count_;
300};
301
302
303ScriptBreakPoint.prototype.active = function() {
304 return this.active_;
305};
306
307
308ScriptBreakPoint.prototype.condition = function() {
309 return this.condition_;
310};
311
312
313ScriptBreakPoint.prototype.ignoreCount = function() {
314 return this.ignoreCount_;
315};
316
317
318ScriptBreakPoint.prototype.enable = function() {
319 this.active_ = true;
320};
321
322
323ScriptBreakPoint.prototype.disable = function() {
324 this.active_ = false;
325};
326
327
328ScriptBreakPoint.prototype.setCondition = function(condition) {
329 this.condition_ = condition;
330};
331
332
333ScriptBreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
334 this.ignoreCount_ = ignoreCount;
335
336 // Set ignore count on all break points created from this script break point.
337 for (var i = 0; i < break_points.length; i++) {
338 if (break_points[i].script_break_point() === this) {
339 break_points[i].setIgnoreCount(ignoreCount);
340 }
341 }
342};
343
344
345// Check whether a script matches this script break point. Currently this is
346// only based on script name.
347ScriptBreakPoint.prototype.matchesScript = function(script) {
348 if (this.type_ == Debug.ScriptBreakPointType.ScriptId) {
349 return this.script_id_ == script.id;
350 } else { // this.type_ == Debug.ScriptBreakPointType.ScriptName
Steve Block6ded16b2010-05-10 14:33:55 +0100351 return this.script_name_ == script.nameOrSourceURL() &&
Andrei Popescu31002712010-02-23 13:46:05 +0000352 script.line_offset <= this.line_ &&
353 this.line_ < script.line_offset + script.lineCount();
354 }
355};
356
357
358// Set the script break point in a script.
359ScriptBreakPoint.prototype.set = function (script) {
360 var column = this.column();
361 var line = this.line();
362 // If the column is undefined the break is on the line. To help locate the
363 // first piece of breakable code on the line try to find the column on the
364 // line which contains some source.
365 if (IS_UNDEFINED(column)) {
366 var source_line = script.sourceLine(this.line());
367
368 // Allocate array for caching the columns where the actual source starts.
369 if (!script.sourceColumnStart_) {
370 script.sourceColumnStart_ = new Array(script.lineCount());
371 }
372
373 // Fill cache if needed and get column where the actual source starts.
374 if (IS_UNDEFINED(script.sourceColumnStart_[line])) {
375 script.sourceColumnStart_[line] =
376 source_line.match(sourceLineBeginningSkip)[0].length;
377 }
378 column = script.sourceColumnStart_[line];
379 }
380
381 // Convert the line and column into an absolute position within the script.
382 var pos = Debug.findScriptSourcePosition(script, this.line(), column);
383
384 // If the position is not found in the script (the script might be shorter
385 // than it used to be) just ignore it.
386 if (pos === null) return;
387
388 // Create a break point object and set the break point.
389 break_point = MakeBreakPoint(pos, this.line(), this.column(), this);
390 break_point.setIgnoreCount(this.ignoreCount());
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100391 pos = %SetScriptBreakPoint(script, pos, break_point);
392 if (!IS_UNDEFINED(pos)) {
393 this.actual_location = script.locationFromPosition(pos);
394 }
Andrei Popescu31002712010-02-23 13:46:05 +0000395
396 return break_point;
397};
398
399
400// Clear all the break points created from this script break point
401ScriptBreakPoint.prototype.clear = function () {
402 var remaining_break_points = [];
403 for (var i = 0; i < break_points.length; i++) {
404 if (break_points[i].script_break_point() &&
405 break_points[i].script_break_point() === this) {
406 %ClearBreakPoint(break_points[i]);
407 } else {
408 remaining_break_points.push(break_points[i]);
409 }
410 }
411 break_points = remaining_break_points;
412};
413
414
415// Function called from runtime when a new script is compiled to set any script
416// break points set in this script.
417function UpdateScriptBreakPoints(script) {
418 for (var i = 0; i < script_break_points.length; i++) {
419 if (script_break_points[i].type() == Debug.ScriptBreakPointType.ScriptName &&
420 script_break_points[i].matchesScript(script)) {
421 script_break_points[i].set(script);
422 }
423 }
424}
425
426
Steve Block6ded16b2010-05-10 14:33:55 +0100427function GetScriptBreakPoints(script) {
428 var result = [];
429 for (var i = 0; i < script_break_points.length; i++) {
430 if (script_break_points[i].matchesScript(script)) {
431 result.push(script_break_points[i]);
432 }
433 }
434 return result;
435}
436
437
Andrei Popescu31002712010-02-23 13:46:05 +0000438Debug.setListener = function(listener, opt_data) {
439 if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) {
440 throw new Error('Parameters have wrong types.');
441 }
442 %SetDebugEventListener(listener, opt_data);
443};
444
445
446Debug.breakExecution = function(f) {
447 %Break();
448};
449
450Debug.breakLocations = function(f) {
451 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
452 return %GetBreakLocations(f);
453};
454
455// Returns a Script object. If the parameter is a function the return value
456// is the script in which the function is defined. If the parameter is a string
457// the return value is the script for which the script name has that string
458// value. If it is a regexp and there is a unique script whose name matches
459// we return that, otherwise undefined.
460Debug.findScript = function(func_or_script_name) {
461 if (IS_FUNCTION(func_or_script_name)) {
462 return %FunctionGetScript(func_or_script_name);
463 } else if (IS_REGEXP(func_or_script_name)) {
464 var scripts = Debug.scripts();
465 var last_result = null;
466 var result_count = 0;
467 for (var i in scripts) {
468 var script = scripts[i];
469 if (func_or_script_name.test(script.name)) {
470 last_result = script;
471 result_count++;
472 }
473 }
474 // Return the unique script matching the regexp. If there are more
475 // than one we don't return a value since there is no good way to
476 // decide which one to return. Returning a "random" one, say the
477 // first, would introduce nondeterminism (or something close to it)
478 // because the order is the heap iteration order.
479 if (result_count == 1) {
480 return last_result;
481 } else {
482 return undefined;
483 }
484 } else {
485 return %GetScript(func_or_script_name);
486 }
487};
488
489// Returns the script source. If the parameter is a function the return value
490// is the script source for the script in which the function is defined. If the
491// parameter is a string the return value is the script for which the script
492// name has that string value.
493Debug.scriptSource = function(func_or_script_name) {
494 return this.findScript(func_or_script_name).source;
495};
496
497Debug.source = function(f) {
498 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
499 return %FunctionGetSourceCode(f);
500};
501
502Debug.disassemble = function(f) {
503 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
504 return %DebugDisassembleFunction(f);
505};
506
507Debug.disassembleConstructor = function(f) {
508 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
509 return %DebugDisassembleConstructor(f);
510};
511
Steve Block6ded16b2010-05-10 14:33:55 +0100512Debug.ExecuteInDebugContext = function(f, without_debugger) {
513 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
514 return %ExecuteInDebugContext(f, !!without_debugger);
515};
516
Andrei Popescu31002712010-02-23 13:46:05 +0000517Debug.sourcePosition = function(f) {
518 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
519 return %FunctionGetScriptSourcePosition(f);
520};
521
522
523Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) {
524 var script = %FunctionGetScript(func);
525 var script_offset = %FunctionGetScriptSourcePosition(func);
526 return script.locationFromLine(opt_line, opt_column, script_offset);
527}
528
529
530// Returns the character position in a script based on a line number and an
531// optional position within that line.
532Debug.findScriptSourcePosition = function(script, opt_line, opt_column) {
533 var location = script.locationFromLine(opt_line, opt_column);
534 return location ? location.position : null;
535}
536
537
538Debug.findBreakPoint = function(break_point_number, remove) {
539 var break_point;
540 for (var i = 0; i < break_points.length; i++) {
541 if (break_points[i].number() == break_point_number) {
542 break_point = break_points[i];
543 // Remove the break point from the list if requested.
544 if (remove) {
545 break_points.splice(i, 1);
546 }
547 break;
548 }
549 }
550 if (break_point) {
551 return break_point;
552 } else {
553 return this.findScriptBreakPoint(break_point_number, remove);
554 }
555};
556
557
558Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) {
559 if (!IS_FUNCTION(func)) throw new Error('Parameters have wrong types.');
560 // Break points in API functions are not supported.
561 if (%FunctionIsAPIFunction(func)) {
562 throw new Error('Cannot set break point in native code.');
563 }
564 // Find source position relative to start of the function
565 var break_position =
566 this.findFunctionSourceLocation(func, opt_line, opt_column).position;
567 var source_position = break_position - this.sourcePosition(func);
568 // Find the script for the function.
569 var script = %FunctionGetScript(func);
570 // Break in builtin JavaScript code is not supported.
571 if (script.type == Debug.ScriptType.Native) {
572 throw new Error('Cannot set break point in native code.');
573 }
574 // If the script for the function has a name convert this to a script break
575 // point.
576 if (script && script.id) {
577 // Adjust the source position to be script relative.
578 source_position += %FunctionGetScriptSourcePosition(func);
579 // Find line and column for the position in the script and set a script
580 // break point from that.
581 var location = script.locationFromPosition(source_position, false);
582 return this.setScriptBreakPointById(script.id,
583 location.line, location.column,
584 opt_condition);
585 } else {
586 // Set a break point directly on the function.
587 var break_point = MakeBreakPoint(source_position, opt_line, opt_column);
588 %SetFunctionBreakPoint(func, source_position, break_point);
589 break_point.setCondition(opt_condition);
590 return break_point.number();
591 }
592};
593
594
595Debug.enableBreakPoint = function(break_point_number) {
596 var break_point = this.findBreakPoint(break_point_number, false);
597 break_point.enable();
598};
599
600
601Debug.disableBreakPoint = function(break_point_number) {
602 var break_point = this.findBreakPoint(break_point_number, false);
603 break_point.disable();
604};
605
606
607Debug.changeBreakPointCondition = function(break_point_number, condition) {
608 var break_point = this.findBreakPoint(break_point_number, false);
609 break_point.setCondition(condition);
610};
611
612
613Debug.changeBreakPointIgnoreCount = function(break_point_number, ignoreCount) {
614 if (ignoreCount < 0) {
615 throw new Error('Invalid argument');
616 }
617 var break_point = this.findBreakPoint(break_point_number, false);
618 break_point.setIgnoreCount(ignoreCount);
619};
620
621
622Debug.clearBreakPoint = function(break_point_number) {
623 var break_point = this.findBreakPoint(break_point_number, true);
624 if (break_point) {
625 return %ClearBreakPoint(break_point);
626 } else {
627 break_point = this.findScriptBreakPoint(break_point_number, true);
628 if (!break_point) {
629 throw new Error('Invalid breakpoint');
630 }
631 }
632};
633
634
635Debug.clearAllBreakPoints = function() {
636 for (var i = 0; i < break_points.length; i++) {
637 break_point = break_points[i];
638 %ClearBreakPoint(break_point);
639 }
640 break_points = [];
641};
642
643
644Debug.findScriptBreakPoint = function(break_point_number, remove) {
645 var script_break_point;
646 for (var i = 0; i < script_break_points.length; i++) {
647 if (script_break_points[i].number() == break_point_number) {
648 script_break_point = script_break_points[i];
649 // Remove the break point from the list if requested.
650 if (remove) {
651 script_break_point.clear();
652 script_break_points.splice(i,1);
653 }
654 break;
655 }
656 }
657 return script_break_point;
658}
659
660
661// Sets a breakpoint in a script identified through id or name at the
662// specified source line and column within that line.
663Debug.setScriptBreakPoint = function(type, script_id_or_name,
664 opt_line, opt_column, opt_condition,
665 opt_groupId) {
666 // Create script break point object.
667 var script_break_point =
668 new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
669 opt_groupId);
670
671 // Assign number to the new script break point and add it.
672 script_break_point.number_ = next_break_point_number++;
673 script_break_point.setCondition(opt_condition);
674 script_break_points.push(script_break_point);
675
676 // Run through all scripts to see if this script break point matches any
677 // loaded scripts.
678 var scripts = this.scripts();
679 for (var i = 0; i < scripts.length; i++) {
680 if (script_break_point.matchesScript(scripts[i])) {
681 script_break_point.set(scripts[i]);
682 }
683 }
684
685 return script_break_point.number();
686}
687
688
689Debug.setScriptBreakPointById = function(script_id,
690 opt_line, opt_column,
691 opt_condition, opt_groupId) {
692 return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
693 script_id, opt_line, opt_column,
694 opt_condition, opt_groupId);
695}
696
697
698Debug.setScriptBreakPointByName = function(script_name,
699 opt_line, opt_column,
700 opt_condition, opt_groupId) {
701 return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName,
702 script_name, opt_line, opt_column,
703 opt_condition, opt_groupId);
704}
705
706
707Debug.enableScriptBreakPoint = function(break_point_number) {
708 var script_break_point = this.findScriptBreakPoint(break_point_number, false);
709 script_break_point.enable();
710};
711
712
713Debug.disableScriptBreakPoint = function(break_point_number) {
714 var script_break_point = this.findScriptBreakPoint(break_point_number, false);
715 script_break_point.disable();
716};
717
718
719Debug.changeScriptBreakPointCondition = function(break_point_number, condition) {
720 var script_break_point = this.findScriptBreakPoint(break_point_number, false);
721 script_break_point.setCondition(condition);
722};
723
724
725Debug.changeScriptBreakPointIgnoreCount = function(break_point_number, ignoreCount) {
726 if (ignoreCount < 0) {
727 throw new Error('Invalid argument');
728 }
729 var script_break_point = this.findScriptBreakPoint(break_point_number, false);
730 script_break_point.setIgnoreCount(ignoreCount);
731};
732
733
734Debug.scriptBreakPoints = function() {
735 return script_break_points;
736}
737
738
739Debug.clearStepping = function() {
740 %ClearStepping();
741}
742
743Debug.setBreakOnException = function() {
744 return %ChangeBreakOnException(Debug.ExceptionBreak.All, true);
745};
746
747Debug.clearBreakOnException = function() {
748 return %ChangeBreakOnException(Debug.ExceptionBreak.All, false);
749};
750
751Debug.setBreakOnUncaughtException = function() {
752 return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true);
753};
754
755Debug.clearBreakOnUncaughtException = function() {
756 return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
757};
758
759Debug.showBreakPoints = function(f, full) {
760 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
761 var source = full ? this.scriptSource(f) : this.source(f);
762 var offset = full ? this.sourcePosition(f) : 0;
763 var locations = this.breakLocations(f);
764 if (!locations) return source;
765 locations.sort(function(x, y) { return x - y; });
766 var result = "";
767 var prev_pos = 0;
768 var pos;
769 for (var i = 0; i < locations.length; i++) {
770 pos = locations[i] - offset;
771 result += source.slice(prev_pos, pos);
772 result += "[B" + i + "]";
773 prev_pos = pos;
774 }
775 pos = source.length;
776 result += source.substring(prev_pos, pos);
777 return result;
778};
779
780
781// Get all the scripts currently loaded. Locating all the scripts is based on
782// scanning the heap.
783Debug.scripts = function() {
784 // Collect all scripts in the heap.
785 return %DebugGetLoadedScripts();
786}
787
788function MakeExecutionState(break_id) {
789 return new ExecutionState(break_id);
790}
791
792function ExecutionState(break_id) {
793 this.break_id = break_id;
794 this.selected_frame = 0;
795}
796
797ExecutionState.prototype.prepareStep = function(opt_action, opt_count) {
798 var action = Debug.StepAction.StepIn;
799 if (!IS_UNDEFINED(opt_action)) action = %ToNumber(opt_action);
800 var count = opt_count ? %ToNumber(opt_count) : 1;
801
802 return %PrepareStep(this.break_id, action, count);
803}
804
805ExecutionState.prototype.evaluateGlobal = function(source, disable_break) {
806 return MakeMirror(
807 %DebugEvaluateGlobal(this.break_id, source, Boolean(disable_break)));
808};
809
810ExecutionState.prototype.frameCount = function() {
811 return %GetFrameCount(this.break_id);
812};
813
814ExecutionState.prototype.threadCount = function() {
815 return %GetThreadCount(this.break_id);
816};
817
818ExecutionState.prototype.frame = function(opt_index) {
819 // If no index supplied return the selected frame.
820 if (opt_index == null) opt_index = this.selected_frame;
Steve Block6ded16b2010-05-10 14:33:55 +0100821 if (opt_index < 0 || opt_index >= this.frameCount())
822 throw new Error('Illegal frame index.');
Andrei Popescu31002712010-02-23 13:46:05 +0000823 return new FrameMirror(this.break_id, opt_index);
824};
825
826ExecutionState.prototype.cframesValue = function(opt_from_index, opt_to_index) {
827 return %GetCFrames(this.break_id);
828};
829
830ExecutionState.prototype.setSelectedFrame = function(index) {
831 var i = %ToNumber(index);
832 if (i < 0 || i >= this.frameCount()) throw new Error('Illegal frame index.');
833 this.selected_frame = i;
834};
835
836ExecutionState.prototype.selectedFrame = function() {
837 return this.selected_frame;
838};
839
840ExecutionState.prototype.debugCommandProcessor = function(opt_is_running) {
841 return new DebugCommandProcessor(this, opt_is_running);
842};
843
844
845function MakeBreakEvent(exec_state, break_points_hit) {
846 return new BreakEvent(exec_state, break_points_hit);
847}
848
849
850function BreakEvent(exec_state, break_points_hit) {
851 this.exec_state_ = exec_state;
852 this.break_points_hit_ = break_points_hit;
853}
854
855
856BreakEvent.prototype.executionState = function() {
857 return this.exec_state_;
858};
859
860
861BreakEvent.prototype.eventType = function() {
862 return Debug.DebugEvent.Break;
863};
864
865
866BreakEvent.prototype.func = function() {
867 return this.exec_state_.frame(0).func();
868};
869
870
871BreakEvent.prototype.sourceLine = function() {
872 return this.exec_state_.frame(0).sourceLine();
873};
874
875
876BreakEvent.prototype.sourceColumn = function() {
877 return this.exec_state_.frame(0).sourceColumn();
878};
879
880
881BreakEvent.prototype.sourceLineText = function() {
882 return this.exec_state_.frame(0).sourceLineText();
883};
884
885
886BreakEvent.prototype.breakPointsHit = function() {
887 return this.break_points_hit_;
888};
889
890
891BreakEvent.prototype.toJSONProtocol = function() {
892 var o = { seq: next_response_seq++,
893 type: "event",
894 event: "break",
895 body: { invocationText: this.exec_state_.frame(0).invocationText(),
896 }
897 };
898
899 // Add script related information to the event if available.
900 var script = this.func().script();
901 if (script) {
902 o.body.sourceLine = this.sourceLine(),
903 o.body.sourceColumn = this.sourceColumn(),
904 o.body.sourceLineText = this.sourceLineText(),
905 o.body.script = MakeScriptObject_(script, false);
906 }
907
908 // Add an Array of break points hit if any.
909 if (this.breakPointsHit()) {
910 o.body.breakpoints = [];
911 for (var i = 0; i < this.breakPointsHit().length; i++) {
912 // Find the break point number. For break points originating from a
913 // script break point supply the script break point number.
914 var breakpoint = this.breakPointsHit()[i];
915 var script_break_point = breakpoint.script_break_point();
916 var number;
917 if (script_break_point) {
918 number = script_break_point.number();
919 } else {
920 number = breakpoint.number();
921 }
922 o.body.breakpoints.push(number);
923 }
924 }
925 return JSON.stringify(ObjectToProtocolObject_(o));
926};
927
928
929function MakeExceptionEvent(exec_state, exception, uncaught) {
930 return new ExceptionEvent(exec_state, exception, uncaught);
931}
932
933
934function ExceptionEvent(exec_state, exception, uncaught) {
935 this.exec_state_ = exec_state;
936 this.exception_ = exception;
937 this.uncaught_ = uncaught;
938}
939
940
941ExceptionEvent.prototype.executionState = function() {
942 return this.exec_state_;
943};
944
945
946ExceptionEvent.prototype.eventType = function() {
947 return Debug.DebugEvent.Exception;
948};
949
950
951ExceptionEvent.prototype.exception = function() {
952 return this.exception_;
953}
954
955
956ExceptionEvent.prototype.uncaught = function() {
957 return this.uncaught_;
958}
959
960
961ExceptionEvent.prototype.func = function() {
962 return this.exec_state_.frame(0).func();
963};
964
965
966ExceptionEvent.prototype.sourceLine = function() {
967 return this.exec_state_.frame(0).sourceLine();
968};
969
970
971ExceptionEvent.prototype.sourceColumn = function() {
972 return this.exec_state_.frame(0).sourceColumn();
973};
974
975
976ExceptionEvent.prototype.sourceLineText = function() {
977 return this.exec_state_.frame(0).sourceLineText();
978};
979
980
981ExceptionEvent.prototype.toJSONProtocol = function() {
982 var o = new ProtocolMessage();
983 o.event = "exception";
984 o.body = { uncaught: this.uncaught_,
985 exception: MakeMirror(this.exception_)
986 };
987
988 // Exceptions might happen whithout any JavaScript frames.
989 if (this.exec_state_.frameCount() > 0) {
990 o.body.sourceLine = this.sourceLine();
991 o.body.sourceColumn = this.sourceColumn();
992 o.body.sourceLineText = this.sourceLineText();
993
994 // Add script information to the event if available.
995 var script = this.func().script();
996 if (script) {
997 o.body.script = MakeScriptObject_(script, false);
998 }
999 } else {
1000 o.body.sourceLine = -1;
1001 }
1002
1003 return o.toJSONProtocol();
1004};
1005
1006
1007function MakeCompileEvent(exec_state, script, before) {
1008 return new CompileEvent(exec_state, script, before);
1009}
1010
1011
1012function CompileEvent(exec_state, script, before) {
1013 this.exec_state_ = exec_state;
1014 this.script_ = MakeMirror(script);
1015 this.before_ = before;
1016}
1017
1018
1019CompileEvent.prototype.executionState = function() {
1020 return this.exec_state_;
1021};
1022
1023
1024CompileEvent.prototype.eventType = function() {
1025 if (this.before_) {
1026 return Debug.DebugEvent.BeforeCompile;
1027 } else {
1028 return Debug.DebugEvent.AfterCompile;
1029 }
1030};
1031
1032
1033CompileEvent.prototype.script = function() {
1034 return this.script_;
1035};
1036
1037
1038CompileEvent.prototype.toJSONProtocol = function() {
1039 var o = new ProtocolMessage();
1040 o.running = true;
1041 if (this.before_) {
1042 o.event = "beforeCompile";
1043 } else {
1044 o.event = "afterCompile";
1045 }
1046 o.body = {};
1047 o.body.script = this.script_;
1048
1049 return o.toJSONProtocol();
1050}
1051
1052
1053function MakeNewFunctionEvent(func) {
1054 return new NewFunctionEvent(func);
1055}
1056
1057
1058function NewFunctionEvent(func) {
1059 this.func = func;
1060}
1061
1062
1063NewFunctionEvent.prototype.eventType = function() {
1064 return Debug.DebugEvent.NewFunction;
1065};
1066
1067
1068NewFunctionEvent.prototype.name = function() {
1069 return this.func.name;
1070};
1071
1072
1073NewFunctionEvent.prototype.setBreakPoint = function(p) {
1074 Debug.setBreakPoint(this.func, p || 0);
1075};
1076
1077
1078function MakeScriptCollectedEvent(exec_state, id) {
1079 return new ScriptCollectedEvent(exec_state, id);
1080}
1081
1082
1083function ScriptCollectedEvent(exec_state, id) {
1084 this.exec_state_ = exec_state;
1085 this.id_ = id;
1086}
1087
1088
1089ScriptCollectedEvent.prototype.id = function() {
1090 return this.id_;
1091};
1092
1093
1094ScriptCollectedEvent.prototype.executionState = function() {
1095 return this.exec_state_;
1096};
1097
1098
1099ScriptCollectedEvent.prototype.toJSONProtocol = function() {
1100 var o = new ProtocolMessage();
1101 o.running = true;
1102 o.event = "scriptCollected";
1103 o.body = {};
1104 o.body.script = { id: this.id() };
1105 return o.toJSONProtocol();
1106}
1107
1108
1109function MakeScriptObject_(script, include_source) {
1110 var o = { id: script.id(),
1111 name: script.name(),
1112 lineOffset: script.lineOffset(),
1113 columnOffset: script.columnOffset(),
1114 lineCount: script.lineCount(),
1115 };
1116 if (!IS_UNDEFINED(script.data())) {
1117 o.data = script.data();
1118 }
1119 if (include_source) {
1120 o.source = script.source();
1121 }
1122 return o;
1123};
1124
1125
1126function DebugCommandProcessor(exec_state, opt_is_running) {
1127 this.exec_state_ = exec_state;
1128 this.running_ = opt_is_running || false;
1129};
1130
1131
1132DebugCommandProcessor.prototype.processDebugRequest = function (request) {
1133 return this.processDebugJSONRequest(request);
1134}
1135
1136
1137function ProtocolMessage(request) {
1138 // Update sequence number.
1139 this.seq = next_response_seq++;
1140
1141 if (request) {
1142 // If message is based on a request this is a response. Fill the initial
1143 // response from the request.
1144 this.type = 'response';
1145 this.request_seq = request.seq;
1146 this.command = request.command;
1147 } else {
1148 // If message is not based on a request it is a dabugger generated event.
1149 this.type = 'event';
1150 }
1151 this.success = true;
1152 // Handler may set this field to control debugger state.
1153 this.running = undefined;
1154}
1155
1156
1157ProtocolMessage.prototype.setOption = function(name, value) {
1158 if (!this.options_) {
1159 this.options_ = {};
1160 }
1161 this.options_[name] = value;
1162}
1163
1164
1165ProtocolMessage.prototype.failed = function(message) {
1166 this.success = false;
1167 this.message = message;
1168}
1169
1170
1171ProtocolMessage.prototype.toJSONProtocol = function() {
1172 // Encode the protocol header.
1173 var json = {};
1174 json.seq= this.seq;
1175 if (this.request_seq) {
1176 json.request_seq = this.request_seq;
1177 }
1178 json.type = this.type;
1179 if (this.event) {
1180 json.event = this.event;
1181 }
1182 if (this.command) {
1183 json.command = this.command;
1184 }
1185 if (this.success) {
1186 json.success = this.success;
1187 } else {
1188 json.success = false;
1189 }
1190 if (this.body) {
1191 // Encode the body part.
1192 var bodyJson;
1193 var serializer = MakeMirrorSerializer(true, this.options_);
1194 if (this.body instanceof Mirror) {
1195 bodyJson = serializer.serializeValue(this.body);
1196 } else if (this.body instanceof Array) {
1197 bodyJson = [];
1198 for (var i = 0; i < this.body.length; i++) {
1199 if (this.body[i] instanceof Mirror) {
1200 bodyJson.push(serializer.serializeValue(this.body[i]));
1201 } else {
1202 bodyJson.push(ObjectToProtocolObject_(this.body[i], serializer));
1203 }
1204 }
1205 } else {
1206 bodyJson = ObjectToProtocolObject_(this.body, serializer);
1207 }
1208 json.body = bodyJson;
1209 json.refs = serializer.serializeReferencedObjects();
1210 }
1211 if (this.message) {
1212 json.message = this.message;
1213 }
1214 json.running = this.running;
1215 return JSON.stringify(json);
1216}
1217
1218
1219DebugCommandProcessor.prototype.createResponse = function(request) {
1220 return new ProtocolMessage(request);
1221};
1222
1223
1224DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request) {
1225 var request; // Current request.
1226 var response; // Generated response.
1227 try {
1228 try {
1229 // Convert the JSON string to an object.
1230 request = %CompileString('(' + json_request + ')', false)();
1231
1232 // Create an initial response.
1233 response = this.createResponse(request);
1234
1235 if (!request.type) {
1236 throw new Error('Type not specified');
1237 }
1238
1239 if (request.type != 'request') {
1240 throw new Error("Illegal type '" + request.type + "' in request");
1241 }
1242
1243 if (!request.command) {
1244 throw new Error('Command not specified');
1245 }
1246
Andrei Popescu402d9372010-02-26 13:31:12 +00001247 if (request.arguments) {
1248 var args = request.arguments;
1249 // TODO(yurys): remove request.arguments.compactFormat check once
1250 // ChromeDevTools are switched to 'inlineRefs'
1251 if (args.inlineRefs || args.compactFormat) {
1252 response.setOption('inlineRefs', true);
1253 }
1254 if (!IS_UNDEFINED(args.maxStringLength)) {
1255 response.setOption('maxStringLength', args.maxStringLength);
1256 }
Andrei Popescu31002712010-02-23 13:46:05 +00001257 }
1258
1259 if (request.command == 'continue') {
1260 this.continueRequest_(request, response);
1261 } else if (request.command == 'break') {
1262 this.breakRequest_(request, response);
1263 } else if (request.command == 'setbreakpoint') {
1264 this.setBreakPointRequest_(request, response);
1265 } else if (request.command == 'changebreakpoint') {
1266 this.changeBreakPointRequest_(request, response);
1267 } else if (request.command == 'clearbreakpoint') {
1268 this.clearBreakPointRequest_(request, response);
1269 } else if (request.command == 'clearbreakpointgroup') {
1270 this.clearBreakPointGroupRequest_(request, response);
Kristian Monsen25f61362010-05-21 11:50:48 +01001271 } else if (request.command == 'listbreakpoints') {
1272 this.listBreakpointsRequest_(request, response);
Andrei Popescu31002712010-02-23 13:46:05 +00001273 } else if (request.command == 'backtrace') {
1274 this.backtraceRequest_(request, response);
1275 } else if (request.command == 'frame') {
1276 this.frameRequest_(request, response);
1277 } else if (request.command == 'scopes') {
1278 this.scopesRequest_(request, response);
1279 } else if (request.command == 'scope') {
1280 this.scopeRequest_(request, response);
1281 } else if (request.command == 'evaluate') {
1282 this.evaluateRequest_(request, response);
1283 } else if (request.command == 'lookup') {
1284 this.lookupRequest_(request, response);
1285 } else if (request.command == 'references') {
1286 this.referencesRequest_(request, response);
1287 } else if (request.command == 'source') {
1288 this.sourceRequest_(request, response);
1289 } else if (request.command == 'scripts') {
1290 this.scriptsRequest_(request, response);
1291 } else if (request.command == 'threads') {
1292 this.threadsRequest_(request, response);
1293 } else if (request.command == 'suspend') {
1294 this.suspendRequest_(request, response);
1295 } else if (request.command == 'version') {
1296 this.versionRequest_(request, response);
1297 } else if (request.command == 'profile') {
Steve Block6ded16b2010-05-10 14:33:55 +01001298 this.profileRequest_(request, response);
1299 } else if (request.command == 'changelive') {
1300 this.changeLiveRequest_(request, response);
Andrei Popescu31002712010-02-23 13:46:05 +00001301 } else {
1302 throw new Error('Unknown command "' + request.command + '" in request');
1303 }
1304 } catch (e) {
1305 // If there is no response object created one (without command).
1306 if (!response) {
1307 response = this.createResponse();
1308 }
1309 response.success = false;
1310 response.message = %ToString(e);
1311 }
1312
1313 // Return the response as a JSON encoded string.
1314 try {
1315 if (!IS_UNDEFINED(response.running)) {
1316 // Response controls running state.
1317 this.running_ = response.running;
1318 }
Steve Block6ded16b2010-05-10 14:33:55 +01001319 response.running = this.running_;
Andrei Popescu31002712010-02-23 13:46:05 +00001320 return response.toJSONProtocol();
1321 } catch (e) {
1322 // Failed to generate response - return generic error.
1323 return '{"seq":' + response.seq + ',' +
1324 '"request_seq":' + request.seq + ',' +
1325 '"type":"response",' +
1326 '"success":false,' +
1327 '"message":"Internal error: ' + %ToString(e) + '"}';
1328 }
1329 } catch (e) {
1330 // Failed in one of the catch blocks above - most generic error.
1331 return '{"seq":0,"type":"response","success":false,"message":"Internal error"}';
1332 }
1333};
1334
1335
1336DebugCommandProcessor.prototype.continueRequest_ = function(request, response) {
1337 // Check for arguments for continue.
1338 if (request.arguments) {
1339 var count = 1;
1340 var action = Debug.StepAction.StepIn;
1341
1342 // Pull out arguments.
1343 var stepaction = request.arguments.stepaction;
1344 var stepcount = request.arguments.stepcount;
1345
1346 // Get the stepcount argument if any.
1347 if (stepcount) {
1348 count = %ToNumber(stepcount);
1349 if (count < 0) {
1350 throw new Error('Invalid stepcount argument "' + stepcount + '".');
1351 }
1352 }
1353
1354 // Get the stepaction argument.
1355 if (stepaction) {
1356 if (stepaction == 'in') {
1357 action = Debug.StepAction.StepIn;
1358 } else if (stepaction == 'min') {
1359 action = Debug.StepAction.StepMin;
1360 } else if (stepaction == 'next') {
1361 action = Debug.StepAction.StepNext;
1362 } else if (stepaction == 'out') {
1363 action = Debug.StepAction.StepOut;
1364 } else {
1365 throw new Error('Invalid stepaction argument "' + stepaction + '".');
1366 }
1367 }
1368
1369 // Setup the VM for stepping.
1370 this.exec_state_.prepareStep(action, count);
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;
1399 var ignoreCount = request.arguments.ignoreCount;
1400 var groupId = request.arguments.groupId;
1401
1402 // Check for legal arguments.
1403 if (!type || IS_UNDEFINED(target)) {
1404 response.failed('Missing argument "type" or "target"');
1405 return;
1406 }
1407 if (type != 'function' && type != 'handle' &&
1408 type != 'script' && type != 'scriptId') {
1409 response.failed('Illegal type "' + type + '"');
1410 return;
1411 }
1412
1413 // Either function or script break point.
1414 var break_point_number;
1415 if (type == 'function') {
1416 // Handle function break point.
1417 if (!IS_STRING(target)) {
1418 response.failed('Argument "target" is not a string value');
1419 return;
1420 }
1421 var f;
1422 try {
1423 // Find the function through a global evaluate.
1424 f = this.exec_state_.evaluateGlobal(target).value();
1425 } catch (e) {
1426 response.failed('Error: "' + %ToString(e) +
1427 '" evaluating "' + target + '"');
1428 return;
1429 }
1430 if (!IS_FUNCTION(f)) {
1431 response.failed('"' + target + '" does not evaluate to a function');
1432 return;
1433 }
1434
1435 // Set function break point.
1436 break_point_number = Debug.setBreakPoint(f, line, column, condition);
1437 } else if (type == 'handle') {
1438 // Find the object pointed by the specified handle.
1439 var handle = parseInt(target, 10);
1440 var mirror = LookupMirror(handle);
1441 if (!mirror) {
1442 return response.failed('Object #' + handle + '# not found');
1443 }
1444 if (!mirror.isFunction()) {
1445 return response.failed('Object #' + handle + '# is not a function');
1446 }
1447
1448 // Set function break point.
1449 break_point_number = Debug.setBreakPoint(mirror.value(),
1450 line, column, condition);
1451 } else if (type == 'script') {
1452 // set script break point.
1453 break_point_number =
1454 Debug.setScriptBreakPointByName(target, line, column, condition,
1455 groupId);
1456 } else { // type == 'scriptId.
1457 break_point_number =
1458 Debug.setScriptBreakPointById(target, line, column, condition, groupId);
1459 }
1460
1461 // Set additional break point properties.
1462 var break_point = Debug.findBreakPoint(break_point_number);
1463 if (ignoreCount) {
1464 Debug.changeBreakPointIgnoreCount(break_point_number, ignoreCount);
1465 }
1466 if (!enabled) {
1467 Debug.disableBreakPoint(break_point_number);
1468 }
1469
1470 // Add the break point number to the response.
1471 response.body = { type: type,
1472 breakpoint: break_point_number }
1473
1474 // Add break point information to the response.
1475 if (break_point instanceof ScriptBreakPoint) {
1476 if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
1477 response.body.type = 'scriptId';
1478 response.body.script_id = break_point.script_id();
1479 } else {
1480 response.body.type = 'scriptName';
1481 response.body.script_name = break_point.script_name();
1482 }
1483 response.body.line = break_point.line();
1484 response.body.column = break_point.column();
1485 } else {
1486 response.body.type = 'function';
1487 }
1488};
1489
1490
1491DebugCommandProcessor.prototype.changeBreakPointRequest_ = function(request, response) {
1492 // Check for legal request.
1493 if (!request.arguments) {
1494 response.failed('Missing arguments');
1495 return;
1496 }
1497
1498 // Pull out arguments.
1499 var break_point = %ToNumber(request.arguments.breakpoint);
1500 var enabled = request.arguments.enabled;
1501 var condition = request.arguments.condition;
1502 var ignoreCount = request.arguments.ignoreCount;
1503
1504 // Check for legal arguments.
1505 if (!break_point) {
1506 response.failed('Missing argument "breakpoint"');
1507 return;
1508 }
1509
1510 // Change enabled state if supplied.
1511 if (!IS_UNDEFINED(enabled)) {
1512 if (enabled) {
1513 Debug.enableBreakPoint(break_point);
1514 } else {
1515 Debug.disableBreakPoint(break_point);
1516 }
1517 }
1518
1519 // Change condition if supplied
1520 if (!IS_UNDEFINED(condition)) {
1521 Debug.changeBreakPointCondition(break_point, condition);
1522 }
1523
1524 // Change ignore count if supplied
1525 if (!IS_UNDEFINED(ignoreCount)) {
1526 Debug.changeBreakPointIgnoreCount(break_point, ignoreCount);
1527 }
1528}
1529
1530
1531DebugCommandProcessor.prototype.clearBreakPointGroupRequest_ = function(request, response) {
1532 // Check for legal request.
1533 if (!request.arguments) {
1534 response.failed('Missing arguments');
1535 return;
1536 }
1537
1538 // Pull out arguments.
1539 var group_id = request.arguments.groupId;
1540
1541 // Check for legal arguments.
1542 if (!group_id) {
1543 response.failed('Missing argument "groupId"');
1544 return;
1545 }
1546
1547 var cleared_break_points = [];
1548 var new_script_break_points = [];
1549 for (var i = 0; i < script_break_points.length; i++) {
1550 var next_break_point = script_break_points[i];
1551 if (next_break_point.groupId() == group_id) {
1552 cleared_break_points.push(next_break_point.number());
1553 next_break_point.clear();
1554 } else {
1555 new_script_break_points.push(next_break_point);
1556 }
1557 }
1558 script_break_points = new_script_break_points;
1559
1560 // Add the cleared break point numbers to the response.
1561 response.body = { breakpoints: cleared_break_points };
1562}
1563
1564
1565DebugCommandProcessor.prototype.clearBreakPointRequest_ = function(request, response) {
1566 // Check for legal request.
1567 if (!request.arguments) {
1568 response.failed('Missing arguments');
1569 return;
1570 }
1571
1572 // Pull out arguments.
1573 var break_point = %ToNumber(request.arguments.breakpoint);
1574
1575 // Check for legal arguments.
1576 if (!break_point) {
1577 response.failed('Missing argument "breakpoint"');
1578 return;
1579 }
1580
1581 // Clear break point.
1582 Debug.clearBreakPoint(break_point);
1583
1584 // Add the cleared break point number to the response.
1585 response.body = { breakpoint: break_point }
1586}
1587
Kristian Monsen25f61362010-05-21 11:50:48 +01001588DebugCommandProcessor.prototype.listBreakpointsRequest_ = function(request, response) {
1589 var array = [];
1590 for (var i = 0; i < script_break_points.length; i++) {
1591 var break_point = script_break_points[i];
1592
1593 var description = {
1594 number: break_point.number(),
1595 line: break_point.line(),
1596 column: break_point.column(),
1597 groupId: break_point.groupId(),
1598 hit_count: break_point.hit_count(),
1599 active: break_point.active(),
1600 condition: break_point.condition(),
1601 ignoreCount: break_point.ignoreCount()
1602 }
1603
1604 if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
1605 description.type = 'scriptId';
1606 description.script_id = break_point.script_id();
1607 } else {
1608 description.type = 'scriptName';
1609 description.script_name = break_point.script_name();
1610 }
1611 array.push(description);
1612 }
1613
1614 response.body = { breakpoints: array }
1615}
1616
Andrei Popescu31002712010-02-23 13:46:05 +00001617
1618DebugCommandProcessor.prototype.backtraceRequest_ = function(request, response) {
1619 // Get the number of frames.
1620 var total_frames = this.exec_state_.frameCount();
1621
1622 // Create simple response if there are no frames.
1623 if (total_frames == 0) {
1624 response.body = {
1625 totalFrames: total_frames
1626 }
1627 return;
1628 }
1629
1630 // Default frame range to include in backtrace.
1631 var from_index = 0
1632 var to_index = kDefaultBacktraceLength;
1633
1634 // Get the range from the arguments.
1635 if (request.arguments) {
1636 if (request.arguments.fromFrame) {
1637 from_index = request.arguments.fromFrame;
1638 }
1639 if (request.arguments.toFrame) {
1640 to_index = request.arguments.toFrame;
1641 }
1642 if (request.arguments.bottom) {
1643 var tmp_index = total_frames - from_index;
1644 from_index = total_frames - to_index
1645 to_index = tmp_index;
1646 }
1647 if (from_index < 0 || to_index < 0) {
1648 return response.failed('Invalid frame number');
1649 }
1650 }
1651
1652 // Adjust the index.
1653 to_index = Math.min(total_frames, to_index);
1654
1655 if (to_index <= from_index) {
1656 var error = 'Invalid frame range';
1657 return response.failed(error);
1658 }
1659
1660 // Create the response body.
1661 var frames = [];
1662 for (var i = from_index; i < to_index; i++) {
1663 frames.push(this.exec_state_.frame(i));
1664 }
1665 response.body = {
1666 fromFrame: from_index,
1667 toFrame: to_index,
1668 totalFrames: total_frames,
1669 frames: frames
1670 }
1671};
1672
1673
1674DebugCommandProcessor.prototype.backtracec = function(cmd, args) {
1675 return this.exec_state_.cframesValue();
1676};
1677
1678
1679DebugCommandProcessor.prototype.frameRequest_ = function(request, response) {
1680 // No frames no source.
1681 if (this.exec_state_.frameCount() == 0) {
1682 return response.failed('No frames');
1683 }
1684
1685 // With no arguments just keep the selected frame.
1686 if (request.arguments) {
1687 var index = request.arguments.number;
1688 if (index < 0 || this.exec_state_.frameCount() <= index) {
1689 return response.failed('Invalid frame number');
1690 }
1691
1692 this.exec_state_.setSelectedFrame(request.arguments.number);
1693 }
1694 response.body = this.exec_state_.frame();
1695};
1696
1697
1698DebugCommandProcessor.prototype.frameForScopeRequest_ = function(request) {
1699 // Get the frame for which the scope or scopes are requested. With no frameNumber
1700 // argument use the currently selected frame.
1701 if (request.arguments && !IS_UNDEFINED(request.arguments.frameNumber)) {
1702 frame_index = request.arguments.frameNumber;
1703 if (frame_index < 0 || this.exec_state_.frameCount() <= frame_index) {
1704 return response.failed('Invalid frame number');
1705 }
1706 return this.exec_state_.frame(frame_index);
1707 } else {
1708 return this.exec_state_.frame();
1709 }
1710}
1711
1712
1713DebugCommandProcessor.prototype.scopesRequest_ = function(request, response) {
1714 // No frames no scopes.
1715 if (this.exec_state_.frameCount() == 0) {
1716 return response.failed('No scopes');
1717 }
1718
1719 // Get the frame for which the scopes are requested.
1720 var frame = this.frameForScopeRequest_(request);
1721
1722 // Fill all scopes for this frame.
1723 var total_scopes = frame.scopeCount();
1724 var scopes = [];
1725 for (var i = 0; i < total_scopes; i++) {
1726 scopes.push(frame.scope(i));
1727 }
1728 response.body = {
1729 fromScope: 0,
1730 toScope: total_scopes,
1731 totalScopes: total_scopes,
1732 scopes: scopes
1733 }
1734};
1735
1736
1737DebugCommandProcessor.prototype.scopeRequest_ = function(request, response) {
1738 // No frames no scopes.
1739 if (this.exec_state_.frameCount() == 0) {
1740 return response.failed('No scopes');
1741 }
1742
1743 // Get the frame for which the scope is requested.
1744 var frame = this.frameForScopeRequest_(request);
1745
1746 // With no scope argument just return top scope.
1747 var scope_index = 0;
1748 if (request.arguments && !IS_UNDEFINED(request.arguments.number)) {
1749 scope_index = %ToNumber(request.arguments.number);
1750 if (scope_index < 0 || frame.scopeCount() <= scope_index) {
1751 return response.failed('Invalid scope number');
1752 }
1753 }
1754
1755 response.body = frame.scope(scope_index);
1756};
1757
1758
1759DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) {
1760 if (!request.arguments) {
1761 return response.failed('Missing arguments');
1762 }
1763
1764 // Pull out arguments.
1765 var expression = request.arguments.expression;
1766 var frame = request.arguments.frame;
1767 var global = request.arguments.global;
1768 var disable_break = request.arguments.disable_break;
1769
1770 // The expression argument could be an integer so we convert it to a
1771 // string.
1772 try {
1773 expression = String(expression);
1774 } catch(e) {
1775 return response.failed('Failed to convert expression argument to string');
1776 }
1777
1778 // Check for legal arguments.
1779 if (!IS_UNDEFINED(frame) && global) {
1780 return response.failed('Arguments "frame" and "global" are exclusive');
1781 }
1782
1783 // Global evaluate.
1784 if (global) {
1785 // Evaluate in the global context.
1786 response.body =
1787 this.exec_state_.evaluateGlobal(expression, Boolean(disable_break));
1788 return;
1789 }
1790
1791 // Default value for disable_break is true.
1792 if (IS_UNDEFINED(disable_break)) {
1793 disable_break = true;
1794 }
1795
1796 // No frames no evaluate in frame.
1797 if (this.exec_state_.frameCount() == 0) {
1798 return response.failed('No frames');
1799 }
1800
1801 // Check whether a frame was specified.
1802 if (!IS_UNDEFINED(frame)) {
1803 var frame_number = %ToNumber(frame);
1804 if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
1805 return response.failed('Invalid frame "' + frame + '"');
1806 }
1807 // Evaluate in the specified frame.
1808 response.body = this.exec_state_.frame(frame_number).evaluate(
1809 expression, Boolean(disable_break));
1810 return;
1811 } else {
1812 // Evaluate in the selected frame.
1813 response.body = this.exec_state_.frame().evaluate(
1814 expression, Boolean(disable_break));
1815 return;
1816 }
1817};
1818
1819
1820DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) {
1821 if (!request.arguments) {
1822 return response.failed('Missing arguments');
1823 }
1824
1825 // Pull out arguments.
1826 var handles = request.arguments.handles;
1827
1828 // Check for legal arguments.
1829 if (IS_UNDEFINED(handles)) {
1830 return response.failed('Argument "handles" missing');
1831 }
1832
1833 // Set 'includeSource' option for script lookup.
1834 if (!IS_UNDEFINED(request.arguments.includeSource)) {
1835 includeSource = %ToBoolean(request.arguments.includeSource);
1836 response.setOption('includeSource', includeSource);
1837 }
1838
1839 // Lookup handles.
1840 var mirrors = {};
1841 for (var i = 0; i < handles.length; i++) {
1842 var handle = handles[i];
1843 var mirror = LookupMirror(handle);
1844 if (!mirror) {
1845 return response.failed('Object #' + handle + '# not found');
1846 }
1847 mirrors[handle] = mirror;
1848 }
1849 response.body = mirrors;
1850};
1851
1852
1853DebugCommandProcessor.prototype.referencesRequest_ =
1854 function(request, response) {
1855 if (!request.arguments) {
1856 return response.failed('Missing arguments');
1857 }
1858
1859 // Pull out arguments.
1860 var type = request.arguments.type;
1861 var handle = request.arguments.handle;
1862
1863 // Check for legal arguments.
1864 if (IS_UNDEFINED(type)) {
1865 return response.failed('Argument "type" missing');
1866 }
1867 if (IS_UNDEFINED(handle)) {
1868 return response.failed('Argument "handle" missing');
1869 }
1870 if (type != 'referencedBy' && type != 'constructedBy') {
1871 return response.failed('Invalid type "' + type + '"');
1872 }
1873
1874 // Lookup handle and return objects with references the object.
1875 var mirror = LookupMirror(handle);
1876 if (mirror) {
1877 if (type == 'referencedBy') {
1878 response.body = mirror.referencedBy();
1879 } else {
1880 response.body = mirror.constructedBy();
1881 }
1882 } else {
1883 return response.failed('Object #' + handle + '# not found');
1884 }
1885};
1886
1887
1888DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) {
1889 // No frames no source.
1890 if (this.exec_state_.frameCount() == 0) {
1891 return response.failed('No source');
1892 }
1893
1894 var from_line;
1895 var to_line;
1896 var frame = this.exec_state_.frame();
1897 if (request.arguments) {
1898 // Pull out arguments.
1899 from_line = request.arguments.fromLine;
1900 to_line = request.arguments.toLine;
1901
1902 if (!IS_UNDEFINED(request.arguments.frame)) {
1903 var frame_number = %ToNumber(request.arguments.frame);
1904 if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
1905 return response.failed('Invalid frame "' + frame + '"');
1906 }
1907 frame = this.exec_state_.frame(frame_number);
1908 }
1909 }
1910
1911 // Get the script selected.
1912 var script = frame.func().script();
1913 if (!script) {
1914 return response.failed('No source');
1915 }
1916
1917 // Get the source slice and fill it into the response.
1918 var slice = script.sourceSlice(from_line, to_line);
1919 if (!slice) {
1920 return response.failed('Invalid line interval');
1921 }
1922 response.body = {};
1923 response.body.source = slice.sourceText();
1924 response.body.fromLine = slice.from_line;
1925 response.body.toLine = slice.to_line;
1926 response.body.fromPosition = slice.from_position;
1927 response.body.toPosition = slice.to_position;
1928 response.body.totalLines = script.lineCount();
1929};
1930
1931
1932DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) {
1933 var types = ScriptTypeFlag(Debug.ScriptType.Normal);
1934 var includeSource = false;
1935 var idsToInclude = null;
1936 if (request.arguments) {
1937 // Pull out arguments.
1938 if (!IS_UNDEFINED(request.arguments.types)) {
1939 types = %ToNumber(request.arguments.types);
1940 if (isNaN(types) || types < 0) {
1941 return response.failed('Invalid types "' + request.arguments.types + '"');
1942 }
1943 }
Steve Block6ded16b2010-05-10 14:33:55 +01001944
Andrei Popescu31002712010-02-23 13:46:05 +00001945 if (!IS_UNDEFINED(request.arguments.includeSource)) {
1946 includeSource = %ToBoolean(request.arguments.includeSource);
1947 response.setOption('includeSource', includeSource);
1948 }
Steve Block6ded16b2010-05-10 14:33:55 +01001949
Andrei Popescu31002712010-02-23 13:46:05 +00001950 if (IS_ARRAY(request.arguments.ids)) {
1951 idsToInclude = {};
1952 var ids = request.arguments.ids;
1953 for (var i = 0; i < ids.length; i++) {
1954 idsToInclude[ids[i]] = true;
1955 }
1956 }
1957 }
1958
1959 // Collect all scripts in the heap.
1960 var scripts = %DebugGetLoadedScripts();
1961
1962 response.body = [];
1963
1964 for (var i = 0; i < scripts.length; i++) {
1965 if (idsToInclude && !idsToInclude[scripts[i].id]) {
1966 continue;
1967 }
1968 if (types & ScriptTypeFlag(scripts[i].type)) {
1969 response.body.push(MakeMirror(scripts[i]));
1970 }
1971 }
1972};
1973
1974
1975DebugCommandProcessor.prototype.threadsRequest_ = function(request, response) {
1976 // Get the number of threads.
1977 var total_threads = this.exec_state_.threadCount();
1978
1979 // Get information for all threads.
1980 var threads = [];
1981 for (var i = 0; i < total_threads; i++) {
1982 var details = %GetThreadDetails(this.exec_state_.break_id, i);
1983 var thread_info = { current: details[0],
1984 id: details[1]
1985 }
1986 threads.push(thread_info);
1987 }
1988
1989 // Create the response body.
1990 response.body = {
1991 totalThreads: total_threads,
1992 threads: threads
1993 }
1994};
1995
1996
1997DebugCommandProcessor.prototype.suspendRequest_ = function(request, response) {
1998 response.running = false;
1999};
2000
2001
2002DebugCommandProcessor.prototype.versionRequest_ = function(request, response) {
2003 response.body = {
2004 V8Version: %GetV8Version()
2005 }
2006};
2007
2008
2009DebugCommandProcessor.prototype.profileRequest_ = function(request, response) {
2010 if (!request.arguments) {
2011 return response.failed('Missing arguments');
2012 }
2013 var modules = parseInt(request.arguments.modules);
2014 if (isNaN(modules)) {
2015 return response.failed('Modules is not an integer');
2016 }
Andrei Popescu402d9372010-02-26 13:31:12 +00002017 var tag = parseInt(request.arguments.tag);
2018 if (isNaN(tag)) {
2019 tag = 0;
2020 }
Andrei Popescu31002712010-02-23 13:46:05 +00002021 if (request.arguments.command == 'resume') {
Andrei Popescu402d9372010-02-26 13:31:12 +00002022 %ProfilerResume(modules, tag);
Andrei Popescu31002712010-02-23 13:46:05 +00002023 } else if (request.arguments.command == 'pause') {
Andrei Popescu402d9372010-02-26 13:31:12 +00002024 %ProfilerPause(modules, tag);
Andrei Popescu31002712010-02-23 13:46:05 +00002025 } else {
2026 return response.failed('Unknown command');
2027 }
2028 response.body = {};
2029};
2030
2031
Steve Block6ded16b2010-05-10 14:33:55 +01002032DebugCommandProcessor.prototype.changeLiveRequest_ = function(request, response) {
2033 if (!Debug.LiveEdit) {
2034 return response.failed('LiveEdit feature is not supported');
2035 }
2036 if (!request.arguments) {
2037 return response.failed('Missing arguments');
2038 }
2039 var script_id = request.arguments.script_id;
2040
2041 var scripts = %DebugGetLoadedScripts();
2042
2043 var the_script = null;
2044 for (var i = 0; i < scripts.length; i++) {
2045 if (scripts[i].id == script_id) {
2046 the_script = scripts[i];
2047 }
2048 }
2049 if (!the_script) {
2050 response.failed('Script not found');
2051 return;
2052 }
2053
2054 var change_log = new Array();
2055
2056 if (!IS_STRING(request.arguments.new_source)) {
2057 throw "new_source argument expected";
2058 }
2059
2060 var new_source = request.arguments.new_source;
2061
2062 try {
2063 Debug.LiveEdit.SetScriptSource(the_script, new_source, change_log);
2064 } catch (e) {
2065 if (e instanceof Debug.LiveEdit.Failure) {
2066 // Let's treat it as a "success" so that body with change_log will be
2067 // sent back. "change_log" will have "failure" field set.
2068 change_log.push( { failure: true, message: e.toString() } );
2069 } else {
2070 throw e;
2071 }
2072 }
2073 response.body = {change_log: change_log};
2074};
2075
2076
Andrei Popescu31002712010-02-23 13:46:05 +00002077// Check whether the previously processed command caused the VM to become
2078// running.
2079DebugCommandProcessor.prototype.isRunning = function() {
2080 return this.running_;
2081}
2082
2083
2084DebugCommandProcessor.prototype.systemBreak = function(cmd, args) {
2085 return %SystemBreak();
2086};
2087
2088
2089function NumberToHex8Str(n) {
2090 var r = "";
2091 for (var i = 0; i < 8; ++i) {
2092 var c = hexCharArray[n & 0x0F]; // hexCharArray is defined in uri.js
2093 r = c + r;
2094 n = n >>> 4;
2095 }
2096 return r;
2097};
2098
2099DebugCommandProcessor.prototype.formatCFrames = function(cframes_value) {
2100 var result = "";
2101 if (cframes_value == null || cframes_value.length == 0) {
2102 result += "(stack empty)";
2103 } else {
2104 for (var i = 0; i < cframes_value.length; ++i) {
2105 if (i != 0) result += "\n";
2106 result += this.formatCFrame(cframes_value[i]);
2107 }
2108 }
2109 return result;
2110};
2111
2112
2113DebugCommandProcessor.prototype.formatCFrame = function(cframe_value) {
2114 var result = "";
2115 result += "0x" + NumberToHex8Str(cframe_value.address);
2116 if (!IS_UNDEFINED(cframe_value.text)) {
2117 result += " " + cframe_value.text;
2118 }
2119 return result;
2120}
2121
2122
2123/**
2124 * Convert an Object to its debugger protocol representation. The representation
2125 * may be serilized to a JSON object using JSON.stringify().
2126 * This implementation simply runs through all string property names, converts
2127 * each property value to a protocol value and adds the property to the result
2128 * object. For type "object" the function will be called recursively. Note that
2129 * circular structures will cause infinite recursion.
2130 * @param {Object} object The object to format as protocol object.
2131 * @param {MirrorSerializer} mirror_serializer The serializer to use if any
2132 * mirror objects are encountered.
2133 * @return {Object} Protocol object value.
2134 */
2135function ObjectToProtocolObject_(object, mirror_serializer) {
2136 var content = {};
2137 for (var key in object) {
2138 // Only consider string keys.
2139 if (typeof key == 'string') {
2140 // Format the value based on its type.
2141 var property_value_json = ValueToProtocolValue_(object[key],
2142 mirror_serializer);
2143 // Add the property if relevant.
2144 if (!IS_UNDEFINED(property_value_json)) {
2145 content[key] = property_value_json;
2146 }
2147 }
2148 }
Steve Block6ded16b2010-05-10 14:33:55 +01002149
Andrei Popescu31002712010-02-23 13:46:05 +00002150 return content;
2151}
2152
2153
2154/**
2155 * Convert an array to its debugger protocol representation. It will convert
2156 * each array element to a protocol value.
2157 * @param {Array} array The array to format as protocol array.
2158 * @param {MirrorSerializer} mirror_serializer The serializer to use if any
2159 * mirror objects are encountered.
2160 * @return {Array} Protocol array value.
2161 */
2162function ArrayToProtocolArray_(array, mirror_serializer) {
2163 var json = [];
2164 for (var i = 0; i < array.length; i++) {
2165 json.push(ValueToProtocolValue_(array[i], mirror_serializer));
2166 }
2167 return json;
2168}
2169
2170
2171/**
Steve Block6ded16b2010-05-10 14:33:55 +01002172 * Convert a value to its debugger protocol representation.
Andrei Popescu31002712010-02-23 13:46:05 +00002173 * @param {*} value The value to format as protocol value.
2174 * @param {MirrorSerializer} mirror_serializer The serializer to use if any
2175 * mirror objects are encountered.
2176 * @return {*} Protocol value.
2177 */
2178function ValueToProtocolValue_(value, mirror_serializer) {
2179 // Format the value based on its type.
2180 var json;
2181 switch (typeof value) {
2182 case 'object':
2183 if (value instanceof Mirror) {
2184 json = mirror_serializer.serializeValue(value);
2185 } else if (IS_ARRAY(value)){
2186 json = ArrayToProtocolArray_(value, mirror_serializer);
2187 } else {
2188 json = ObjectToProtocolObject_(value, mirror_serializer);
2189 }
2190 break;
2191
2192 case 'boolean':
2193 case 'string':
2194 case 'number':
2195 json = value;
2196 break
2197
2198 default:
2199 json = null;
2200 }
2201 return json;
2202}