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