| 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 |