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