OLD | NEW |
1 // Copyright 2008 the V8 project authors. All rights reserved. | 1 // Copyright 2008 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 13 matching lines...) Expand all Loading... |
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | 27 |
28 // How crappy is it that I have to implement completely basic stuff | 28 // How crappy is it that I have to implement completely basic stuff |
29 // like this myself? Answer: very. | 29 // like this myself? Answer: very. |
30 String.prototype.startsWith = function (str) { | 30 String.prototype.startsWith = function (str) { |
31 if (str.length > this.length) | 31 if (str.length > this.length) |
32 return false; | 32 return false; |
33 return this.substr(0, str.length) == str; | 33 return this.substr(0, str.length) == str; |
34 }; | 34 } |
| 35 |
| 36 function log10(num) { |
| 37 return Math.log(num)/Math.log(10); |
| 38 } |
35 | 39 |
36 function ToInspectableObject(obj) { | 40 function ToInspectableObject(obj) { |
37 if (!obj && typeof obj === 'object') { | 41 if (!obj && typeof obj === 'object') { |
38 return void 0; | 42 return void 0; |
39 } else { | 43 } else { |
40 return Object(obj); | 44 return Object(obj); |
41 } | 45 } |
42 } | 46 } |
43 | 47 |
44 function GetCompletions(global, last, full) { | 48 function GetCompletions(global, last, full) { |
(...skipping 16 matching lines...) Expand all Loading... |
61 var properties = mirror.properties(); | 65 var properties = mirror.properties(); |
62 for (var i = 0; i < properties.length; i++) { | 66 for (var i = 0; i < properties.length; i++) { |
63 var name = properties[i].name(); | 67 var name = properties[i].name(); |
64 if (typeof name === 'string' && name.startsWith(last)) | 68 if (typeof name === 'string' && name.startsWith(last)) |
65 result.push(name); | 69 result.push(name); |
66 } | 70 } |
67 current = ToInspectableObject(current.__proto__); | 71 current = ToInspectableObject(current.__proto__); |
68 } | 72 } |
69 return result; | 73 return result; |
70 } | 74 } |
| 75 |
| 76 |
| 77 // Global object holding debugger related constants and state. |
| 78 const Debug = {}; |
| 79 |
| 80 |
| 81 // Debug events which can occour in the V8 JavaScript engine. These originate |
| 82 // from the API include file debug.h. |
| 83 Debug.DebugEvent = { Break: 1, |
| 84 Exception: 2, |
| 85 NewFunction: 3, |
| 86 BeforeCompile: 4, |
| 87 AfterCompile: 5 }; |
| 88 |
| 89 |
| 90 // The different types of scripts matching enum ScriptType in objects.h. |
| 91 Debug.ScriptType = { Native: 0, |
| 92 Extension: 1, |
| 93 Normal: 2 }; |
| 94 |
| 95 |
| 96 // Current debug state. |
| 97 const kNoFrame = -1; |
| 98 Debug.State = { |
| 99 currentFrame: kNoFrame, |
| 100 currentSourceLine: -1 |
| 101 } |
| 102 |
| 103 |
| 104 function DebugEventToText(event) { |
| 105 if (event.eventType() == 1) { |
| 106 // Build the break details. |
| 107 var details = ''; |
| 108 if (event.breakPointsHit()) { |
| 109 details += 'breakpoint'; |
| 110 if (event.breakPointsHit().length > 1) { |
| 111 details += 's'; |
| 112 } |
| 113 details += ' #'; |
| 114 for (var i = 0; i < event.breakPointsHit().length; i++) { |
| 115 if (i > 0) { |
| 116 details += ', #'; |
| 117 } |
| 118 // Find the break point number. For break points originating from a |
| 119 // script break point display the script break point number. |
| 120 var break_point = event.breakPointsHit()[i]; |
| 121 var script_break_point = break_point.script_break_point(); |
| 122 if (script_break_point) { |
| 123 details += script_break_point.number(); |
| 124 } else { |
| 125 details += break_point.number(); |
| 126 } |
| 127 } |
| 128 } else { |
| 129 details += 'break'; |
| 130 } |
| 131 details += ' in '; |
| 132 details += event.executionState().frame(0).invocationText(); |
| 133 details += ' at '; |
| 134 details += event.executionState().frame(0).sourceAndPositionText(); |
| 135 details += '\n' |
| 136 if (event.func().script()) { |
| 137 details += FrameSourceUnderline(event.executionState().frame(0)); |
| 138 } |
| 139 Debug.State.currentSourceLine = |
| 140 event.executionState().frame(0).sourceLine(); |
| 141 Debug.State.currentFrame = 0; |
| 142 return details; |
| 143 } else if (event.eventType() == 2) { |
| 144 var details = ''; |
| 145 if (event.uncaught_) { |
| 146 details += 'Uncaught: '; |
| 147 } else { |
| 148 details += 'Exception: '; |
| 149 } |
| 150 |
| 151 details += '"'; |
| 152 details += event.exception(); |
| 153 details += '"'; |
| 154 if (event.executionState().frameCount() > 0) { |
| 155 details += '"'; |
| 156 details += event.exception(); |
| 157 details += ' at '; |
| 158 details += event.executionState().frame(0).sourceAndPositionText(); |
| 159 details += '\n'; |
| 160 details += FrameSourceUnderline(event.executionState().frame(0)); |
| 161 Debug.State.currentSourceLine = |
| 162 event.executionState().frame(0).sourceLine(); |
| 163 Debug.State.currentFrame = 0; |
| 164 } else { |
| 165 details += ' (empty stack)'; |
| 166 Debug.State.currentSourceLine = -1; |
| 167 Debug.State.currentFrame = kNoFrame; |
| 168 } |
| 169 |
| 170 return details; |
| 171 } |
| 172 |
| 173 return 'Unknown debug event ' + event.eventType(); |
| 174 }; |
| 175 |
| 176 |
| 177 function SourceUnderline(source_text, position) { |
| 178 if (!source_text) { |
| 179 return; |
| 180 } |
| 181 |
| 182 // Create an underline with a caret pointing to the source position. If the
|
| 183 // source contains a tab character the underline will have a tab character in
|
| 184 // the same place otherwise the underline will have a space character.
|
| 185 var underline = ''; |
| 186 for (var i = 0; i < position; i++) { |
| 187 if (source_text[i] == '\t') { |
| 188 underline += '\t'; |
| 189 } else { |
| 190 underline += ' '; |
| 191 } |
| 192 } |
| 193 underline += '^'; |
| 194 |
| 195 // Return the source line text with the underline beneath. |
| 196 return source_text + '\n' + underline; |
| 197 }; |
| 198 |
| 199 |
| 200 function FrameSourceUnderline(frame) { |
| 201 var location = frame.sourceLocation(); |
| 202 if (location) { |
| 203 return SourceUnderline(location.sourceText(), |
| 204 location.position - location.start); |
| 205 } |
| 206 }; |
| 207 |
| 208 |
| 209 // Converts a text command to a JSON request. |
| 210 function DebugCommandToJSONRequest(cmd_line) { |
| 211 return new DebugRequest(cmd_line).JSONRequest(); |
| 212 }; |
| 213 |
| 214 |
| 215 function DebugRequest(cmd_line) { |
| 216 // If the very first character is a { assume that a JSON request have been |
| 217 // entered as a command. Converting that to a JSON request is trivial. |
| 218 if (cmd_line && cmd_line.length > 0 && cmd_line.charAt(0) == '{') { |
| 219 this.request_ = cmd_line; |
| 220 return; |
| 221 } |
| 222 |
| 223 // Trim string for leading and trailing whitespace. |
| 224 cmd_line = cmd_line.replace(/^\s+|\s+$/g, ''); |
| 225 |
| 226 // Find the command. |
| 227 var pos = cmd_line.indexOf(' '); |
| 228 var cmd; |
| 229 var args; |
| 230 if (pos == -1) { |
| 231 cmd = cmd_line; |
| 232 args = ''; |
| 233 } else { |
| 234 cmd = cmd_line.slice(0, pos); |
| 235 args = cmd_line.slice(pos).replace(/^\s+|\s+$/g, ''); |
| 236 } |
| 237 |
| 238 // Switch on command. |
| 239 switch (cmd) { |
| 240 case 'continue': |
| 241 case 'c': |
| 242 this.request_ = this.continueCommandToJSONRequest_(args); |
| 243 break; |
| 244 |
| 245 case 'step': |
| 246 case 's': |
| 247 this.request_ = this.stepCommandToJSONRequest_(args); |
| 248 break; |
| 249 |
| 250 case 'backtrace': |
| 251 case 'bt': |
| 252 this.request_ = this.backtraceCommandToJSONRequest_(args); |
| 253 break; |
| 254 |
| 255 case 'frame': |
| 256 case 'f': |
| 257 this.request_ = this.frameCommandToJSONRequest_(args); |
| 258 break; |
| 259 |
| 260 case 'print': |
| 261 case 'p': |
| 262 this.request_ = this.printCommandToJSONRequest_(args); |
| 263 break; |
| 264 |
| 265 case 'source': |
| 266 this.request_ = this.sourceCommandToJSONRequest_(args); |
| 267 break; |
| 268 |
| 269 case 'scripts': |
| 270 this.request_ = this.scriptsCommandToJSONRequest_(args); |
| 271 break; |
| 272 |
| 273 case 'break': |
| 274 case 'b': |
| 275 this.request_ = this.breakCommandToJSONRequest_(args); |
| 276 break; |
| 277 |
| 278 case 'clear': |
| 279 this.request_ = this.clearCommandToJSONRequest_(args); |
| 280 break; |
| 281 |
| 282 case 'help': |
| 283 case '?': |
| 284 this.helpCommand_(args); |
| 285 // Return null to indicate no JSON to send (command handled internally). |
| 286 this.request_ = void 0; |
| 287 break; |
| 288 |
| 289 default: |
| 290 throw new Error('Unknown command "' + cmd + '"'); |
| 291 } |
| 292 } |
| 293 |
| 294 DebugRequest.prototype.JSONRequest = function() { |
| 295 return this.request_; |
| 296 } |
| 297 |
| 298 |
| 299 function RequestPacket(command) { |
| 300 this.seq = 0; |
| 301 this.type = 'request'; |
| 302 this.command = command; |
| 303 } |
| 304 |
| 305 |
| 306 RequestPacket.prototype.toJSONProtocol = function() { |
| 307 // Encode the protocol header. |
| 308 var json = '{'; |
| 309 json += '"seq":' + this.seq; |
| 310 json += ',"type":"' + this.type + '"'; |
| 311 if (this.command) { |
| 312 json += ',"command":' + StringToJSON_(this.command); |
| 313 } |
| 314 if (this.arguments) { |
| 315 json += ',"arguments":'; |
| 316 // Encode the arguments part. |
| 317 if (this.arguments.toJSONProtocol) { |
| 318 json += this.arguments.toJSONProtocol() |
| 319 } else { |
| 320 json += SimpleObjectToJSON_(this.arguments); |
| 321 } |
| 322 } |
| 323 json += '}'; |
| 324 return json; |
| 325 } |
| 326 |
| 327 |
| 328 DebugRequest.prototype.createRequest = function(command) { |
| 329 return new RequestPacket(command); |
| 330 }; |
| 331 |
| 332 |
| 333 // Create a JSON request for the continue command. |
| 334 DebugRequest.prototype.continueCommandToJSONRequest_ = function(args) { |
| 335 var request = this.createRequest('continue'); |
| 336 return request.toJSONProtocol(); |
| 337 }; |
| 338 |
| 339 |
| 340 // Create a JSON request for the step command. |
| 341 DebugRequest.prototype.stepCommandToJSONRequest_ = function(args) { |
| 342 // Requesting a step is through the continue command with additional |
| 343 // arguments. |
| 344 var request = this.createRequest('continue'); |
| 345 request.arguments = {}; |
| 346 |
| 347 // Process arguments if any. |
| 348 if (args && args.length > 0) { |
| 349 args = args.split(/\s*[ ]+\s*/g); |
| 350 |
| 351 if (args.length > 2) { |
| 352 throw new Error('Invalid step arguments.'); |
| 353 } |
| 354 |
| 355 if (args.length > 0) { |
| 356 // Get step count argument if any. |
| 357 if (args.length == 2) { |
| 358 var stepcount = parseInt(args[1]); |
| 359 if (isNaN(stepcount) || stepcount <= 0) { |
| 360 throw new Error('Invalid step count argument "' + args[0] + '".'); |
| 361 } |
| 362 request.arguments.stepcount = stepcount; |
| 363 } |
| 364 |
| 365 // Get the step action. |
| 366 switch (args[0]) { |
| 367 case 'in': |
| 368 case 'i': |
| 369 request.arguments.stepaction = 'in'; |
| 370 break; |
| 371 |
| 372 case 'min': |
| 373 case 'm': |
| 374 request.arguments.stepaction = 'min'; |
| 375 break; |
| 376 |
| 377 case 'next': |
| 378 case 'n': |
| 379 request.arguments.stepaction = 'next'; |
| 380 break; |
| 381 |
| 382 case 'out': |
| 383 case 'o': |
| 384 request.arguments.stepaction = 'out'; |
| 385 break; |
| 386 |
| 387 default: |
| 388 throw new Error('Invalid step argument "' + args[0] + '".'); |
| 389 } |
| 390 } |
| 391 } else { |
| 392 // Default is step next. |
| 393 request.arguments.stepaction = 'next'; |
| 394 } |
| 395 |
| 396 return request.toJSONProtocol(); |
| 397 }; |
| 398 |
| 399 |
| 400 // Create a JSON request for the backtrace command. |
| 401 DebugRequest.prototype.backtraceCommandToJSONRequest_ = function(args) { |
| 402 // Build a backtrace request from the text command. |
| 403 var request = this.createRequest('backtrace'); |
| 404 args = args.split(/\s*[ ]+\s*/g); |
| 405 if (args.length == 2) { |
| 406 request.arguments = {}; |
| 407 var fromFrame = parseInt(args[0]); |
| 408 var toFrame = parseInt(args[1]); |
| 409 if (isNaN(fromFrame) || fromFrame < 0) { |
| 410 throw new Error('Invalid start frame argument "' + args[0] + '".'); |
| 411 } |
| 412 if (isNaN(toFrame) || toFrame < 0) { |
| 413 throw new Error('Invalid end frame argument "' + args[1] + '".'); |
| 414 } |
| 415 if (fromFrame > toFrame) { |
| 416 throw new Error('Invalid arguments start frame cannot be larger ' + |
| 417 'than end frame.'); |
| 418 } |
| 419 request.arguments.fromFrame = fromFrame; |
| 420 request.arguments.toFrame = toFrame + 1; |
| 421 } |
| 422 return request.toJSONProtocol(); |
| 423 }; |
| 424 |
| 425 |
| 426 // Create a JSON request for the frame command. |
| 427 DebugRequest.prototype.frameCommandToJSONRequest_ = function(args) { |
| 428 // Build a frame request from the text command. |
| 429 var request = this.createRequest('frame'); |
| 430 args = args.split(/\s*[ ]+\s*/g); |
| 431 if (args.length > 0 && args[0].length > 0) { |
| 432 request.arguments = {}; |
| 433 request.arguments.number = args[0]; |
| 434 } |
| 435 return request.toJSONProtocol(); |
| 436 }; |
| 437 |
| 438 |
| 439 // Create a JSON request for the print command. |
| 440 DebugRequest.prototype.printCommandToJSONRequest_ = function(args) { |
| 441 // Build a evaluate request from the text command. |
| 442 var request = this.createRequest('evaluate'); |
| 443 if (args.length == 0) { |
| 444 throw new Error('Missing expression.'); |
| 445 } |
| 446 |
| 447 request.arguments = {}; |
| 448 request.arguments.expression = args; |
| 449 |
| 450 return request.toJSONProtocol(); |
| 451 }; |
| 452 |
| 453 |
| 454 // Create a JSON request for the source command. |
| 455 DebugRequest.prototype.sourceCommandToJSONRequest_ = function(args) { |
| 456 // Build a evaluate request from the text command. |
| 457 var request = this.createRequest('source'); |
| 458 |
| 459 // Default is ten lines starting five lines before the current location. |
| 460 var from = Debug.State.currentSourceLine - 5; |
| 461 var lines = 10; |
| 462 |
| 463 // Parse the arguments. |
| 464 args = args.split(/\s*[ ]+\s*/g); |
| 465 if (args.length > 1 && args[0].length > 0 && args[1].length > 0) { |
| 466 from = parseInt(args[0]) - 1; |
| 467 lines = parseInt(args[1]); |
| 468 } else if (args.length > 0 && args[0].length > 0) { |
| 469 from = parseInt(args[0]) - 1; |
| 470 } |
| 471 |
| 472 if (from < 0) from = 0; |
| 473 if (lines < 0) lines = 10; |
| 474 |
| 475 // Request source arround current source location. |
| 476 request.arguments = {}; |
| 477 request.arguments.fromLine = from; |
| 478 request.arguments.toLine = from + lines; |
| 479 |
| 480 return request.toJSONProtocol(); |
| 481 }; |
| 482 |
| 483 |
| 484 // Create a JSON request for the scripts command. |
| 485 DebugRequest.prototype.scriptsCommandToJSONRequest_ = function(args) { |
| 486 // Build a evaluate request from the text command. |
| 487 var request = this.createRequest('scripts'); |
| 488 |
| 489 // Process arguments if any. |
| 490 if (args && args.length > 0) { |
| 491 args = args.split(/\s*[ ]+\s*/g); |
| 492 |
| 493 if (args.length > 1) { |
| 494 throw new Error('Invalid scripts arguments.'); |
| 495 } |
| 496 |
| 497 request.arguments = {}; |
| 498 switch (args[0]) { |
| 499 case 'natives': |
| 500 request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Native); |
| 501 break; |
| 502 |
| 503 case 'extensions': |
| 504 request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Extension); |
| 505 break; |
| 506 |
| 507 case 'all': |
| 508 request.arguments.types = |
| 509 ScriptTypeFlag(Debug.ScriptType.Normal) | |
| 510 ScriptTypeFlag(Debug.ScriptType.Native) | |
| 511 ScriptTypeFlag(Debug.ScriptType.Extension); |
| 512 break; |
| 513 |
| 514 default: |
| 515 throw new Error('Invalid argument "' + args[0] + '".'); |
| 516 } |
| 517 } |
| 518 |
| 519 return request.toJSONProtocol(); |
| 520 }; |
| 521 |
| 522 |
| 523 // Create a JSON request for the break command. |
| 524 DebugRequest.prototype.breakCommandToJSONRequest_ = function(args) { |
| 525 // Build a evaluate request from the text command. |
| 526 var request = this.createRequest('setbreakpoint'); |
| 527 |
| 528 // Process arguments if any. |
| 529 if (args && args.length > 0) { |
| 530 var target = args; |
| 531 var condition; |
| 532 |
| 533 var pos = args.indexOf(' '); |
| 534 if (pos > 0) { |
| 535 target = args.substring(0, pos); |
| 536 condition = args.substring(pos + 1, args.length); |
| 537 } |
| 538 |
| 539 request.arguments = {}; |
| 540 request.arguments.type = 'function'; |
| 541 request.arguments.target = target; |
| 542 request.arguments.condition = condition; |
| 543 } else { |
| 544 throw new Error('Invalid break arguments.'); |
| 545 } |
| 546 |
| 547 return request.toJSONProtocol(); |
| 548 }; |
| 549 |
| 550 |
| 551 // Create a JSON request for the clear command. |
| 552 DebugRequest.prototype.clearCommandToJSONRequest_ = function(args) { |
| 553 // Build a evaluate request from the text command. |
| 554 var request = this.createRequest('clearbreakpoint'); |
| 555 |
| 556 // Process arguments if any. |
| 557 if (args && args.length > 0) { |
| 558 request.arguments = {}; |
| 559 request.arguments.breakpoint = parseInt(args); |
| 560 } else { |
| 561 throw new Error('Invalid break arguments.'); |
| 562 } |
| 563 |
| 564 return request.toJSONProtocol(); |
| 565 }; |
| 566 |
| 567 |
| 568 // Create a JSON request for the break command. |
| 569 DebugRequest.prototype.helpCommand_ = function(args) { |
| 570 // Help os quite simple. |
| 571 if (args && args.length > 0) { |
| 572 print('warning: arguments to \'help\' are ignored'); |
| 573 } |
| 574 |
| 575 print('break location [condition]'); |
| 576 print('clear <breakpoint #>'); |
| 577 print('backtrace [from frame #] [to frame #]]'); |
| 578 print('frame <frame #>'); |
| 579 print('step [in | next | out| min [step count]]'); |
| 580 print('print <expression>'); |
| 581 print('source [from line [num lines]]'); |
| 582 print('scripts'); |
| 583 print('continue'); |
| 584 print('help'); |
| 585 } |
| 586 |
| 587 |
| 588 // Convert a JSON response to text for display in a text based debugger. |
| 589 function DebugResponseDetails(json_response) { |
| 590 details = {text:'', running:false} |
| 591 |
| 592 try { |
| 593 // Convert the JSON string to an object. |
| 594 response = eval('(' + json_response + ')'); |
| 595 |
| 596 if (!response.success) { |
| 597 details.text = response.message; |
| 598 return details; |
| 599 } |
| 600 |
| 601 // Get the running state. |
| 602 details.running = response.running; |
| 603 |
| 604 switch (response.command) { |
| 605 case 'setbreakpoint': |
| 606 var body = response.body; |
| 607 result = 'set breakpoint #'; |
| 608 result += body.breakpoint; |
| 609 details.text = result; |
| 610 break; |
| 611 |
| 612 case 'clearbreakpoint': |
| 613 var body = response.body; |
| 614 result = 'cleared breakpoint #'; |
| 615 result += body.breakpoint; |
| 616 details.text = result; |
| 617 break; |
| 618 |
| 619 case 'backtrace': |
| 620 var body = response.body; |
| 621 if (body.totalFrames == 0) { |
| 622 result = '(empty stack)'; |
| 623 } else { |
| 624 var result = 'Frames #' + body.fromFrame + ' to #' + |
| 625 (body.toFrame - 1) + ' of ' + body.totalFrames + '\n'; |
| 626 for (i = 0; i < body.frames.length; i++) { |
| 627 if (i != 0) result += '\n'; |
| 628 result += body.frames[i].text; |
| 629 } |
| 630 } |
| 631 details.text = result; |
| 632 break; |
| 633 |
| 634 case 'frame': |
| 635 details.text = SourceUnderline(response.body.sourceLineText, |
| 636 response.body.column); |
| 637 Debug.State.currentSourceLine = response.body.line; |
| 638 Debug.State.currentFrame = response.body.index; |
| 639 break; |
| 640 |
| 641 case 'evaluate': |
| 642 details.text = response.body.text; |
| 643 break; |
| 644 |
| 645 case 'source': |
| 646 // Get the source from the response. |
| 647 var source = response.body.source; |
| 648 var from_line = response.body.fromLine + 1; |
| 649 var lines = source.split('\n'); |
| 650 var maxdigits = 1 + Math.floor(log10(from_line + lines.length)); |
| 651 if (maxdigits < 3) { |
| 652 maxdigits = 3; |
| 653 } |
| 654 var result = ''; |
| 655 for (var num = 0; num < lines.length; num++) { |
| 656 // Check if there's an extra newline at the end. |
| 657 if (num == (lines.length - 1) && lines[num].length == 0) { |
| 658 break; |
| 659 } |
| 660 |
| 661 var current_line = from_line + num; |
| 662 spacer = maxdigits - (1 + Math.floor(log10(current_line))); |
| 663 if (current_line == Debug.State.currentSourceLine + 1) { |
| 664 for (var i = 0; i < maxdigits; i++) { |
| 665 result += '>'; |
| 666 } |
| 667 result += ' '; |
| 668 } else { |
| 669 for (var i = 0; i < spacer; i++) { |
| 670 result += ' '; |
| 671 } |
| 672 result += current_line + ': '; |
| 673 } |
| 674 result += lines[num]; |
| 675 result += '\n'; |
| 676 } |
| 677 details.text = result; |
| 678 break; |
| 679 |
| 680 case 'scripts': |
| 681 var result = ''; |
| 682 for (i = 0; i < response.body.length; i++) { |
| 683 if (i != 0) result += '\n'; |
| 684 if (response.body[i].name) { |
| 685 result += response.body[i].name; |
| 686 } else { |
| 687 result += '[unnamed] '; |
| 688 var sourceStart = response.body[i].sourceStart; |
| 689 if (sourceStart.length > 40) { |
| 690 sourceStart = sourceStart.substring(0, 37) + '...'; |
| 691 } |
| 692 result += sourceStart; |
| 693 } |
| 694 result += ' (lines: '; |
| 695 result += response.body[i].sourceLines; |
| 696 result += ', length: '; |
| 697 result += response.body[i].sourceLength; |
| 698 if (response.body[i].type == Debug.ScriptType.Native) { |
| 699 result += ', native'; |
| 700 } else if (response.body[i].type == Debug.ScriptType.Extension) { |
| 701 result += ', extension'; |
| 702 } |
| 703 result += ')'; |
| 704 } |
| 705 details.text = result; |
| 706 break; |
| 707 |
| 708 default: |
| 709 details.text = |
| 710 'Response for unknown command \'' + response.command + '\''; |
| 711 } |
| 712 } catch (e) { |
| 713 details.text = 'Error: "' + e + '" formatting response'; |
| 714 } |
| 715 |
| 716 return details; |
| 717 }; |
| 718 |
| 719 |
| 720 function MakeJSONPair_(name, value) { |
| 721 return '"' + name + '":' + value; |
| 722 } |
| 723 |
| 724 |
| 725 function ArrayToJSONObject_(content) { |
| 726 return '{' + content.join(',') + '}'; |
| 727 } |
| 728 |
| 729 |
| 730 function ArrayToJSONArray_(content) { |
| 731 return '[' + content.join(',') + ']'; |
| 732 } |
| 733 |
| 734 |
| 735 function BooleanToJSON_(value) { |
| 736 return String(value); |
| 737 } |
| 738 |
| 739 |
| 740 function NumberToJSON_(value) { |
| 741 return String(value); |
| 742 } |
| 743 |
| 744 |
| 745 // Mapping of some control characters to avoid the \uXXXX syntax for most |
| 746 // commonly used control cahracters. |
| 747 const ctrlCharMap_ = { |
| 748 '\b': '\\b', |
| 749 '\t': '\\t', |
| 750 '\n': '\\n', |
| 751 '\f': '\\f', |
| 752 '\r': '\\r', |
| 753 '"' : '\\"', |
| 754 '\\': '\\\\' |
| 755 }; |
| 756 |
| 757 |
| 758 // Regular expression testing for ", \ and control characters (0x00 - 0x1F). |
| 759 const ctrlCharTest_ = new RegExp('["\\\\\x00-\x1F]'); |
| 760 |
| 761 |
| 762 // Regular expression matching ", \ and control characters (0x00 - 0x1F) |
| 763 // globally. |
| 764 const ctrlCharMatch_ = new RegExp('["\\\\\x00-\x1F]', 'g'); |
| 765 |
| 766 |
| 767 /** |
| 768 * Convert a String to its JSON representation (see http://www.json.org/). To |
| 769 * avoid depending on the String object this method calls the functions in |
| 770 * string.js directly and not through the value. |
| 771 * @param {String} value The String value to format as JSON |
| 772 * @return {string} JSON formatted String value |
| 773 */ |
| 774 function StringToJSON_(value) { |
| 775 // Check for" , \ and control characters (0x00 - 0x1F). No need to call |
| 776 // RegExpTest as ctrlchar is constructed using RegExp. |
| 777 if (ctrlCharTest_.test(value)) { |
| 778 // Replace ", \ and control characters (0x00 - 0x1F). |
| 779 return '"' + |
| 780 value.replace(ctrlCharMatch_, function (char) { |
| 781 // Use charmap if possible. |
| 782 var mapped = ctrlCharMap_[char]; |
| 783 if (mapped) return mapped; |
| 784 mapped = char.charCodeAt(); |
| 785 // Convert control character to unicode escape sequence. |
| 786 return '\\u00' + |
| 787 '0' + // TODO %NumberToRadixString(Math.floor(mapped / 16), 16) + |
| 788 '0' // TODO %NumberToRadixString(mapped % 16, 16); |
| 789 }) |
| 790 + '"'; |
| 791 } |
| 792 |
| 793 // Simple string with no special characters. |
| 794 return '"' + value + '"'; |
| 795 } |
| 796 |
| 797 |
| 798 /** |
| 799 * Convert a Date to ISO 8601 format. To avoid depending on the Date object |
| 800 * this method calls the functions in date.js directly and not through the |
| 801 * value. |
| 802 * @param {Date} value The Date value to format as JSON |
| 803 * @return {string} JSON formatted Date value |
| 804 */ |
| 805 function DateToISO8601_(value) { |
| 806 function f(n) { |
| 807 return n < 10 ? '0' + n : n; |
| 808 } |
| 809 function g(n) { |
| 810 return n < 10 ? '00' + n : n < 100 ? '0' + n : n; |
| 811 } |
| 812 return builtins.GetUTCFullYearFrom(value) + '-' + |
| 813 f(builtins.GetUTCMonthFrom(value) + 1) + '-' + |
| 814 f(builtins.GetUTCDateFrom(value)) + 'T' + |
| 815 f(builtins.GetUTCHoursFrom(value)) + ':' + |
| 816 f(builtins.GetUTCMinutesFrom(value)) + ':' + |
| 817 f(builtins.GetUTCSecondsFrom(value)) + '.' + |
| 818 g(builtins.GetUTCMillisecondsFrom(value)) + 'Z'; |
| 819 } |
| 820 |
| 821 |
| 822 /** |
| 823 * Convert a Date to ISO 8601 format. To avoid depending on the Date object |
| 824 * this method calls the functions in date.js directly and not through the |
| 825 * value. |
| 826 * @param {Date} value The Date value to format as JSON |
| 827 * @return {string} JSON formatted Date value |
| 828 */ |
| 829 function DateToJSON_(value) { |
| 830 return '"' + DateToISO8601_(value) + '"'; |
| 831 } |
| 832 |
| 833 |
| 834 /** |
| 835 * Convert an Object to its JSON representation (see http://www.json.org/). |
| 836 * This implementation simply runs through all string property names and adds |
| 837 * each property to the JSON representation for some predefined types. For type |
| 838 * "object" the function calls itself recursively unless the object has the |
| 839 * function property "toJSONProtocol" in which case that is used. This is not |
| 840 * a general implementation but sufficient for the debugger. Note that circular |
| 841 * structures will cause infinite recursion. |
| 842 * @param {Object} object The object to format as JSON |
| 843 * @return {string} JSON formatted object value |
| 844 */ |
| 845 function SimpleObjectToJSON_(object) { |
| 846 var content = []; |
| 847 for (var key in object) { |
| 848 // Only consider string keys. |
| 849 if (typeof key == 'string') { |
| 850 var property_value = object[key]; |
| 851 |
| 852 // Format the value based on its type. |
| 853 var property_value_json; |
| 854 switch (typeof property_value) { |
| 855 case 'object': |
| 856 if (typeof property_value.toJSONProtocol == 'function') { |
| 857 property_value_json = property_value.toJSONProtocol(true) |
| 858 } else if (property_value.constructor.name == 'Array'){ |
| 859 property_value_json = SimpleArrayToJSON_(property_value); |
| 860 } else { |
| 861 property_value_json = SimpleObjectToJSON_(property_value); |
| 862 } |
| 863 break; |
| 864 |
| 865 case 'boolean': |
| 866 property_value_json = BooleanToJSON_(property_value); |
| 867 break; |
| 868 |
| 869 case 'number': |
| 870 property_value_json = NumberToJSON_(property_value); |
| 871 break; |
| 872 |
| 873 case 'string': |
| 874 property_value_json = StringToJSON_(property_value); |
| 875 break; |
| 876 |
| 877 default: |
| 878 property_value_json = null; |
| 879 } |
| 880 |
| 881 // Add the property if relevant. |
| 882 if (property_value_json) { |
| 883 content.push(StringToJSON_(key) + ':' + property_value_json); |
| 884 } |
| 885 } |
| 886 } |
| 887 |
| 888 // Make JSON object representation. |
| 889 return '{' + content.join(',') + '}'; |
| 890 } |
| 891 |
| 892 |
| 893 /** |
| 894 * Convert an array to its JSON representation. This is a VERY simple |
| 895 * implementation just to support what is needed for the debugger. |
| 896 * @param {Array} arrya The array to format as JSON |
| 897 * @return {string} JSON formatted array value |
| 898 */ |
| 899 function SimpleArrayToJSON_(array) { |
| 900 // Make JSON array representation. |
| 901 var json = '['; |
| 902 for (var i = 0; i < array.length; i++) { |
| 903 if (i != 0) { |
| 904 json += ','; |
| 905 } |
| 906 var elem = array[i]; |
| 907 if (elem.toJSONProtocol) { |
| 908 json += elem.toJSONProtocol(true) |
| 909 } else if (typeof(elem) === 'object') { |
| 910 json += SimpleObjectToJSON_(elem); |
| 911 } else if (typeof(elem) === 'boolean') { |
| 912 json += BooleanToJSON_(elem); |
| 913 } else if (typeof(elem) === 'number') { |
| 914 json += NumberToJSON_(elem); |
| 915 } else if (typeof(elem) === 'string') { |
| 916 json += StringToJSON_(elem); |
| 917 } else { |
| 918 json += elem; |
| 919 } |
| 920 } |
| 921 json += ']'; |
| 922 return json; |
| 923 } |
OLD | NEW |