blob: 17b82b573dd32f814de973acb8e92d97142134f8 [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// 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 = /^(?:[ \v\h]*(?:\/\*.*?\*\/)*)*/;
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
46// Types of exceptions that can be broken upon.
47Debug.ExceptionBreak = { All : 0,
48 Uncaught: 1 };
49
50// The different types of steps.
51Debug.StepAction = { StepOut: 0,
52 StepNext: 1,
53 StepIn: 2,
54 StepMin: 3,
55 StepInMin: 4 };
56
57// The different types of scripts matching enum ScriptType in objects.h.
58Debug.ScriptType = { Native: 0,
59 Extension: 1,
60 Normal: 2 };
61
62function ScriptTypeFlag(type) {
63 return (1 << type);
64}
65
66// Globals.
67var next_response_seq = 0;
68var next_break_point_number = 1;
69var break_points = [];
70var script_break_points = [];
71
72
73// Create a new break point object and add it to the list of break points.
74function MakeBreakPoint(source_position, opt_line, opt_column, opt_script_break_point) {
75 var break_point = new BreakPoint(source_position, opt_line, opt_column, opt_script_break_point);
76 break_points.push(break_point);
77 return break_point;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000078}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000079
80
81// Object representing a break point.
82// NOTE: This object does not have a reference to the function having break
83// point as this would cause function not to be garbage collected when it is
84// not used any more. We do not want break points to keep functions alive.
85function BreakPoint(source_position, opt_line, opt_column, opt_script_break_point) {
86 this.source_position_ = source_position;
87 this.source_line_ = opt_line;
88 this.source_column_ = opt_column;
89 if (opt_script_break_point) {
90 this.script_break_point_ = opt_script_break_point;
91 } else {
92 this.number_ = next_break_point_number++;
93 }
94 this.hit_count_ = 0;
95 this.active_ = true;
96 this.condition_ = null;
97 this.ignoreCount_ = 0;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000098}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000099
100
101BreakPoint.prototype.number = function() {
102 return this.number_;
103};
104
105
106BreakPoint.prototype.func = function() {
107 return this.func_;
108};
109
110
111BreakPoint.prototype.source_position = function() {
112 return this.source_position_;
113};
114
115
116BreakPoint.prototype.hit_count = function() {
117 return this.hit_count_;
118};
119
120
121BreakPoint.prototype.active = function() {
122 if (this.script_break_point()) {
123 return this.script_break_point().active();
124 }
125 return this.active_;
126};
127
128
129BreakPoint.prototype.condition = function() {
130 if (this.script_break_point() && this.script_break_point().condition()) {
131 return this.script_break_point().condition();
132 }
133 return this.condition_;
134};
135
136
137BreakPoint.prototype.ignoreCount = function() {
138 return this.ignoreCount_;
139};
140
141
142BreakPoint.prototype.script_break_point = function() {
143 return this.script_break_point_;
144};
145
146
147BreakPoint.prototype.enable = function() {
148 this.active_ = true;
149};
150
151
152BreakPoint.prototype.disable = function() {
153 this.active_ = false;
154};
155
156
157BreakPoint.prototype.setCondition = function(condition) {
158 this.condition_ = condition;
159};
160
161
162BreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
163 this.ignoreCount_ = ignoreCount;
164};
165
166
167BreakPoint.prototype.isTriggered = function(exec_state) {
168 // Break point not active - not triggered.
169 if (!this.active()) return false;
170
171 // Check for conditional break point.
172 if (this.condition()) {
173 // If break point has condition try to evaluate it in the top frame.
174 try {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000175 var mirror = exec_state.frame(0).evaluate(this.condition());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000176 // If no sensible mirror or non true value break point not triggered.
177 if (!(mirror instanceof ValueMirror) || !%ToBoolean(mirror.value_)) {
178 return false;
179 }
180 } catch (e) {
181 // Exception evaluating condition counts as not triggered.
182 return false;
183 }
184 }
185
186 // Update the hit count.
187 this.hit_count_++;
188 if (this.script_break_point_) {
189 this.script_break_point_.hit_count_++;
190 }
191
192 // If the break point has an ignore count it is not triggered.
193 if (this.ignoreCount_ > 0) {
194 this.ignoreCount_--;
195 return false;
196 }
197
198 // Break point triggered.
199 return true;
200};
201
202
203// Function called from the runtime when a break point is hit. Returns true if
204// the break point is triggered and supposed to break execution.
205function IsBreakPointTriggered(break_id, break_point) {
206 return break_point.isTriggered(MakeExecutionState(break_id));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000207}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000208
209
210// Object representing a script break point. The script is referenced by its
211// script name and the break point is represented as line and column.
212function ScriptBreakPoint(script_name, opt_line, opt_column) {
213 this.script_name_ = script_name;
214 this.line_ = opt_line || 0;
215 this.column_ = opt_column;
216 this.hit_count_ = 0;
217 this.active_ = true;
218 this.condition_ = null;
219 this.ignoreCount_ = 0;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000220}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000221
222
223ScriptBreakPoint.prototype.number = function() {
224 return this.number_;
225};
226
227
228ScriptBreakPoint.prototype.script_name = function() {
229 return this.script_name_;
230};
231
232
233ScriptBreakPoint.prototype.line = function() {
234 return this.line_;
235};
236
237
238ScriptBreakPoint.prototype.column = function() {
239 return this.column_;
240};
241
242
243ScriptBreakPoint.prototype.hit_count = function() {
244 return this.hit_count_;
245};
246
247
248ScriptBreakPoint.prototype.active = function() {
249 return this.active_;
250};
251
252
253ScriptBreakPoint.prototype.condition = function() {
254 return this.condition_;
255};
256
257
258ScriptBreakPoint.prototype.ignoreCount = function() {
259 return this.ignoreCount_;
260};
261
262
263ScriptBreakPoint.prototype.enable = function() {
264 this.active_ = true;
265};
266
267
268ScriptBreakPoint.prototype.disable = function() {
269 this.active_ = false;
270};
271
272
273ScriptBreakPoint.prototype.setCondition = function(condition) {
274 this.condition_ = condition;
275};
276
277
278ScriptBreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
279 this.ignoreCount_ = ignoreCount;
280
281 // Set ignore count on all break points created from this script break point.
282 for (var i = 0; i < break_points.length; i++) {
283 if (break_points[i].script_break_point() === this) {
284 break_points[i].setIgnoreCount(ignoreCount);
285 }
286 }
287};
288
289
290// Check whether a script matches this script break point. Currently this is
291// only based on script name.
292ScriptBreakPoint.prototype.matchesScript = function(script) {
293 return this.script_name_ == script.name &&
294 script.line_offset <= this.line_ &&
295 this.line_ < script.line_offset + script.lineCount();
296};
297
298
299// Set the script break point in a script.
300ScriptBreakPoint.prototype.set = function (script) {
301 var column = this.column();
302 var line = this.line();
303 // If the column is undefined the break is on the line. To help locate the
304 // first piece of breakable code on the line try to find the column on the
305 // line which contains some source.
306 if (IS_UNDEFINED(column)) {
307 var source_line = script.sourceLine(this.line());
308
309 // Allocate array for caching the columns where the actual source starts.
310 if (!script.sourceColumnStart_) {
311 script.sourceColumnStart_ = new Array(script.lineCount());
312 }
313
314 // Fill cache if needed and get column where the actual source starts.
315 if (IS_UNDEFINED(script.sourceColumnStart_[line])) {
316 script.sourceColumnStart_[line] =
317 source_line.match(sourceLineBeginningSkip)[0].length;
318 }
319 column = script.sourceColumnStart_[line];
320 }
321
322 // Convert the line and column into an absolute position within the script.
323 var pos = Debug.findScriptSourcePosition(script, this.line(), column);
324
325 // Create a break point object and set the break point.
326 break_point = MakeBreakPoint(pos, this.line(), this.column(), this);
327 break_point.setIgnoreCount(this.ignoreCount());
328 %SetScriptBreakPoint(script, pos, break_point);
329
330 return break_point;
331};
332
333
334// Clear all the break points created from this script break point
335ScriptBreakPoint.prototype.clear = function () {
336 var remaining_break_points = [];
337 for (var i = 0; i < break_points.length; i++) {
338 if (break_points[i].script_break_point() &&
339 break_points[i].script_break_point() === this) {
340 %ClearBreakPoint(break_points[i]);
341 } else {
342 remaining_break_points.push(break_points[i]);
343 }
344 }
345 break_points = remaining_break_points;
346};
347
348
349// Function called from runtime when a new script is compiled to set any script
350// break points set in this script.
351function UpdateScriptBreakPoints(script) {
352 for (var i = 0; i < script_break_points.length; i++) {
353 if (script_break_points[i].script_name() == script.name) {
354 script_break_points[i].set(script);
355 }
356 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000357}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000358
359
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000360Debug.addListener = function(listener, opt_data) {
361 if (!IS_FUNCTION(listener)) throw new Error('Parameters have wrong types.');
362 %AddDebugEventListener(listener, opt_data);
363};
364
365Debug.removeListener = function(listener) {
366 if (!IS_FUNCTION(listener)) throw new Error('Parameters have wrong types.');
367 %RemoveDebugEventListener(listener);
368};
369
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000370Debug.breakExecution = function(f) {
mads.s.ager31e71382008-08-13 09:32:07 +0000371 %Break();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000372};
373
374Debug.breakLocations = function(f) {
375 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
376 return %GetBreakLocations(f);
377};
378
379// Returns a Script object. If the parameter is a function the return value
380// is the script in which the function is defined. If the parameter is a string
381// the return value is the script for which the script name has that string
mads.s.agercbaa0602008-08-14 13:41:48 +0000382// value. If it is a regexp and there is a unique script whose name matches
383// we return that, otherwise undefined.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384Debug.findScript = function(func_or_script_name) {
385 if (IS_FUNCTION(func_or_script_name)) {
386 return %FunctionGetScript(func_or_script_name);
mads.s.agercbaa0602008-08-14 13:41:48 +0000387 } else if (IS_REGEXP(func_or_script_name)) {
388 var scripts = Debug.scripts();
389 var last_result = null;
390 var result_count = 0;
391 for (var i in scripts) {
392 var script = scripts[i];
393 if (func_or_script_name.test(script.name)) {
394 last_result = script;
395 result_count++;
396 }
397 }
398 // Return the unique script matching the regexp. If there are more
399 // than one we don't return a value since there is no good way to
400 // decide which one to return. Returning a "random" one, say the
401 // first, would introduce nondeterminism (or something close to it)
402 // because the order is the heap iteration order.
403 if (result_count == 1) {
404 return last_result;
405 } else {
406 return undefined;
407 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000408 } else {
409 return %GetScript(func_or_script_name);
410 }
411};
412
413// Returns the script source. If the parameter is a function the return value
414// is the script source for the script in which the function is defined. If the
415// parameter is a string the return value is the script for which the script
416// name has that string value.
417Debug.scriptSource = function(func_or_script_name) {
418 return this.findScript(func_or_script_name).source;
419};
420
421Debug.source = function(f) {
422 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
423 return %FunctionGetSourceCode(f);
424};
425
426Debug.assembler = function(f) {
427 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
428 return %FunctionGetAssemblerCode(f);
429};
430
431Debug.sourcePosition = function(f) {
432 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
433 return %FunctionGetScriptSourcePosition(f);
434};
435
436Debug.findFunctionSourcePosition = function(func, opt_line, opt_column) {
437 var script = %FunctionGetScript(func);
438 var script_offset = %FunctionGetScriptSourcePosition(func);
439 return script.locationFromLine(opt_line, opt_column, script_offset).position;
440}
441
442
443// Returns the character position in a script based on a line number and an
444// optional position within that line.
445Debug.findScriptSourcePosition = function(script, opt_line, opt_column) {
446 return script.locationFromLine(opt_line, opt_column).position;
447}
448
449
450Debug.findBreakPoint = function(break_point_number, remove) {
451 var break_point;
452 for (var i = 0; i < break_points.length; i++) {
453 if (break_points[i].number() == break_point_number) {
454 break_point = break_points[i];
455 // Remove the break point from the list if requested.
456 if (remove) {
457 break_points.splice(i, 1);
458 }
459 break;
460 }
461 }
462 if (break_point) {
463 return break_point;
464 } else {
465 return this.findScriptBreakPoint(break_point_number, remove);
466 }
467};
468
469
470Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) {
471 if (!IS_FUNCTION(func)) throw new Error('Parameters have wrong types.');
472 var source_position = this.findFunctionSourcePosition(func, opt_line, opt_column) -
473 this.sourcePosition(func);
474 // Find the script for the function.
475 var script = %FunctionGetScript(func);
476 // If the script for the function has a name convert this to a script break
477 // point.
478 if (script && script.name) {
479 // Adjust the source position to be script relative.
480 source_position += %FunctionGetScriptSourcePosition(func);
481 // Find line and column for the position in the script and set a script
482 // break point from that.
483 var location = script.locationFromPosition(source_position);
484 return this.setScriptBreakPoint(script.name,
485 location.line, location.column,
486 opt_condition);
487 } else {
488 // Set a break point directly on the function.
489 var break_point = MakeBreakPoint(source_position, opt_line, opt_column);
490 %SetFunctionBreakPoint(func, source_position, break_point);
491 break_point.setCondition(opt_condition);
492 return break_point.number();
493 }
494};
495
496
497Debug.enableBreakPoint = function(break_point_number) {
498 var break_point = this.findBreakPoint(break_point_number, false);
499 break_point.enable();
500};
501
502
503Debug.disableBreakPoint = function(break_point_number) {
504 var break_point = this.findBreakPoint(break_point_number, false);
505 break_point.disable();
506};
507
508
509Debug.changeBreakPointCondition = function(break_point_number, condition) {
510 var break_point = this.findBreakPoint(break_point_number, false);
511 break_point.setCondition(condition);
512};
513
514
515Debug.changeBreakPointIgnoreCount = function(break_point_number, ignoreCount) {
516 if (ignoreCount < 0) {
517 throw new Error('Invalid argument');
518 }
519 var break_point = this.findBreakPoint(break_point_number, false);
520 break_point.setIgnoreCount(ignoreCount);
521};
522
523
524Debug.clearBreakPoint = function(break_point_number) {
525 var break_point = this.findBreakPoint(break_point_number, true);
526 if (break_point) {
527 return %ClearBreakPoint(break_point);
528 } else {
529 break_point = this.findScriptBreakPoint(break_point_number, true);
530 if (!break_point) {
531 throw new Error('Invalid breakpoint');
532 }
533 }
534};
535
536
537Debug.clearAllBreakPoints = function() {
538 for (var i = 0; i < break_points.length; i++) {
539 break_point = break_points[i];
540 %ClearBreakPoint(break_point);
541 }
542 break_points = [];
543};
544
545
546Debug.findScriptBreakPoint = function(break_point_number, remove) {
547 var script_break_point;
548 for (var i = 0; i < script_break_points.length; i++) {
549 if (script_break_points[i].number() == break_point_number) {
550 script_break_point = script_break_points[i];
551 // Remove the break point from the list if requested.
552 if (remove) {
553 script_break_point.clear();
554 script_break_points.splice(i,1);
555 }
556 break;
557 }
558 }
559 return script_break_point;
560}
561
562
563// Sets a breakpoint in a script identified through script name at the
564// specified source line and column within that line.
565Debug.setScriptBreakPoint = function(script_name, opt_line, opt_column, opt_condition) {
566 // Create script break point object.
567 var script_break_point = new ScriptBreakPoint(script_name, opt_line, opt_column);
568
569 // Assign number to the new script break point and add it.
570 script_break_point.number_ = next_break_point_number++;
571 script_break_point.setCondition(opt_condition);
572 script_break_points.push(script_break_point);
573
574 // Run through all scripts to see it this script break point matches any
575 // loaded scripts.
576 var scripts = this.scripts();
577 for (var i = 0; i < scripts.length; i++) {
578 if (script_break_point.matchesScript(scripts[i])) {
579 script_break_point.set(scripts[i]);
580 }
581 }
582
583 return script_break_point.number();
584}
585
586
587Debug.enableScriptBreakPoint = function(break_point_number) {
588 var script_break_point = this.findScriptBreakPoint(break_point_number, false);
589 script_break_point.enable();
590};
591
592
593Debug.disableScriptBreakPoint = function(break_point_number) {
594 var script_break_point = this.findScriptBreakPoint(break_point_number, false);
595 script_break_point.disable();
596};
597
598
599Debug.changeScriptBreakPointCondition = function(break_point_number, condition) {
600 var script_break_point = this.findScriptBreakPoint(break_point_number, false);
601 script_break_point.setCondition(condition);
602};
603
604
605Debug.changeScriptBreakPointIgnoreCount = function(break_point_number, ignoreCount) {
606 if (ignoreCount < 0) {
607 throw new Error('Invalid argument');
608 }
609 var script_break_point = this.findScriptBreakPoint(break_point_number, false);
610 script_break_point.setIgnoreCount(ignoreCount);
611};
612
613
614Debug.scriptBreakPoints = function() {
615 return script_break_points;
616}
617
618
619Debug.clearStepping = function() {
mads.s.ager31e71382008-08-13 09:32:07 +0000620 %ClearStepping();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000621}
622
623Debug.setBreakOnException = function() {
624 return %ChangeBreakOnException(Debug.ExceptionBreak.All, true);
625};
626
627Debug.clearBreakOnException = function() {
628 return %ChangeBreakOnException(Debug.ExceptionBreak.All, false);
629};
630
631Debug.setBreakOnUncaughtException = function() {
632 return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true);
633};
634
635Debug.clearBreakOnUncaughtException = function() {
636 return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
637};
638
639Debug.showBreakPoints = function(f, full) {
640 if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
641 var source = full ? this.scriptSource(f) : this.source(f);
642 var offset = full ? this.sourcePosition(f) : 0;
643 var locations = this.breakLocations(f);
644 if (!locations) return source;
645 locations.sort(function(x, y) { return x - y; });
646 var result = "";
647 var prev_pos = 0;
648 var pos;
649 for (var i = 0; i < locations.length; i++) {
650 pos = locations[i] - offset;
651 result += source.slice(prev_pos, pos);
652 result += "[B" + i + "]";
653 prev_pos = pos;
654 }
655 pos = source.length;
656 result += source.substring(prev_pos, pos);
657 return result;
658};
659
660
661// Get all the scripts currently loaded. Locating all the scripts is based on
662// scanning the heap.
663Debug.scripts = function() {
664 // Collect all scripts in the heap.
mads.s.ager31e71382008-08-13 09:32:07 +0000665 return %DebugGetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000666}
667
668function MakeExecutionState(break_id) {
669 return new ExecutionState(break_id);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000670}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000671
672function ExecutionState(break_id) {
673 this.break_id = break_id;
674 this.selected_frame = 0;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000675}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000676
677ExecutionState.prototype.prepareStep = function(opt_action, opt_count) {
678 var action = Debug.StepAction.StepIn;
679 if (!IS_UNDEFINED(opt_action)) action = %ToNumber(opt_action);
680 var count = opt_count ? %ToNumber(opt_count) : 1;
681
682 return %PrepareStep(this.break_id, action, count);
683}
684
kasper.lundbd3ec4e2008-07-09 11:06:54 +0000685ExecutionState.prototype.evaluateGlobal = function(source, disable_break) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000686 return MakeMirror(
687 %DebugEvaluateGlobal(this.break_id, source, Boolean(disable_break)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000688};
689
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000690ExecutionState.prototype.frameCount = function() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000691 return %GetFrameCount(this.break_id);
692};
693
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000694ExecutionState.prototype.frame = function(opt_index) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000695 // If no index supplied return the selected frame.
696 if (opt_index == null) opt_index = this.selected_frame;
697 return new FrameMirror(this.break_id, opt_index);
698};
699
700ExecutionState.prototype.cframesValue = function(opt_from_index, opt_to_index) {
701 return %GetCFrames(this.break_id);
702};
703
704ExecutionState.prototype.setSelectedFrame = function(index) {
705 var i = %ToNumber(index);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000706 if (i < 0 || i >= this.frameCount()) throw new Error('Illegal frame index.');
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000707 this.selected_frame = i;
708};
709
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000710ExecutionState.prototype.selectedFrame = function() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000711 return this.selected_frame;
712};
713
714ExecutionState.prototype.debugCommandProcessor = function(protocol) {
715 return new DebugCommandProcessor(this, protocol);
716};
717
718
719function MakeBreakEvent(exec_state, break_points_hit) {
720 return new BreakEvent(exec_state, break_points_hit);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000721}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000722
723
724function BreakEvent(exec_state, break_points_hit) {
725 this.exec_state_ = exec_state;
726 this.break_points_hit_ = break_points_hit;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000727}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000728
729
730BreakEvent.prototype.func = function() {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000731 return this.exec_state_.frame(0).func();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000732};
733
734
735BreakEvent.prototype.sourceLine = function() {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000736 return this.exec_state_.frame(0).sourceLine();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000737};
738
739
740BreakEvent.prototype.sourceColumn = function() {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000741 return this.exec_state_.frame(0).sourceColumn();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000742};
743
744
745BreakEvent.prototype.sourceLineText = function() {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000746 return this.exec_state_.frame(0).sourceLineText();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000747};
748
749
750BreakEvent.prototype.breakPointsHit = function() {
751 return this.break_points_hit_;
752};
753
754
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000755BreakEvent.prototype.toJSONProtocol = function() {
756 var o = { seq: next_response_seq++,
757 type: "event",
758 event: "break",
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000759 body: { invocationText: this.exec_state_.frame(0).invocationText(),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000760 }
761 }
762
763 // Add script related information to the event if available.
764 var script = this.func().script();
765 if (script) {
766 o.body.sourceLine = this.sourceLine(),
767 o.body.sourceColumn = this.sourceColumn(),
768 o.body.sourceLineText = this.sourceLineText(),
769 o.body.script = { name: script.name(),
770 lineOffset: script.lineOffset(),
771 columnOffset: script.columnOffset(),
772 lineCount: script.lineCount()
773 };
774 }
775
776 // Add an Array of break points hit if any.
777 if (this.breakPointsHit()) {
778 o.body.breakpoints = [];
779 for (var i = 0; i < this.breakPointsHit().length; i++) {
780 // Find the break point number. For break points originating from a
781 // script break point supply the script break point number.
782 var breakpoint = this.breakPointsHit()[i];
783 var script_break_point = breakpoint.script_break_point();
784 var number;
785 if (script_break_point) {
786 number = script_break_point.number();
787 } else {
788 number = breakpoint.number();
789 }
790 o.body.breakpoints.push(number);
791 }
792 }
793
794 return SimpleObjectToJSON_(o);
795};
796
797
798function MakeExceptionEvent(exec_state, exception, uncaught) {
799 return new ExceptionEvent(exec_state, exception, uncaught);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000800}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000801
802function ExceptionEvent(exec_state, exception, uncaught) {
803 this.exec_state_ = exec_state;
804 this.exception_ = exception;
805 this.uncaught_ = uncaught;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000806}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000807
808ExceptionEvent.prototype.uncaught = function() {
809 return this.uncaught_;
810}
811
812ExceptionEvent.prototype.func = function() {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000813 return this.exec_state_.frame(0).func();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000814};
815
816
817ExceptionEvent.prototype.sourceLine = function() {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000818 return this.exec_state_.frame(0).sourceLine();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000819};
820
821
822ExceptionEvent.prototype.sourceColumn = function() {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000823 return this.exec_state_.frame(0).sourceColumn();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000824};
825
826
827ExceptionEvent.prototype.sourceLineText = function() {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000828 return this.exec_state_.frame(0).sourceLineText();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000829};
830
831
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000832ExceptionEvent.prototype.toJSONProtocol = function() {
833 var o = { seq: next_response_seq++,
834 type: "event",
835 event: "exception",
836 body: { uncaught: this.uncaught_,
837 exception: MakeMirror(this.exception_),
838 sourceLine: this.sourceLine(),
839 sourceColumn: this.sourceColumn(),
840 sourceLineText: this.sourceLineText(),
841 }
842 }
843
844 // Add script information to the event if available.
845 var script = this.func().script();
846 if (script) {
847 o.body.script = { name: script.name(),
848 lineOffset: script.lineOffset(),
849 columnOffset: script.columnOffset(),
850 lineCount: script.lineCount()
851 };
852 }
853
854 return SimpleObjectToJSON_(o);
855};
856
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000857
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000858function MakeCompileEvent(script_source, script_name, script_function) {
859 return new CompileEvent(script_source, script_name, script_function);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000860}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000861
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000862
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000863function CompileEvent(script_source, script_name, script_function) {
864 this.scriptSource = script_source;
865 this.scriptName = script_name;
866 this.scriptFunction = script_function;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000867}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000868
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000869
870function MakeNewFunctionEvent(func) {
871 return new NewFunctionEvent(func);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000872}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000873
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000874
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000875function NewFunctionEvent(func) {
876 this.func = func;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000877}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000878
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000879NewFunctionEvent.prototype.name = function() {
880 return this.func.name;
881};
882
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000883
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000884NewFunctionEvent.prototype.setBreakPoint = function(p) {
885 Debug.setBreakPoint(this.func, p || 0);
886};
887
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000888
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000889function DebugCommandProcessor(exec_state) {
890 this.exec_state_ = exec_state;
891};
892
893
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000894DebugCommandProcessor.prototype.processDebugRequest = function (request) {
895 return this.processDebugJSONRequest(request);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000896}
897
898
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000899DebugCommandProcessor.prototype.responseIsRunning = function (response) {
900 return this.isRunning(response);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000901}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000902
903
904function RequestPacket(command) {
905 this.seq = 0;
906 this.type = 'request';
907 this.command = command;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000908}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000909
910
911RequestPacket.prototype.toJSONProtocol = function() {
912 // Encode the protocol header.
913 var json = '{';
914 json += '"seq":' + this.seq;
915 json += ',"type":"' + this.type + '"';
916 if (this.command) {
917 json += ',"command":' + StringToJSON_(this.command);
918 }
919 if (this.arguments) {
920 json += ',"arguments":';
921 // Encode the arguments part.
922 if (this.arguments.toJSONProtocol) {
923 json += this.arguments.toJSONProtocol()
924 } else {
925 json += SimpleObjectToJSON_(this.arguments);
926 }
927 }
928 json += '}';
929 return json;
930}
931
932
933DebugCommandProcessor.prototype.createRequest = function(command) {
934 return new RequestPacket(command);
935};
936
937
938function ResponsePacket(request) {
939 // Build the initial response from the request.
940 this.seq = next_response_seq++;
941 this.type = 'response';
942 if (request) this.request_seq = request.seq;
943 if (request) this.command = request.command;
944 this.success = true;
945 this.running = false;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000946}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000947
948
949ResponsePacket.prototype.failed = function(message) {
950 this.success = false;
951 this.message = message;
952}
953
954
955ResponsePacket.prototype.toJSONProtocol = function() {
956 // Encode the protocol header.
957 var json = '{';
958 json += '"seq":' + this.seq;
959 if (this.request_seq) {
960 json += ',"request_seq":' + this.request_seq;
961 }
962 json += ',"type":"' + this.type + '"';
963 if (this.command) {
964 json += ',"command":' + StringToJSON_(this.command);
965 }
966 if (this.success) {
967 json += ',"success":' + this.success;
968 } else {
969 json += ',"success":false';
970 }
971 if (this.body) {
972 json += ',"body":';
973 // Encode the body part.
974 if (this.body.toJSONProtocol) {
975 json += this.body.toJSONProtocol(true);
976 } else if (this.body instanceof Array) {
977 json += '[';
978 for (var i = 0; i < this.body.length; i++) {
979 if (i != 0) json += ',';
980 if (this.body[i].toJSONProtocol) {
981 json += this.body[i].toJSONProtocol(true)
982 } else {
983 json += SimpleObjectToJSON_(this.body[i]);
984 }
985 }
986 json += ']';
987 } else {
988 json += SimpleObjectToJSON_(this.body);
989 }
990 }
991 if (this.message) {
992 json += ',"message":' + StringToJSON_(this.message) ;
993 }
994 if (this.running) {
995 json += ',"running":true';
996 } else {
997 json += ',"running":false';
998 }
999 json += '}';
1000 return json;
1001}
1002
1003
1004DebugCommandProcessor.prototype.createResponse = function(request) {
1005 return new ResponsePacket(request);
1006};
1007
1008
1009DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request, stopping) {
1010 var request; // Current request.
1011 var response; // Generated response.
1012 try {
1013 try {
1014 // Convert the JSON string to an object.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001015 request = %CompileString('(' + json_request + ')', 0)();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001016
1017 // Create an initial response.
1018 response = this.createResponse(request);
1019
1020 if (!request.type) {
1021 throw new Error('Type not specified');
1022 }
1023
1024 if (request.type != 'request') {
1025 throw new Error("Illegal type '" + request.type + "' in request");
1026 }
1027
1028 if (!request.command) {
1029 throw new Error('Command not specified');
1030 }
1031
1032 if (request.command == 'continue') {
1033 this.continueRequest_(request, response);
1034 } else if (request.command == 'break') {
1035 this.breakRequest_(request, response);
1036 } else if (request.command == 'setbreakpoint') {
1037 this.setBreakPointRequest_(request, response);
1038 } else if (request.command == 'changebreakpoint') {
1039 this.changeBreakPointRequest_(request, response);
1040 } else if (request.command == 'clearbreakpoint') {
1041 this.clearBreakPointRequest_(request, response);
1042 } else if (request.command == 'backtrace') {
1043 this.backtraceRequest_(request, response);
1044 } else if (request.command == 'frame') {
1045 this.frameRequest_(request, response);
1046 } else if (request.command == 'evaluate') {
1047 this.evaluateRequest_(request, response);
1048 } else if (request.command == 'source') {
1049 this.sourceRequest_(request, response);
1050 } else if (request.command == 'scripts') {
1051 this.scriptsRequest_(request, response);
1052 } else {
1053 throw new Error('Unknown command "' + request.command + '" in request');
1054 }
1055 } catch (e) {
1056 // If there is no response object created one (without command).
1057 if (!response) {
1058 response = this.createResponse();
1059 }
1060 response.success = false;
1061 response.message = %ToString(e);
1062 }
1063
1064 // Return the response as a JSON encoded string.
1065 try {
1066 // Set the running state to what indicated.
1067 if (!IS_UNDEFINED(stopping)) {
1068 response.running = !stopping;
1069 }
1070 return response.toJSONProtocol();
1071 } catch (e) {
1072 // Failed to generate response - return generic error.
1073 return '{"seq":' + response.seq + ',' +
1074 '"request_seq":' + request.seq + ',' +
1075 '"type":"response",' +
1076 '"success":false,' +
1077 '"message":"Internal error: ' + %ToString(e) + '"}';
1078 }
1079 } catch (e) {
1080 // Failed in one of the catch blocks above - most generic error.
1081 return '{"seq":0,"type":"response","success":false,"message":"Internal error"}';
1082 }
1083};
1084
1085
1086DebugCommandProcessor.prototype.continueRequest_ = function(request, response) {
1087 // Check for arguments for continue.
1088 if (request.arguments) {
1089 var count = 1;
1090 var action = Debug.StepAction.StepIn;
1091
1092 // Pull out arguments.
1093 var stepaction = request.arguments.stepaction;
1094 var stepcount = request.arguments.stepcount;
1095
1096 // Get the stepcount argument if any.
1097 if (stepcount) {
1098 count = %ToNumber(stepcount);
1099 if (count < 0) {
1100 throw new Error('Invalid stepcount argument "' + stepcount + '".');
1101 }
1102 }
1103
1104 // Get the stepaction argument.
1105 if (stepaction) {
1106 if (stepaction == 'in') {
1107 action = Debug.StepAction.StepIn;
1108 } else if (stepaction == 'min') {
1109 action = Debug.StepAction.StepMin;
1110 } else if (stepaction == 'next') {
1111 action = Debug.StepAction.StepNext;
1112 } else if (stepaction == 'out') {
1113 action = Debug.StepAction.StepOut;
1114 } else {
1115 throw new Error('Invalid stepaction argument "' + stepaction + '".');
1116 }
1117 }
1118
1119 // Setup the VM for stepping.
1120 this.exec_state_.prepareStep(action, count);
1121 }
1122
1123 // VM should be running after executing this request.
1124 response.running = true;
1125};
1126
1127
1128DebugCommandProcessor.prototype.breakRequest_ = function(request, response) {
1129 // Ignore as break command does not do anything when broken.
1130};
1131
1132
1133DebugCommandProcessor.prototype.setBreakPointRequest_ =
1134 function(request, response) {
1135 // Check for legal request.
1136 if (!request.arguments) {
1137 response.failed('Missing arguments');
1138 return;
1139 }
1140
1141 // Pull out arguments.
1142 var type = request.arguments.type;
1143 var target = request.arguments.target;
1144 var line = request.arguments.line;
1145 var column = request.arguments.column;
1146 var enabled = IS_UNDEFINED(request.arguments.enabled) ?
1147 true : request.arguments.enabled;
1148 var condition = request.arguments.condition;
1149 var ignoreCount = request.arguments.ignoreCount;
1150
1151 // Check for legal arguments.
1152 if (!type || !target) {
1153 response.failed('Missing argument "type" or "target"');
1154 return;
1155 }
1156 if (type != 'function' && type != 'script') {
1157 response.failed('Illegal type "' + type + '"');
1158 return;
1159 }
1160
1161 // Either function or script break point.
1162 var break_point_number;
1163 if (type == 'function') {
1164 // Handle function break point.
1165 if (!IS_STRING(target)) {
1166 response.failed('Argument "target" is not a string value');
1167 return;
1168 }
1169 var f;
1170 try {
1171 // Find the function through a global evaluate.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001172 f = this.exec_state_.evaluateGlobal(target).value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001173 } catch (e) {
1174 response.failed('Error: "' + %ToString(e) +
1175 '" evaluating "' + target + '"');
1176 return;
1177 }
1178 if (!IS_FUNCTION(f)) {
1179 response.failed('"' + target + '" does not evaluate to a function');
1180 return;
1181 }
1182
1183 // Set function break point.
1184 break_point_number = Debug.setBreakPoint(f, line, column, condition);
1185 } else {
1186 // set script break point.
1187 break_point_number = Debug.setScriptBreakPoint(target,
1188 line, column,
1189 condition);
1190 }
1191
1192 // Set additional break point properties.
1193 var break_point = Debug.findBreakPoint(break_point_number);
1194 if (ignoreCount) {
1195 Debug.changeBreakPointIgnoreCount(break_point_number, ignoreCount);
1196 }
1197 if (!enabled) {
1198 Debug.disableBreakPoint(break_point_number);
1199 }
1200
1201 // Add the break point number to the response.
1202 response.body = { type: type,
1203 breakpoint: break_point_number }
1204
1205 // Add break point information to the response.
1206 if (break_point instanceof ScriptBreakPoint) {
1207 response.body.type = 'script';
1208 response.body.script_name = break_point.script_name();
1209 response.body.line = break_point.line();
1210 response.body.column = break_point.column();
1211 } else {
1212 response.body.type = 'function';
1213 }
1214};
1215
1216
1217DebugCommandProcessor.prototype.changeBreakPointRequest_ = function(request, response) {
1218 // Check for legal request.
1219 if (!request.arguments) {
1220 response.failed('Missing arguments');
1221 return;
1222 }
1223
1224 // Pull out arguments.
1225 var break_point = %ToNumber(request.arguments.breakpoint);
1226 var enabled = request.arguments.enabled;
1227 var condition = request.arguments.condition;
1228 var ignoreCount = request.arguments.ignoreCount;
1229
1230 // Check for legal arguments.
1231 if (!break_point) {
1232 response.failed('Missing argument "breakpoint"');
1233 return;
1234 }
1235
1236 // Change enabled state if supplied.
1237 if (!IS_UNDEFINED(enabled)) {
1238 if (enabled) {
1239 Debug.enableBreakPoint(break_point);
1240 } else {
1241 Debug.disableBreakPoint(break_point);
1242 }
1243 }
1244
1245 // Change condition if supplied
1246 if (!IS_UNDEFINED(condition)) {
1247 Debug.changeBreakPointCondition(break_point, condition);
1248 }
1249
1250 // Change ignore count if supplied
1251 if (!IS_UNDEFINED(ignoreCount)) {
1252 Debug.changeBreakPointIgnoreCount(break_point, ignoreCount);
1253 }
1254}
1255
1256
1257DebugCommandProcessor.prototype.clearBreakPointRequest_ = function(request, response) {
1258 // Check for legal request.
1259 if (!request.arguments) {
1260 response.failed('Missing arguments');
1261 return;
1262 }
1263
1264 // Pull out arguments.
1265 var break_point = %ToNumber(request.arguments.breakpoint);
1266
1267 // Check for legal arguments.
1268 if (!break_point) {
1269 response.failed('Missing argument "breakpoint"');
1270 return;
1271 }
1272
1273 // Clear break point.
1274 Debug.clearBreakPoint(break_point);
1275}
1276
1277
1278DebugCommandProcessor.prototype.backtraceRequest_ = function(request, response) {
1279 // Get the number of frames.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001280 var total_frames = this.exec_state_.frameCount();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001281
1282 // Default frame range to include in backtrace.
1283 var from_index = 0
1284 var to_index = kDefaultBacktraceLength;
1285
1286 // Get the range from the arguments.
1287 if (request.arguments) {
1288 from_index = request.arguments.fromFrame;
1289 if (from_index < 0) {
1290 return response.failed('Invalid frame number');
1291 }
1292 to_index = request.arguments.toFrame;
1293 if (to_index < 0) {
1294 return response.failed('Invalid frame number');
1295 }
1296 }
1297
1298 // Adjust the index.
1299 to_index = Math.min(total_frames, to_index);
1300
1301 if (to_index <= from_index) {
1302 var error = 'Invalid frame range';
1303 return response.failed(error);
1304 }
1305
1306 // Create the response body.
1307 var frames = [];
1308 for (var i = from_index; i < to_index; i++) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001309 frames.push(this.exec_state_.frame(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001310 }
1311 response.body = {
1312 fromFrame: from_index,
1313 toFrame: to_index,
1314 totalFrames: total_frames,
1315 frames: frames
1316 }
1317};
1318
1319
1320DebugCommandProcessor.prototype.backtracec = function(cmd, args) {
1321 return this.exec_state_.cframesValue();
1322};
1323
1324
1325DebugCommandProcessor.prototype.frameRequest_ = function(request, response) {
1326 // With no arguments just keep the selected frame.
1327 if (request.arguments && request.arguments.number >= 0) {
1328 this.exec_state_.setSelectedFrame(request.arguments.number);
1329 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001330 response.body = this.exec_state_.frame();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001331};
1332
1333
1334DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) {
1335 if (!request.arguments) {
1336 return response.failed('Missing arguments');
1337 }
1338
1339 // Pull out arguments.
1340 var expression = request.arguments.expression;
1341 var frame = request.arguments.frame;
1342 var global = request.arguments.global;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001343 var disable_break = request.arguments.disable_break;
1344
1345 // The expression argument could be an integer so we convert it to a
1346 // string.
1347 try {
1348 expression = String(expression);
1349 } catch(e) {
1350 return response.failed('Failed to convert expression argument to string');
1351 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001352
1353 // Check for legal arguments.
1354 if (!IS_UNDEFINED(frame) && global) {
1355 return response.failed('Arguments "frame" and "global" are exclusive');
1356 }
1357
1358 // Global evaluate.
1359 if (global) {
1360 // Evaluate in the global context.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001361 response.body =
1362 this.exec_state_.evaluateGlobal(expression), Boolean(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001363 return;
1364 }
1365
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001366 // Default value for disable_break is true.
1367 if (IS_UNDEFINED(disable_break)) {
1368 disable_break = true;
1369 }
1370
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001371 // Check whether a frame was specified.
1372 if (!IS_UNDEFINED(frame)) {
1373 var frame_number = %ToNumber(frame);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001374 if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001375 return response.failed('Invalid frame "' + frame + '"');
1376 }
1377 // Evaluate in the specified frame.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001378 response.body = this.exec_state_.frame(frame_number).evaluate(
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001379 expression, Boolean(disable_break));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001380 return;
1381 } else {
1382 // Evaluate in the selected frame.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001383 response.body = this.exec_state_.frame().evaluate(
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001384 expression, Boolean(disable_break));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001385 return;
1386 }
1387};
1388
1389
1390DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) {
1391 var from_line;
1392 var to_line;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001393 var frame = this.exec_state_.frame();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001394 if (request.arguments) {
1395 // Pull out arguments.
1396 from_line = request.arguments.fromLine;
1397 to_line = request.arguments.toLine;
1398
1399 if (!IS_UNDEFINED(request.arguments.frame)) {
1400 var frame_number = %ToNumber(request.arguments.frame);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001401 if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001402 return response.failed('Invalid frame "' + frame + '"');
1403 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001404 frame = this.exec_state_.frame(frame_number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001405 }
1406 }
1407
1408 // Get the script selected.
1409 var script = frame.func().script();
1410 if (!script) {
1411 return response.failed('No source');
1412 }
1413
1414 // Get the source slice and fill it into the response.
1415 var slice = script.sourceSlice(from_line, to_line);
1416 if (!slice) {
1417 return response.failed('Invalid line interval');
1418 }
1419 response.body = {};
1420 response.body.source = slice.sourceText();
1421 response.body.fromLine = slice.from_line;
1422 response.body.toLine = slice.to_line;
1423 response.body.fromPosition = slice.from_position;
1424 response.body.toPosition = slice.to_position;
1425 response.body.totalLines = script.lineCount();
1426};
1427
1428
1429DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) {
1430 var types = ScriptTypeFlag(Debug.ScriptType.Normal);
1431 if (request.arguments) {
1432 // Pull out arguments.
1433 if (!IS_UNDEFINED(request.arguments.types)) {
1434 types = %ToNumber(request.arguments.types);
1435 if (isNaN(types) || types < 0) {
1436 return response.failed('Invalid types "' + request.arguments.types + '"');
1437 }
1438 }
1439 }
1440
1441 // Collect all scripts in the heap.
mads.s.ager31e71382008-08-13 09:32:07 +00001442 var scripts = %DebugGetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001443
1444 response.body = [];
1445
1446 for (var i = 0; i < scripts.length; i++) {
1447 if (types & ScriptTypeFlag(scripts[i].type)) {
1448 var script = {};
1449 if (scripts[i].name) {
1450 script.name = scripts[i].name;
1451 }
1452 script.lineOffset = scripts[i].line_offset;
1453 script.columnOffset = scripts[i].column_offset;
1454 script.lineCount = scripts[i].lineCount();
1455 script.sourceStart = scripts[i].source.substring(0, 80);
1456 script.sourceLength = scripts[i].source.length;
1457 script.type = scripts[i].type;
1458 response.body.push(script);
1459 }
1460 }
1461};
1462
1463
1464// Check whether the JSON response indicate that the VM should be running.
1465DebugCommandProcessor.prototype.isRunning = function(json_response) {
1466 try {
1467 // Convert the JSON string to an object.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001468 response = %CompileString('(' + json_response + ')', 0)();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001469
1470 // Return whether VM should be running after this request.
1471 return response.running;
1472
1473 } catch (e) {
1474 return false;
1475 }
1476}
1477
1478
1479DebugCommandProcessor.prototype.systemBreak = function(cmd, args) {
mads.s.ager31e71382008-08-13 09:32:07 +00001480 return %SystemBreak();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001481};
1482
1483
1484function NumberToHex8Str(n) {
1485 var r = "";
1486 for (var i = 0; i < 8; ++i) {
1487 var c = hexCharArray[n & 0x0F]; // hexCharArray is defined in uri.js
1488 r = c + r;
1489 n = n >>> 4;
1490 }
1491 return r;
1492};
1493
1494DebugCommandProcessor.prototype.formatCFrames = function(cframes_value) {
1495 var result = "";
1496 if (cframes_value == null || cframes_value.length == 0) {
1497 result += "(stack empty)";
1498 } else {
1499 for (var i = 0; i < cframes_value.length; ++i) {
1500 if (i != 0) result += "\n";
1501 result += this.formatCFrame(cframes_value[i]);
1502 }
1503 }
1504 return result;
1505};
1506
1507
1508DebugCommandProcessor.prototype.formatCFrame = function(cframe_value) {
1509 var result = "";
1510 result += "0x" + NumberToHex8Str(cframe_value.address);
1511 if (!IS_UNDEFINED(cframe_value.text)) {
1512 result += " " + cframe_value.text;
1513 }
1514 return result;
1515}
1516
1517
1518/**
1519 * Convert an Object to its JSON representation (see http://www.json.org/).
1520 * This implementation simply runs through all string property names and adds
1521 * each property to the JSON representation for some predefined types. For type
1522 * "object" the function calls itself recursively unless the object has the
1523 * function property "toJSONProtocol" in which case that is used. This is not
1524 * a general implementation but sufficient for the debugger. Note that circular
1525 * structures will cause infinite recursion.
1526 * @param {Object} object The object to format as JSON
1527 * @return {string} JSON formatted object value
1528 */
1529function SimpleObjectToJSON_(object) {
1530 var content = [];
1531 for (var key in object) {
1532 // Only consider string keys.
1533 if (typeof key == 'string') {
1534 var property_value = object[key];
1535
1536 // Format the value based on its type.
1537 var property_value_json;
1538 switch (typeof property_value) {
1539 case 'object':
1540 if (typeof property_value.toJSONProtocol == 'function') {
1541 property_value_json = property_value.toJSONProtocol(true)
1542 } else if (IS_ARRAY(property_value)){
1543 property_value_json = SimpleArrayToJSON_(property_value);
1544 } else {
1545 property_value_json = SimpleObjectToJSON_(property_value);
1546 }
1547 break;
1548
1549 case 'boolean':
1550 property_value_json = BooleanToJSON_(property_value);
1551 break;
1552
1553 case 'number':
1554 property_value_json = NumberToJSON_(property_value);
1555 break;
1556
1557 case 'string':
1558 property_value_json = StringToJSON_(property_value);
1559 break;
1560
1561 default:
1562 property_value_json = null;
1563 }
1564
1565 // Add the property if relevant.
1566 if (property_value_json) {
1567 content.push(StringToJSON_(key) + ':' + property_value_json);
1568 }
1569 }
1570 }
1571
1572 // Make JSON object representation.
1573 return '{' + content.join(',') + '}';
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001574}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001575
1576/**
1577 * Convert an array to its JSON representation. This is a VERY simple
1578 * implementation just to support what is needed for the debugger.
1579 * @param {Array} arrya The array to format as JSON
1580 * @return {string} JSON formatted array value
1581 */
1582function SimpleArrayToJSON_(array) {
1583 // Make JSON array representation.
1584 var json = '[';
1585 for (var i = 0; i < array.length; i++) {
1586 if (i != 0) {
1587 json += ',';
1588 }
1589 var elem = array[i];
1590 if (elem.toJSONProtocol) {
1591 json += elem.toJSONProtocol(true)
1592 } else if (IS_OBJECT(elem)) {
1593 json += SimpleObjectToJSON_(elem);
1594 } else if (IS_BOOLEAN(elem)) {
1595 json += BooleanToJSON_(elem);
1596 } else if (IS_NUMBER(elem)) {
1597 json += NumberToJSON_(elem);
1598 } else if (IS_STRING(elem)) {
1599 json += StringToJSON_(elem);
1600 } else {
1601 json += elem;
1602 }
1603 }
1604 json += ']';
1605 return json;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001606}