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