blob: 77fa1ddd65e2c3623540e6b1acb399470ff0cc98 [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);
Kristian Monsen25f61362010-05-21 11:50:48 +01001269 } else if (request.command == 'listbreakpoints') {
1270 this.listBreakpointsRequest_(request, response);
Andrei Popescu31002712010-02-23 13:46:05 +00001271 } else if (request.command == 'backtrace') {
1272 this.backtraceRequest_(request, response);
1273 } else if (request.command == 'frame') {
1274 this.frameRequest_(request, response);
1275 } else if (request.command == 'scopes') {
1276 this.scopesRequest_(request, response);
1277 } else if (request.command == 'scope') {
1278 this.scopeRequest_(request, response);
1279 } else if (request.command == 'evaluate') {
1280 this.evaluateRequest_(request, response);
1281 } else if (request.command == 'lookup') {
1282 this.lookupRequest_(request, response);
1283 } else if (request.command == 'references') {
1284 this.referencesRequest_(request, response);
1285 } else if (request.command == 'source') {
1286 this.sourceRequest_(request, response);
1287 } else if (request.command == 'scripts') {
1288 this.scriptsRequest_(request, response);
1289 } else if (request.command == 'threads') {
1290 this.threadsRequest_(request, response);
1291 } else if (request.command == 'suspend') {
1292 this.suspendRequest_(request, response);
1293 } else if (request.command == 'version') {
1294 this.versionRequest_(request, response);
1295 } else if (request.command == 'profile') {
Steve Block6ded16b2010-05-10 14:33:55 +01001296 this.profileRequest_(request, response);
1297 } else if (request.command == 'changelive') {
1298 this.changeLiveRequest_(request, response);
Andrei Popescu31002712010-02-23 13:46:05 +00001299 } else {
1300 throw new Error('Unknown command "' + request.command + '" in request');
1301 }
1302 } catch (e) {
1303 // If there is no response object created one (without command).
1304 if (!response) {
1305 response = this.createResponse();
1306 }
1307 response.success = false;
1308 response.message = %ToString(e);
1309 }
1310
1311 // Return the response as a JSON encoded string.
1312 try {
1313 if (!IS_UNDEFINED(response.running)) {
1314 // Response controls running state.
1315 this.running_ = response.running;
1316 }
Steve Block6ded16b2010-05-10 14:33:55 +01001317 response.running = this.running_;
Andrei Popescu31002712010-02-23 13:46:05 +00001318 return response.toJSONProtocol();
1319 } catch (e) {
1320 // Failed to generate response - return generic error.
1321 return '{"seq":' + response.seq + ',' +
1322 '"request_seq":' + request.seq + ',' +
1323 '"type":"response",' +
1324 '"success":false,' +
1325 '"message":"Internal error: ' + %ToString(e) + '"}';
1326 }
1327 } catch (e) {
1328 // Failed in one of the catch blocks above - most generic error.
1329 return '{"seq":0,"type":"response","success":false,"message":"Internal error"}';
1330 }
1331};
1332
1333
1334DebugCommandProcessor.prototype.continueRequest_ = function(request, response) {
1335 // Check for arguments for continue.
1336 if (request.arguments) {
1337 var count = 1;
1338 var action = Debug.StepAction.StepIn;
1339
1340 // Pull out arguments.
1341 var stepaction = request.arguments.stepaction;
1342 var stepcount = request.arguments.stepcount;
1343
1344 // Get the stepcount argument if any.
1345 if (stepcount) {
1346 count = %ToNumber(stepcount);
1347 if (count < 0) {
1348 throw new Error('Invalid stepcount argument "' + stepcount + '".');
1349 }
1350 }
1351
1352 // Get the stepaction argument.
1353 if (stepaction) {
1354 if (stepaction == 'in') {
1355 action = Debug.StepAction.StepIn;
1356 } else if (stepaction == 'min') {
1357 action = Debug.StepAction.StepMin;
1358 } else if (stepaction == 'next') {
1359 action = Debug.StepAction.StepNext;
1360 } else if (stepaction == 'out') {
1361 action = Debug.StepAction.StepOut;
1362 } else {
1363 throw new Error('Invalid stepaction argument "' + stepaction + '".');
1364 }
1365 }
1366
1367 // Setup the VM for stepping.
1368 this.exec_state_.prepareStep(action, count);
1369 }
1370
1371 // VM should be running after executing this request.
1372 response.running = true;
1373};
1374
1375
1376DebugCommandProcessor.prototype.breakRequest_ = function(request, response) {
1377 // Ignore as break command does not do anything when broken.
1378};
1379
1380
1381DebugCommandProcessor.prototype.setBreakPointRequest_ =
1382 function(request, response) {
1383 // Check for legal request.
1384 if (!request.arguments) {
1385 response.failed('Missing arguments');
1386 return;
1387 }
1388
1389 // Pull out arguments.
1390 var type = request.arguments.type;
1391 var target = request.arguments.target;
1392 var line = request.arguments.line;
1393 var column = request.arguments.column;
1394 var enabled = IS_UNDEFINED(request.arguments.enabled) ?
1395 true : request.arguments.enabled;
1396 var condition = request.arguments.condition;
1397 var ignoreCount = request.arguments.ignoreCount;
1398 var groupId = request.arguments.groupId;
1399
1400 // Check for legal arguments.
1401 if (!type || IS_UNDEFINED(target)) {
1402 response.failed('Missing argument "type" or "target"');
1403 return;
1404 }
1405 if (type != 'function' && type != 'handle' &&
1406 type != 'script' && type != 'scriptId') {
1407 response.failed('Illegal type "' + type + '"');
1408 return;
1409 }
1410
1411 // Either function or script break point.
1412 var break_point_number;
1413 if (type == 'function') {
1414 // Handle function break point.
1415 if (!IS_STRING(target)) {
1416 response.failed('Argument "target" is not a string value');
1417 return;
1418 }
1419 var f;
1420 try {
1421 // Find the function through a global evaluate.
1422 f = this.exec_state_.evaluateGlobal(target).value();
1423 } catch (e) {
1424 response.failed('Error: "' + %ToString(e) +
1425 '" evaluating "' + target + '"');
1426 return;
1427 }
1428 if (!IS_FUNCTION(f)) {
1429 response.failed('"' + target + '" does not evaluate to a function');
1430 return;
1431 }
1432
1433 // Set function break point.
1434 break_point_number = Debug.setBreakPoint(f, line, column, condition);
1435 } else if (type == 'handle') {
1436 // Find the object pointed by the specified handle.
1437 var handle = parseInt(target, 10);
1438 var mirror = LookupMirror(handle);
1439 if (!mirror) {
1440 return response.failed('Object #' + handle + '# not found');
1441 }
1442 if (!mirror.isFunction()) {
1443 return response.failed('Object #' + handle + '# is not a function');
1444 }
1445
1446 // Set function break point.
1447 break_point_number = Debug.setBreakPoint(mirror.value(),
1448 line, column, condition);
1449 } else if (type == 'script') {
1450 // set script break point.
1451 break_point_number =
1452 Debug.setScriptBreakPointByName(target, line, column, condition,
1453 groupId);
1454 } else { // type == 'scriptId.
1455 break_point_number =
1456 Debug.setScriptBreakPointById(target, line, column, condition, groupId);
1457 }
1458
1459 // Set additional break point properties.
1460 var break_point = Debug.findBreakPoint(break_point_number);
1461 if (ignoreCount) {
1462 Debug.changeBreakPointIgnoreCount(break_point_number, ignoreCount);
1463 }
1464 if (!enabled) {
1465 Debug.disableBreakPoint(break_point_number);
1466 }
1467
1468 // Add the break point number to the response.
1469 response.body = { type: type,
1470 breakpoint: break_point_number }
1471
1472 // Add break point information to the response.
1473 if (break_point instanceof ScriptBreakPoint) {
1474 if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
1475 response.body.type = 'scriptId';
1476 response.body.script_id = break_point.script_id();
1477 } else {
1478 response.body.type = 'scriptName';
1479 response.body.script_name = break_point.script_name();
1480 }
1481 response.body.line = break_point.line();
1482 response.body.column = break_point.column();
1483 } else {
1484 response.body.type = 'function';
1485 }
1486};
1487
1488
1489DebugCommandProcessor.prototype.changeBreakPointRequest_ = function(request, response) {
1490 // Check for legal request.
1491 if (!request.arguments) {
1492 response.failed('Missing arguments');
1493 return;
1494 }
1495
1496 // Pull out arguments.
1497 var break_point = %ToNumber(request.arguments.breakpoint);
1498 var enabled = request.arguments.enabled;
1499 var condition = request.arguments.condition;
1500 var ignoreCount = request.arguments.ignoreCount;
1501
1502 // Check for legal arguments.
1503 if (!break_point) {
1504 response.failed('Missing argument "breakpoint"');
1505 return;
1506 }
1507
1508 // Change enabled state if supplied.
1509 if (!IS_UNDEFINED(enabled)) {
1510 if (enabled) {
1511 Debug.enableBreakPoint(break_point);
1512 } else {
1513 Debug.disableBreakPoint(break_point);
1514 }
1515 }
1516
1517 // Change condition if supplied
1518 if (!IS_UNDEFINED(condition)) {
1519 Debug.changeBreakPointCondition(break_point, condition);
1520 }
1521
1522 // Change ignore count if supplied
1523 if (!IS_UNDEFINED(ignoreCount)) {
1524 Debug.changeBreakPointIgnoreCount(break_point, ignoreCount);
1525 }
1526}
1527
1528
1529DebugCommandProcessor.prototype.clearBreakPointGroupRequest_ = function(request, response) {
1530 // Check for legal request.
1531 if (!request.arguments) {
1532 response.failed('Missing arguments');
1533 return;
1534 }
1535
1536 // Pull out arguments.
1537 var group_id = request.arguments.groupId;
1538
1539 // Check for legal arguments.
1540 if (!group_id) {
1541 response.failed('Missing argument "groupId"');
1542 return;
1543 }
1544
1545 var cleared_break_points = [];
1546 var new_script_break_points = [];
1547 for (var i = 0; i < script_break_points.length; i++) {
1548 var next_break_point = script_break_points[i];
1549 if (next_break_point.groupId() == group_id) {
1550 cleared_break_points.push(next_break_point.number());
1551 next_break_point.clear();
1552 } else {
1553 new_script_break_points.push(next_break_point);
1554 }
1555 }
1556 script_break_points = new_script_break_points;
1557
1558 // Add the cleared break point numbers to the response.
1559 response.body = { breakpoints: cleared_break_points };
1560}
1561
1562
1563DebugCommandProcessor.prototype.clearBreakPointRequest_ = function(request, response) {
1564 // Check for legal request.
1565 if (!request.arguments) {
1566 response.failed('Missing arguments');
1567 return;
1568 }
1569
1570 // Pull out arguments.
1571 var break_point = %ToNumber(request.arguments.breakpoint);
1572
1573 // Check for legal arguments.
1574 if (!break_point) {
1575 response.failed('Missing argument "breakpoint"');
1576 return;
1577 }
1578
1579 // Clear break point.
1580 Debug.clearBreakPoint(break_point);
1581
1582 // Add the cleared break point number to the response.
1583 response.body = { breakpoint: break_point }
1584}
1585
Kristian Monsen25f61362010-05-21 11:50:48 +01001586DebugCommandProcessor.prototype.listBreakpointsRequest_ = function(request, response) {
1587 var array = [];
1588 for (var i = 0; i < script_break_points.length; i++) {
1589 var break_point = script_break_points[i];
1590
1591 var description = {
1592 number: break_point.number(),
1593 line: break_point.line(),
1594 column: break_point.column(),
1595 groupId: break_point.groupId(),
1596 hit_count: break_point.hit_count(),
1597 active: break_point.active(),
1598 condition: break_point.condition(),
1599 ignoreCount: break_point.ignoreCount()
1600 }
1601
1602 if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
1603 description.type = 'scriptId';
1604 description.script_id = break_point.script_id();
1605 } else {
1606 description.type = 'scriptName';
1607 description.script_name = break_point.script_name();
1608 }
1609 array.push(description);
1610 }
1611
1612 response.body = { breakpoints: array }
1613}
1614
Andrei Popescu31002712010-02-23 13:46:05 +00001615
1616DebugCommandProcessor.prototype.backtraceRequest_ = function(request, response) {
1617 // Get the number of frames.
1618 var total_frames = this.exec_state_.frameCount();
1619
1620 // Create simple response if there are no frames.
1621 if (total_frames == 0) {
1622 response.body = {
1623 totalFrames: total_frames
1624 }
1625 return;
1626 }
1627
1628 // Default frame range to include in backtrace.
1629 var from_index = 0
1630 var to_index = kDefaultBacktraceLength;
1631
1632 // Get the range from the arguments.
1633 if (request.arguments) {
1634 if (request.arguments.fromFrame) {
1635 from_index = request.arguments.fromFrame;
1636 }
1637 if (request.arguments.toFrame) {
1638 to_index = request.arguments.toFrame;
1639 }
1640 if (request.arguments.bottom) {
1641 var tmp_index = total_frames - from_index;
1642 from_index = total_frames - to_index
1643 to_index = tmp_index;
1644 }
1645 if (from_index < 0 || to_index < 0) {
1646 return response.failed('Invalid frame number');
1647 }
1648 }
1649
1650 // Adjust the index.
1651 to_index = Math.min(total_frames, to_index);
1652
1653 if (to_index <= from_index) {
1654 var error = 'Invalid frame range';
1655 return response.failed(error);
1656 }
1657
1658 // Create the response body.
1659 var frames = [];
1660 for (var i = from_index; i < to_index; i++) {
1661 frames.push(this.exec_state_.frame(i));
1662 }
1663 response.body = {
1664 fromFrame: from_index,
1665 toFrame: to_index,
1666 totalFrames: total_frames,
1667 frames: frames
1668 }
1669};
1670
1671
1672DebugCommandProcessor.prototype.backtracec = function(cmd, args) {
1673 return this.exec_state_.cframesValue();
1674};
1675
1676
1677DebugCommandProcessor.prototype.frameRequest_ = function(request, response) {
1678 // No frames no source.
1679 if (this.exec_state_.frameCount() == 0) {
1680 return response.failed('No frames');
1681 }
1682
1683 // With no arguments just keep the selected frame.
1684 if (request.arguments) {
1685 var index = request.arguments.number;
1686 if (index < 0 || this.exec_state_.frameCount() <= index) {
1687 return response.failed('Invalid frame number');
1688 }
1689
1690 this.exec_state_.setSelectedFrame(request.arguments.number);
1691 }
1692 response.body = this.exec_state_.frame();
1693};
1694
1695
1696DebugCommandProcessor.prototype.frameForScopeRequest_ = function(request) {
1697 // Get the frame for which the scope or scopes are requested. With no frameNumber
1698 // argument use the currently selected frame.
1699 if (request.arguments && !IS_UNDEFINED(request.arguments.frameNumber)) {
1700 frame_index = request.arguments.frameNumber;
1701 if (frame_index < 0 || this.exec_state_.frameCount() <= frame_index) {
1702 return response.failed('Invalid frame number');
1703 }
1704 return this.exec_state_.frame(frame_index);
1705 } else {
1706 return this.exec_state_.frame();
1707 }
1708}
1709
1710
1711DebugCommandProcessor.prototype.scopesRequest_ = function(request, response) {
1712 // No frames no scopes.
1713 if (this.exec_state_.frameCount() == 0) {
1714 return response.failed('No scopes');
1715 }
1716
1717 // Get the frame for which the scopes are requested.
1718 var frame = this.frameForScopeRequest_(request);
1719
1720 // Fill all scopes for this frame.
1721 var total_scopes = frame.scopeCount();
1722 var scopes = [];
1723 for (var i = 0; i < total_scopes; i++) {
1724 scopes.push(frame.scope(i));
1725 }
1726 response.body = {
1727 fromScope: 0,
1728 toScope: total_scopes,
1729 totalScopes: total_scopes,
1730 scopes: scopes
1731 }
1732};
1733
1734
1735DebugCommandProcessor.prototype.scopeRequest_ = function(request, response) {
1736 // No frames no scopes.
1737 if (this.exec_state_.frameCount() == 0) {
1738 return response.failed('No scopes');
1739 }
1740
1741 // Get the frame for which the scope is requested.
1742 var frame = this.frameForScopeRequest_(request);
1743
1744 // With no scope argument just return top scope.
1745 var scope_index = 0;
1746 if (request.arguments && !IS_UNDEFINED(request.arguments.number)) {
1747 scope_index = %ToNumber(request.arguments.number);
1748 if (scope_index < 0 || frame.scopeCount() <= scope_index) {
1749 return response.failed('Invalid scope number');
1750 }
1751 }
1752
1753 response.body = frame.scope(scope_index);
1754};
1755
1756
1757DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) {
1758 if (!request.arguments) {
1759 return response.failed('Missing arguments');
1760 }
1761
1762 // Pull out arguments.
1763 var expression = request.arguments.expression;
1764 var frame = request.arguments.frame;
1765 var global = request.arguments.global;
1766 var disable_break = request.arguments.disable_break;
1767
1768 // The expression argument could be an integer so we convert it to a
1769 // string.
1770 try {
1771 expression = String(expression);
1772 } catch(e) {
1773 return response.failed('Failed to convert expression argument to string');
1774 }
1775
1776 // Check for legal arguments.
1777 if (!IS_UNDEFINED(frame) && global) {
1778 return response.failed('Arguments "frame" and "global" are exclusive');
1779 }
1780
1781 // Global evaluate.
1782 if (global) {
1783 // Evaluate in the global context.
1784 response.body =
1785 this.exec_state_.evaluateGlobal(expression, Boolean(disable_break));
1786 return;
1787 }
1788
1789 // Default value for disable_break is true.
1790 if (IS_UNDEFINED(disable_break)) {
1791 disable_break = true;
1792 }
1793
1794 // No frames no evaluate in frame.
1795 if (this.exec_state_.frameCount() == 0) {
1796 return response.failed('No frames');
1797 }
1798
1799 // Check whether a frame was specified.
1800 if (!IS_UNDEFINED(frame)) {
1801 var frame_number = %ToNumber(frame);
1802 if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
1803 return response.failed('Invalid frame "' + frame + '"');
1804 }
1805 // Evaluate in the specified frame.
1806 response.body = this.exec_state_.frame(frame_number).evaluate(
1807 expression, Boolean(disable_break));
1808 return;
1809 } else {
1810 // Evaluate in the selected frame.
1811 response.body = this.exec_state_.frame().evaluate(
1812 expression, Boolean(disable_break));
1813 return;
1814 }
1815};
1816
1817
1818DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) {
1819 if (!request.arguments) {
1820 return response.failed('Missing arguments');
1821 }
1822
1823 // Pull out arguments.
1824 var handles = request.arguments.handles;
1825
1826 // Check for legal arguments.
1827 if (IS_UNDEFINED(handles)) {
1828 return response.failed('Argument "handles" missing');
1829 }
1830
1831 // Set 'includeSource' option for script lookup.
1832 if (!IS_UNDEFINED(request.arguments.includeSource)) {
1833 includeSource = %ToBoolean(request.arguments.includeSource);
1834 response.setOption('includeSource', includeSource);
1835 }
1836
1837 // Lookup handles.
1838 var mirrors = {};
1839 for (var i = 0; i < handles.length; i++) {
1840 var handle = handles[i];
1841 var mirror = LookupMirror(handle);
1842 if (!mirror) {
1843 return response.failed('Object #' + handle + '# not found');
1844 }
1845 mirrors[handle] = mirror;
1846 }
1847 response.body = mirrors;
1848};
1849
1850
1851DebugCommandProcessor.prototype.referencesRequest_ =
1852 function(request, response) {
1853 if (!request.arguments) {
1854 return response.failed('Missing arguments');
1855 }
1856
1857 // Pull out arguments.
1858 var type = request.arguments.type;
1859 var handle = request.arguments.handle;
1860
1861 // Check for legal arguments.
1862 if (IS_UNDEFINED(type)) {
1863 return response.failed('Argument "type" missing');
1864 }
1865 if (IS_UNDEFINED(handle)) {
1866 return response.failed('Argument "handle" missing');
1867 }
1868 if (type != 'referencedBy' && type != 'constructedBy') {
1869 return response.failed('Invalid type "' + type + '"');
1870 }
1871
1872 // Lookup handle and return objects with references the object.
1873 var mirror = LookupMirror(handle);
1874 if (mirror) {
1875 if (type == 'referencedBy') {
1876 response.body = mirror.referencedBy();
1877 } else {
1878 response.body = mirror.constructedBy();
1879 }
1880 } else {
1881 return response.failed('Object #' + handle + '# not found');
1882 }
1883};
1884
1885
1886DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) {
1887 // No frames no source.
1888 if (this.exec_state_.frameCount() == 0) {
1889 return response.failed('No source');
1890 }
1891
1892 var from_line;
1893 var to_line;
1894 var frame = this.exec_state_.frame();
1895 if (request.arguments) {
1896 // Pull out arguments.
1897 from_line = request.arguments.fromLine;
1898 to_line = request.arguments.toLine;
1899
1900 if (!IS_UNDEFINED(request.arguments.frame)) {
1901 var frame_number = %ToNumber(request.arguments.frame);
1902 if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
1903 return response.failed('Invalid frame "' + frame + '"');
1904 }
1905 frame = this.exec_state_.frame(frame_number);
1906 }
1907 }
1908
1909 // Get the script selected.
1910 var script = frame.func().script();
1911 if (!script) {
1912 return response.failed('No source');
1913 }
1914
1915 // Get the source slice and fill it into the response.
1916 var slice = script.sourceSlice(from_line, to_line);
1917 if (!slice) {
1918 return response.failed('Invalid line interval');
1919 }
1920 response.body = {};
1921 response.body.source = slice.sourceText();
1922 response.body.fromLine = slice.from_line;
1923 response.body.toLine = slice.to_line;
1924 response.body.fromPosition = slice.from_position;
1925 response.body.toPosition = slice.to_position;
1926 response.body.totalLines = script.lineCount();
1927};
1928
1929
1930DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) {
1931 var types = ScriptTypeFlag(Debug.ScriptType.Normal);
1932 var includeSource = false;
1933 var idsToInclude = null;
1934 if (request.arguments) {
1935 // Pull out arguments.
1936 if (!IS_UNDEFINED(request.arguments.types)) {
1937 types = %ToNumber(request.arguments.types);
1938 if (isNaN(types) || types < 0) {
1939 return response.failed('Invalid types "' + request.arguments.types + '"');
1940 }
1941 }
Steve Block6ded16b2010-05-10 14:33:55 +01001942
Andrei Popescu31002712010-02-23 13:46:05 +00001943 if (!IS_UNDEFINED(request.arguments.includeSource)) {
1944 includeSource = %ToBoolean(request.arguments.includeSource);
1945 response.setOption('includeSource', includeSource);
1946 }
Steve Block6ded16b2010-05-10 14:33:55 +01001947
Andrei Popescu31002712010-02-23 13:46:05 +00001948 if (IS_ARRAY(request.arguments.ids)) {
1949 idsToInclude = {};
1950 var ids = request.arguments.ids;
1951 for (var i = 0; i < ids.length; i++) {
1952 idsToInclude[ids[i]] = true;
1953 }
1954 }
1955 }
1956
1957 // Collect all scripts in the heap.
1958 var scripts = %DebugGetLoadedScripts();
1959
1960 response.body = [];
1961
1962 for (var i = 0; i < scripts.length; i++) {
1963 if (idsToInclude && !idsToInclude[scripts[i].id]) {
1964 continue;
1965 }
1966 if (types & ScriptTypeFlag(scripts[i].type)) {
1967 response.body.push(MakeMirror(scripts[i]));
1968 }
1969 }
1970};
1971
1972
1973DebugCommandProcessor.prototype.threadsRequest_ = function(request, response) {
1974 // Get the number of threads.
1975 var total_threads = this.exec_state_.threadCount();
1976
1977 // Get information for all threads.
1978 var threads = [];
1979 for (var i = 0; i < total_threads; i++) {
1980 var details = %GetThreadDetails(this.exec_state_.break_id, i);
1981 var thread_info = { current: details[0],
1982 id: details[1]
1983 }
1984 threads.push(thread_info);
1985 }
1986
1987 // Create the response body.
1988 response.body = {
1989 totalThreads: total_threads,
1990 threads: threads
1991 }
1992};
1993
1994
1995DebugCommandProcessor.prototype.suspendRequest_ = function(request, response) {
1996 response.running = false;
1997};
1998
1999
2000DebugCommandProcessor.prototype.versionRequest_ = function(request, response) {
2001 response.body = {
2002 V8Version: %GetV8Version()
2003 }
2004};
2005
2006
2007DebugCommandProcessor.prototype.profileRequest_ = function(request, response) {
2008 if (!request.arguments) {
2009 return response.failed('Missing arguments');
2010 }
2011 var modules = parseInt(request.arguments.modules);
2012 if (isNaN(modules)) {
2013 return response.failed('Modules is not an integer');
2014 }
Andrei Popescu402d9372010-02-26 13:31:12 +00002015 var tag = parseInt(request.arguments.tag);
2016 if (isNaN(tag)) {
2017 tag = 0;
2018 }
Andrei Popescu31002712010-02-23 13:46:05 +00002019 if (request.arguments.command == 'resume') {
Andrei Popescu402d9372010-02-26 13:31:12 +00002020 %ProfilerResume(modules, tag);
Andrei Popescu31002712010-02-23 13:46:05 +00002021 } else if (request.arguments.command == 'pause') {
Andrei Popescu402d9372010-02-26 13:31:12 +00002022 %ProfilerPause(modules, tag);
Andrei Popescu31002712010-02-23 13:46:05 +00002023 } else {
2024 return response.failed('Unknown command');
2025 }
2026 response.body = {};
2027};
2028
2029
Steve Block6ded16b2010-05-10 14:33:55 +01002030DebugCommandProcessor.prototype.changeLiveRequest_ = function(request, response) {
2031 if (!Debug.LiveEdit) {
2032 return response.failed('LiveEdit feature is not supported');
2033 }
2034 if (!request.arguments) {
2035 return response.failed('Missing arguments');
2036 }
2037 var script_id = request.arguments.script_id;
2038
2039 var scripts = %DebugGetLoadedScripts();
2040
2041 var the_script = null;
2042 for (var i = 0; i < scripts.length; i++) {
2043 if (scripts[i].id == script_id) {
2044 the_script = scripts[i];
2045 }
2046 }
2047 if (!the_script) {
2048 response.failed('Script not found');
2049 return;
2050 }
2051
2052 var change_log = new Array();
2053
2054 if (!IS_STRING(request.arguments.new_source)) {
2055 throw "new_source argument expected";
2056 }
2057
2058 var new_source = request.arguments.new_source;
2059
2060 try {
2061 Debug.LiveEdit.SetScriptSource(the_script, new_source, change_log);
2062 } catch (e) {
2063 if (e instanceof Debug.LiveEdit.Failure) {
2064 // Let's treat it as a "success" so that body with change_log will be
2065 // sent back. "change_log" will have "failure" field set.
2066 change_log.push( { failure: true, message: e.toString() } );
2067 } else {
2068 throw e;
2069 }
2070 }
2071 response.body = {change_log: change_log};
2072};
2073
2074
Andrei Popescu31002712010-02-23 13:46:05 +00002075// Check whether the previously processed command caused the VM to become
2076// running.
2077DebugCommandProcessor.prototype.isRunning = function() {
2078 return this.running_;
2079}
2080
2081
2082DebugCommandProcessor.prototype.systemBreak = function(cmd, args) {
2083 return %SystemBreak();
2084};
2085
2086
2087function NumberToHex8Str(n) {
2088 var r = "";
2089 for (var i = 0; i < 8; ++i) {
2090 var c = hexCharArray[n & 0x0F]; // hexCharArray is defined in uri.js
2091 r = c + r;
2092 n = n >>> 4;
2093 }
2094 return r;
2095};
2096
2097DebugCommandProcessor.prototype.formatCFrames = function(cframes_value) {
2098 var result = "";
2099 if (cframes_value == null || cframes_value.length == 0) {
2100 result += "(stack empty)";
2101 } else {
2102 for (var i = 0; i < cframes_value.length; ++i) {
2103 if (i != 0) result += "\n";
2104 result += this.formatCFrame(cframes_value[i]);
2105 }
2106 }
2107 return result;
2108};
2109
2110
2111DebugCommandProcessor.prototype.formatCFrame = function(cframe_value) {
2112 var result = "";
2113 result += "0x" + NumberToHex8Str(cframe_value.address);
2114 if (!IS_UNDEFINED(cframe_value.text)) {
2115 result += " " + cframe_value.text;
2116 }
2117 return result;
2118}
2119
2120
2121/**
2122 * Convert an Object to its debugger protocol representation. The representation
2123 * may be serilized to a JSON object using JSON.stringify().
2124 * This implementation simply runs through all string property names, converts
2125 * each property value to a protocol value and adds the property to the result
2126 * object. For type "object" the function will be called recursively. Note that
2127 * circular structures will cause infinite recursion.
2128 * @param {Object} object The object to format as protocol object.
2129 * @param {MirrorSerializer} mirror_serializer The serializer to use if any
2130 * mirror objects are encountered.
2131 * @return {Object} Protocol object value.
2132 */
2133function ObjectToProtocolObject_(object, mirror_serializer) {
2134 var content = {};
2135 for (var key in object) {
2136 // Only consider string keys.
2137 if (typeof key == 'string') {
2138 // Format the value based on its type.
2139 var property_value_json = ValueToProtocolValue_(object[key],
2140 mirror_serializer);
2141 // Add the property if relevant.
2142 if (!IS_UNDEFINED(property_value_json)) {
2143 content[key] = property_value_json;
2144 }
2145 }
2146 }
Steve Block6ded16b2010-05-10 14:33:55 +01002147
Andrei Popescu31002712010-02-23 13:46:05 +00002148 return content;
2149}
2150
2151
2152/**
2153 * Convert an array to its debugger protocol representation. It will convert
2154 * each array element to a protocol value.
2155 * @param {Array} array The array to format as protocol array.
2156 * @param {MirrorSerializer} mirror_serializer The serializer to use if any
2157 * mirror objects are encountered.
2158 * @return {Array} Protocol array value.
2159 */
2160function ArrayToProtocolArray_(array, mirror_serializer) {
2161 var json = [];
2162 for (var i = 0; i < array.length; i++) {
2163 json.push(ValueToProtocolValue_(array[i], mirror_serializer));
2164 }
2165 return json;
2166}
2167
2168
2169/**
Steve Block6ded16b2010-05-10 14:33:55 +01002170 * Convert a value to its debugger protocol representation.
Andrei Popescu31002712010-02-23 13:46:05 +00002171 * @param {*} value The value to format as protocol value.
2172 * @param {MirrorSerializer} mirror_serializer The serializer to use if any
2173 * mirror objects are encountered.
2174 * @return {*} Protocol value.
2175 */
2176function ValueToProtocolValue_(value, mirror_serializer) {
2177 // Format the value based on its type.
2178 var json;
2179 switch (typeof value) {
2180 case 'object':
2181 if (value instanceof Mirror) {
2182 json = mirror_serializer.serializeValue(value);
2183 } else if (IS_ARRAY(value)){
2184 json = ArrayToProtocolArray_(value, mirror_serializer);
2185 } else {
2186 json = ObjectToProtocolObject_(value, mirror_serializer);
2187 }
2188 break;
2189
2190 case 'boolean':
2191 case 'string':
2192 case 'number':
2193 json = value;
2194 break
2195
2196 default:
2197 json = null;
2198 }
2199 return json;
2200}