1 // Copyright 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 String.prototype.startsWith = function (str) { 29 if (str.length > this.length) 30 return false; 31 return this.substr(0, str.length) == str; 32 } 33 34 function log10(num) { 35 return Math.log(num)/Math.log(10); 36 } 37 38 function ToInspectableObject(obj) { 39 if (!obj && typeof obj === 'object') { 40 return void 0; 41 } else { 42 return Object(obj); 43 } 44 } 45 46 function GetCompletions(global, last, full) { 47 var full_tokens = full.split(); 48 full = full_tokens.pop(); 49 var parts = full.split('.'); 50 parts.pop(); 51 var current = global; 52 for (var i = 0; i < parts.length; i++) { 53 var part = parts[i]; 54 var next = current[part]; 55 if (!next) 56 return []; 57 current = next; 58 } 59 var result = []; 60 current = ToInspectableObject(current); 61 while (typeof current !== 'undefined') { 62 var mirror = new $debug.ObjectMirror(current); 63 var properties = mirror.properties(); 64 for (var i = 0; i < properties.length; i++) { 65 var name = properties[i].name(); 66 if (typeof name === 'string' && name.startsWith(last)) 67 result.push(name); 68 } 69 current = ToInspectableObject(current.__proto__); 70 } 71 return result; 72 } 73 74 75 // Global object holding debugger related constants and state. 76 const Debug = {}; 77 78 79 // Debug events which can occour in the V8 JavaScript engine. These originate 80 // from the API include file v8-debug.h. 81 Debug.DebugEvent = { Break: 1, 82 Exception: 2, 83 NewFunction: 3, 84 BeforeCompile: 4, 85 AfterCompile: 5 }; 86 87 88 // The different types of scripts matching enum ScriptType in objects.h. 89 Debug.ScriptType = { Native: 0, 90 Extension: 1, 91 Normal: 2 }; 92 93 94 // The different types of script compilations matching enum 95 // Script::CompilationType in objects.h. 96 Debug.ScriptCompilationType = { Host: 0, 97 Eval: 1, 98 JSON: 2 }; 99 100 101 // The different types of scopes matching constants runtime.cc. 102 Debug.ScopeType = { Global: 0, 103 Local: 1, 104 With: 2, 105 Closure: 3, 106 Catch: 4 }; 107 108 109 // Current debug state. 110 const kNoFrame = -1; 111 Debug.State = { 112 currentFrame: kNoFrame, 113 displaySourceStartLine: -1, 114 displaySourceEndLine: -1, 115 currentSourceLine: -1 116 } 117 var trace_compile = false; // Tracing all compile events? 118 var trace_debug_json = false; // Tracing all debug json packets? 119 var last_cmd_line = ''; 120 //var lol_is_enabled; // Set to true in d8.cc if LIVE_OBJECT_LIST is defined. 121 var lol_next_dump_index = 0; 122 const kDefaultLolLinesToPrintAtATime = 10; 123 const kMaxLolLinesToPrintAtATime = 1000; 124 var repeat_cmd_line = ''; 125 var is_running = true; 126 127 // Copied from debug-delay.js. This is needed below: 128 function ScriptTypeFlag(type) { 129 return (1 << type); 130 } 131 132 133 // Process a debugger JSON message into a display text and a running status. 134 // This function returns an object with properties "text" and "running" holding 135 // this information. 136 function DebugMessageDetails(message) { 137 if (trace_debug_json) { 138 print("received: '" + message + "'"); 139 } 140 // Convert the JSON string to an object. 141 var response = new ProtocolPackage(message); 142 is_running = response.running(); 143 144 if (response.type() == 'event') { 145 return DebugEventDetails(response); 146 } else { 147 return DebugResponseDetails(response); 148 } 149 } 150 151 function DebugEventDetails(response) { 152 details = {text:'', running:false} 153 154 // Get the running state. 155 details.running = response.running(); 156 157 var body = response.body(); 158 var result = ''; 159 switch (response.event()) { 160 case 'break': 161 if (body.breakpoints) { 162 result += 'breakpoint'; 163 if (body.breakpoints.length > 1) { 164 result += 's'; 165 } 166 result += ' #'; 167 for (var i = 0; i < body.breakpoints.length; i++) { 168 if (i > 0) { 169 result += ', #'; 170 } 171 result += body.breakpoints[i]; 172 } 173 } else { 174 result += 'break'; 175 } 176 result += ' in '; 177 result += body.invocationText; 178 result += ', '; 179 result += SourceInfo(body); 180 result += '\n'; 181 result += SourceUnderline(body.sourceLineText, body.sourceColumn); 182 Debug.State.currentSourceLine = body.sourceLine; 183 Debug.State.displaySourceStartLine = -1; 184 Debug.State.displaySourceEndLine = -1; 185 Debug.State.currentFrame = 0; 186 details.text = result; 187 break; 188 189 case 'exception': 190 if (body.uncaught) { 191 result += 'Uncaught: '; 192 } else { 193 result += 'Exception: '; 194 } 195 result += '"'; 196 result += body.exception.text; 197 result += '"'; 198 if (body.sourceLine >= 0) { 199 result += ', '; 200 result += SourceInfo(body); 201 result += '\n'; 202 result += SourceUnderline(body.sourceLineText, body.sourceColumn); 203 Debug.State.currentSourceLine = body.sourceLine; 204 Debug.State.displaySourceStartLine = -1; 205 Debug.State.displaySourceEndLine = -1; 206 Debug.State.currentFrame = 0; 207 } else { 208 result += ' (empty stack)'; 209 Debug.State.currentSourceLine = -1; 210 Debug.State.displaySourceStartLine = -1; 211 Debug.State.displaySourceEndLine = -1; 212 Debug.State.currentFrame = kNoFrame; 213 } 214 details.text = result; 215 break; 216 217 case 'afterCompile': 218 if (trace_compile) { 219 result = 'Source ' + body.script.name + ' compiled:\n' 220 var source = body.script.source; 221 if (!(source[source.length - 1] == '\n')) { 222 result += source; 223 } else { 224 result += source.substring(0, source.length - 1); 225 } 226 } 227 details.text = result; 228 break; 229 230 case 'scriptCollected': 231 details.text = result; 232 break; 233 234 default: 235 details.text = 'Unknown debug event ' + response.event(); 236 } 237 238 return details; 239 }; 240 241 242 function SourceInfo(body) { 243 var result = ''; 244 245 if (body.script) { 246 if (body.script.name) { 247 result += body.script.name; 248 } else { 249 result += '[unnamed]'; 250 } 251 } 252 result += ' line '; 253 result += body.sourceLine + 1; 254 result += ' column '; 255 result += body.sourceColumn + 1; 256 257 return result; 258 } 259 260 261 function SourceUnderline(source_text, position) { 262 if (!source_text) { 263 return; 264 } 265 266 // Create an underline with a caret pointing to the source position. If the 267 // source contains a tab character the underline will have a tab character in 268 // the same place otherwise the underline will have a space character. 269 var underline = ''; 270 for (var i = 0; i < position; i++) { 271 if (source_text[i] == '\t') { 272 underline += '\t'; 273 } else { 274 underline += ' '; 275 } 276 } 277 underline += '^'; 278 279 // Return the source line text with the underline beneath. 280 return source_text + '\n' + underline; 281 }; 282 283 284 // Converts a text command to a JSON request. 285 function DebugCommandToJSONRequest(cmd_line) { 286 var result = new DebugRequest(cmd_line).JSONRequest(); 287 if (trace_debug_json && result) { 288 print("sending: '" + result + "'"); 289 } 290 return result; 291 }; 292 293 294 function DebugRequest(cmd_line) { 295 // If the very first character is a { assume that a JSON request have been 296 // entered as a command. Converting that to a JSON request is trivial. 297 if (cmd_line && cmd_line.length > 0 && cmd_line.charAt(0) == '{') { 298 this.request_ = cmd_line; 299 return; 300 } 301 302 // Check for a simple carriage return to repeat the last command: 303 var is_repeating = false; 304 if (cmd_line == '\n') { 305 if (is_running) { 306 cmd_line = 'break'; // Not in debugger mode, break with a frame request. 307 } else { 308 cmd_line = repeat_cmd_line; // use command to repeat. 309 is_repeating = true; 310 } 311 } 312 if (!is_running) { // Only save the command if in debugger mode. 313 repeat_cmd_line = cmd_line; // save last command. 314 } 315 316 // Trim string for leading and trailing whitespace. 317 cmd_line = cmd_line.replace(/^\s+|\s+$/g, ''); 318 319 // Find the command. 320 var pos = cmd_line.indexOf(' '); 321 var cmd; 322 var args; 323 if (pos == -1) { 324 cmd = cmd_line; 325 args = ''; 326 } else { 327 cmd = cmd_line.slice(0, pos); 328 args = cmd_line.slice(pos).replace(/^\s+|\s+$/g, ''); 329 } 330 331 if ((cmd === undefined) || !cmd) { 332 this.request_ = void 0; 333 return; 334 } 335 336 last_cmd = cmd; 337 338 // Switch on command. 339 switch (cmd) { 340 case 'continue': 341 case 'c': 342 this.request_ = this.continueCommandToJSONRequest_(args); 343 break; 344 345 case 'step': 346 case 's': 347 this.request_ = this.stepCommandToJSONRequest_(args, 'in'); 348 break; 349 350 case 'stepi': 351 case 'si': 352 this.request_ = this.stepCommandToJSONRequest_(args, 'min'); 353 break; 354 355 case 'next': 356 case 'n': 357 this.request_ = this.stepCommandToJSONRequest_(args, 'next'); 358 break; 359 360 case 'finish': 361 case 'fin': 362 this.request_ = this.stepCommandToJSONRequest_(args, 'out'); 363 break; 364 365 case 'backtrace': 366 case 'bt': 367 this.request_ = this.backtraceCommandToJSONRequest_(args); 368 break; 369 370 case 'frame': 371 case 'f': 372 this.request_ = this.frameCommandToJSONRequest_(args); 373 break; 374 375 case 'scopes': 376 this.request_ = this.scopesCommandToJSONRequest_(args); 377 break; 378 379 case 'scope': 380 this.request_ = this.scopeCommandToJSONRequest_(args); 381 break; 382 383 case 'disconnect': 384 case 'exit': 385 case 'quit': 386 this.request_ = this.disconnectCommandToJSONRequest_(args); 387 break; 388 389 case 'up': 390 this.request_ = 391 this.frameCommandToJSONRequest_('' + 392 (Debug.State.currentFrame + 1)); 393 break; 394 395 case 'down': 396 case 'do': 397 this.request_ = 398 this.frameCommandToJSONRequest_('' + 399 (Debug.State.currentFrame - 1)); 400 break; 401 402 case 'set': 403 case 'print': 404 case 'p': 405 this.request_ = this.printCommandToJSONRequest_(args); 406 break; 407 408 case 'dir': 409 this.request_ = this.dirCommandToJSONRequest_(args); 410 break; 411 412 case 'references': 413 this.request_ = this.referencesCommandToJSONRequest_(args); 414 break; 415 416 case 'instances': 417 this.request_ = this.instancesCommandToJSONRequest_(args); 418 break; 419 420 case 'list': 421 case 'l': 422 this.request_ = this.listCommandToJSONRequest_(args); 423 break; 424 case 'source': 425 this.request_ = this.sourceCommandToJSONRequest_(args); 426 break; 427 428 case 'scripts': 429 case 'script': 430 case 'scr': 431 this.request_ = this.scriptsCommandToJSONRequest_(args); 432 break; 433 434 case 'break': 435 case 'b': 436 this.request_ = this.breakCommandToJSONRequest_(args); 437 break; 438 439 case 'breakpoints': 440 case 'bb': 441 this.request_ = this.breakpointsCommandToJSONRequest_(args); 442 break; 443 444 case 'clear': 445 case 'delete': 446 case 'd': 447 this.request_ = this.clearCommandToJSONRequest_(args); 448 break; 449 450 case 'threads': 451 this.request_ = this.threadsCommandToJSONRequest_(args); 452 break; 453 454 case 'cond': 455 this.request_ = this.changeBreakpointCommandToJSONRequest_(args, 'cond'); 456 break; 457 458 case 'enable': 459 case 'en': 460 this.request_ = 461 this.changeBreakpointCommandToJSONRequest_(args, 'enable'); 462 break; 463 464 case 'disable': 465 case 'dis': 466 this.request_ = 467 this.changeBreakpointCommandToJSONRequest_(args, 'disable'); 468 break; 469 470 case 'ignore': 471 this.request_ = 472 this.changeBreakpointCommandToJSONRequest_(args, 'ignore'); 473 break; 474 475 case 'info': 476 case 'inf': 477 this.request_ = this.infoCommandToJSONRequest_(args); 478 break; 479 480 case 'flags': 481 this.request_ = this.v8FlagsToJSONRequest_(args); 482 break; 483 484 case 'gc': 485 this.request_ = this.gcToJSONRequest_(args); 486 break; 487 488 case 'trace': 489 case 'tr': 490 // Return undefined to indicate command handled internally (no JSON). 491 this.request_ = void 0; 492 this.traceCommand_(args); 493 break; 494 495 case 'help': 496 case '?': 497 this.helpCommand_(args); 498 // Return undefined to indicate command handled internally (no JSON). 499 this.request_ = void 0; 500 break; 501 502 case 'liveobjectlist': 503 case 'lol': 504 if (lol_is_enabled) { 505 this.request_ = this.lolToJSONRequest_(args, is_repeating); 506 break; 507 } 508 509 default: 510 throw new Error('Unknown command "' + cmd + '"'); 511 } 512 } 513 514 DebugRequest.prototype.JSONRequest = function() { 515 return this.request_; 516 } 517 518 519 function RequestPacket(command) { 520 this.seq = 0; 521 this.type = 'request'; 522 this.command = command; 523 } 524 525 526 RequestPacket.prototype.toJSONProtocol = function() { 527 // Encode the protocol header. 528 var json = '{'; 529 json += '"seq":' + this.seq; 530 json += ',"type":"' + this.type + '"'; 531 if (this.command) { 532 json += ',"command":' + StringToJSON_(this.command); 533 } 534 if (this.arguments) { 535 json += ',"arguments":'; 536 // Encode the arguments part. 537 if (this.arguments.toJSONProtocol) { 538 json += this.arguments.toJSONProtocol() 539 } else { 540 json += SimpleObjectToJSON_(this.arguments); 541 } 542 } 543 json += '}'; 544 return json; 545 } 546 547 548 DebugRequest.prototype.createRequest = function(command) { 549 return new RequestPacket(command); 550 }; 551 552 553 // Note: we use detected command repetition as a signal for continuation here. 554 DebugRequest.prototype.createLOLRequest = function(command, 555 start_index, 556 lines_to_dump, 557 is_continuation) { 558 if (is_continuation) { 559 start_index = lol_next_dump_index; 560 } 561 562 if (lines_to_dump) { 563 lines_to_dump = parseInt(lines_to_dump); 564 } else { 565 lines_to_dump = kDefaultLolLinesToPrintAtATime; 566 } 567 if (lines_to_dump > kMaxLolLinesToPrintAtATime) { 568 lines_to_dump = kMaxLolLinesToPrintAtATime; 569 } 570 571 // Save the next start_index to dump from: 572 lol_next_dump_index = start_index + lines_to_dump; 573 574 var request = this.createRequest(command); 575 request.arguments = {}; 576 request.arguments.start = start_index; 577 request.arguments.count = lines_to_dump; 578 579 return request; 580 }; 581 582 583 // Create a JSON request for the evaluation command. 584 DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) { 585 // Global varaible used to store whether a handle was requested. 586 lookup_handle = null; 587 588 if (lol_is_enabled) { 589 // Check if the expression is a obj id in the form @<obj id>. 590 var obj_id_match = expression.match(/^@([0-9]+)$/); 591 if (obj_id_match) { 592 var obj_id = parseInt(obj_id_match[1]); 593 // Build a dump request. 594 var request = this.createRequest('getobj'); 595 request.arguments = {}; 596 request.arguments.obj_id = obj_id; 597 return request.toJSONProtocol(); 598 } 599 } 600 601 // Check if the expression is a handle id in the form #<handle>#. 602 var handle_match = expression.match(/^#([0-9]*)#$/); 603 if (handle_match) { 604 // Remember the handle requested in a global variable. 605 lookup_handle = parseInt(handle_match[1]); 606 // Build a lookup request. 607 var request = this.createRequest('lookup'); 608 request.arguments = {}; 609 request.arguments.handles = [ lookup_handle ]; 610 return request.toJSONProtocol(); 611 } else { 612 // Build an evaluate request. 613 var request = this.createRequest('evaluate'); 614 request.arguments = {}; 615 request.arguments.expression = expression; 616 // Request a global evaluation if there is no current frame. 617 if (Debug.State.currentFrame == kNoFrame) { 618 request.arguments.global = true; 619 } 620 return request.toJSONProtocol(); 621 } 622 }; 623 624 625 // Create a JSON request for the references/instances command. 626 DebugRequest.prototype.makeReferencesJSONRequest_ = function(handle, type) { 627 // Build a references request. 628 var handle_match = handle.match(/^#([0-9]*)#$/); 629 if (handle_match) { 630 var request = this.createRequest('references'); 631 request.arguments = {}; 632 request.arguments.type = type; 633 request.arguments.handle = parseInt(handle_match[1]); 634 return request.toJSONProtocol(); 635 } else { 636 throw new Error('Invalid object id.'); 637 } 638 }; 639 640 641 // Create a JSON request for the continue command. 642 DebugRequest.prototype.continueCommandToJSONRequest_ = function(args) { 643 var request = this.createRequest('continue'); 644 return request.toJSONProtocol(); 645 }; 646 647 648 // Create a JSON request for the step command. 649 DebugRequest.prototype.stepCommandToJSONRequest_ = function(args, type) { 650 // Requesting a step is through the continue command with additional 651 // arguments. 652 var request = this.createRequest('continue'); 653 request.arguments = {}; 654 655 // Process arguments if any. 656 657 // Only process args if the command is 'step' which is indicated by type being 658 // set to 'in'. For all other commands, ignore the args. 659 if (args && args.length > 0) { 660 args = args.split(/\s+/g); 661 662 if (args.length > 2) { 663 throw new Error('Invalid step arguments.'); 664 } 665 666 if (args.length > 0) { 667 // Check if we have a gdb stype step command. If so, the 1st arg would 668 // be the step count. If it's not a number, then assume that we're 669 // parsing for the legacy v8 step command. 670 var stepcount = Number(args[0]); 671 if (stepcount == Number.NaN) { 672 // No step count at arg 1. Process as legacy d8 step command: 673 if (args.length == 2) { 674 var stepcount = parseInt(args[1]); 675 if (isNaN(stepcount) || stepcount <= 0) { 676 throw new Error('Invalid step count argument "' + args[0] + '".'); 677 } 678 request.arguments.stepcount = stepcount; 679 } 680 681 // Get the step action. 682 switch (args[0]) { 683 case 'in': 684 case 'i': 685 request.arguments.stepaction = 'in'; 686 break; 687 688 case 'min': 689 case 'm': 690 request.arguments.stepaction = 'min'; 691 break; 692 693 case 'next': 694 case 'n': 695 request.arguments.stepaction = 'next'; 696 break; 697 698 case 'out': 699 case 'o': 700 request.arguments.stepaction = 'out'; 701 break; 702 703 default: 704 throw new Error('Invalid step argument "' + args[0] + '".'); 705 } 706 707 } else { 708 // gdb style step commands: 709 request.arguments.stepaction = type; 710 request.arguments.stepcount = stepcount; 711 } 712 } 713 } else { 714 // Default is step of the specified type. 715 request.arguments.stepaction = type; 716 } 717 718 return request.toJSONProtocol(); 719 }; 720 721 722 // Create a JSON request for the backtrace command. 723 DebugRequest.prototype.backtraceCommandToJSONRequest_ = function(args) { 724 // Build a backtrace request from the text command. 725 var request = this.createRequest('backtrace'); 726 727 // Default is to show top 10 frames. 728 request.arguments = {}; 729 request.arguments.fromFrame = 0; 730 request.arguments.toFrame = 10; 731 732 args = args.split(/\s*[ ]+\s*/g); 733 if (args.length == 1 && args[0].length > 0) { 734 var frameCount = parseInt(args[0]); 735 if (frameCount > 0) { 736 // Show top frames. 737 request.arguments.fromFrame = 0; 738 request.arguments.toFrame = frameCount; 739 } else { 740 // Show bottom frames. 741 request.arguments.fromFrame = 0; 742 request.arguments.toFrame = -frameCount; 743 request.arguments.bottom = true; 744 } 745 } else if (args.length == 2) { 746 var fromFrame = parseInt(args[0]); 747 var toFrame = parseInt(args[1]); 748 if (isNaN(fromFrame) || fromFrame < 0) { 749 throw new Error('Invalid start frame argument "' + args[0] + '".'); 750 } 751 if (isNaN(toFrame) || toFrame < 0) { 752 throw new Error('Invalid end frame argument "' + args[1] + '".'); 753 } 754 if (fromFrame > toFrame) { 755 throw new Error('Invalid arguments start frame cannot be larger ' + 756 'than end frame.'); 757 } 758 // Show frame range. 759 request.arguments.fromFrame = fromFrame; 760 request.arguments.toFrame = toFrame + 1; 761 } else if (args.length > 2) { 762 throw new Error('Invalid backtrace arguments.'); 763 } 764 765 return request.toJSONProtocol(); 766 }; 767 768 769 // Create a JSON request for the frame command. 770 DebugRequest.prototype.frameCommandToJSONRequest_ = function(args) { 771 // Build a frame request from the text command. 772 var request = this.createRequest('frame'); 773 args = args.split(/\s*[ ]+\s*/g); 774 if (args.length > 0 && args[0].length > 0) { 775 request.arguments = {}; 776 request.arguments.number = args[0]; 777 } 778 return request.toJSONProtocol(); 779 }; 780 781 782 // Create a JSON request for the scopes command. 783 DebugRequest.prototype.scopesCommandToJSONRequest_ = function(args) { 784 // Build a scopes request from the text command. 785 var request = this.createRequest('scopes'); 786 return request.toJSONProtocol(); 787 }; 788 789 790 // Create a JSON request for the scope command. 791 DebugRequest.prototype.scopeCommandToJSONRequest_ = function(args) { 792 // Build a scope request from the text command. 793 var request = this.createRequest('scope'); 794 args = args.split(/\s*[ ]+\s*/g); 795 if (args.length > 0 && args[0].length > 0) { 796 request.arguments = {}; 797 request.arguments.number = args[0]; 798 } 799 return request.toJSONProtocol(); 800 }; 801 802 803 // Create a JSON request for the print command. 804 DebugRequest.prototype.printCommandToJSONRequest_ = function(args) { 805 // Build an evaluate request from the text command. 806 if (args.length == 0) { 807 throw new Error('Missing expression.'); 808 } 809 return this.makeEvaluateJSONRequest_(args); 810 }; 811 812 813 // Create a JSON request for the dir command. 814 DebugRequest.prototype.dirCommandToJSONRequest_ = function(args) { 815 // Build an evaluate request from the text command. 816 if (args.length == 0) { 817 throw new Error('Missing expression.'); 818 } 819 return this.makeEvaluateJSONRequest_(args); 820 }; 821 822 823 // Create a JSON request for the references command. 824 DebugRequest.prototype.referencesCommandToJSONRequest_ = function(args) { 825 // Build an evaluate request from the text command. 826 if (args.length == 0) { 827 throw new Error('Missing object id.'); 828 } 829 830 return this.makeReferencesJSONRequest_(args, 'referencedBy'); 831 }; 832 833 834 // Create a JSON request for the instances command. 835 DebugRequest.prototype.instancesCommandToJSONRequest_ = function(args) { 836 // Build an evaluate request from the text command. 837 if (args.length == 0) { 838 throw new Error('Missing object id.'); 839 } 840 841 // Build a references request. 842 return this.makeReferencesJSONRequest_(args, 'constructedBy'); 843 }; 844 845 846 // Create a JSON request for the list command. 847 DebugRequest.prototype.listCommandToJSONRequest_ = function(args) { 848 849 // Default is ten lines starting five lines before the current location. 850 if (Debug.State.displaySourceEndLine == -1) { 851 // If we list forwards, we will start listing after the last source end 852 // line. Set it to start from 5 lines before the current location. 853 Debug.State.displaySourceEndLine = Debug.State.currentSourceLine - 5; 854 // If we list backwards, we will start listing backwards from the last 855 // source start line. Set it to start from 1 lines before the current 856 // location. 857 Debug.State.displaySourceStartLine = Debug.State.currentSourceLine + 1; 858 } 859 860 var from = Debug.State.displaySourceEndLine + 1; 861 var lines = 10; 862 863 // Parse the arguments. 864 args = args.split(/\s*,\s*/g); 865 if (args == '') { 866 } else if ((args.length == 1) && (args[0] == '-')) { 867 from = Debug.State.displaySourceStartLine - lines; 868 } else if (args.length == 2) { 869 from = parseInt(args[0]); 870 lines = parseInt(args[1]) - from + 1; // inclusive of the ending line. 871 } else { 872 throw new Error('Invalid list arguments.'); 873 } 874 Debug.State.displaySourceStartLine = from; 875 Debug.State.displaySourceEndLine = from + lines - 1; 876 var sourceArgs = '' + from + ' ' + lines; 877 return this.sourceCommandToJSONRequest_(sourceArgs); 878 }; 879 880 881 // Create a JSON request for the source command. 882 DebugRequest.prototype.sourceCommandToJSONRequest_ = function(args) { 883 // Build a evaluate request from the text command. 884 var request = this.createRequest('source'); 885 886 // Default is ten lines starting five lines before the current location. 887 var from = Debug.State.currentSourceLine - 5; 888 var lines = 10; 889 890 // Parse the arguments. 891 args = args.split(/\s*[ ]+\s*/g); 892 if (args.length > 1 && args[0].length > 0 && args[1].length > 0) { 893 from = parseInt(args[0]) - 1; 894 lines = parseInt(args[1]); 895 } else if (args.length > 0 && args[0].length > 0) { 896 from = parseInt(args[0]) - 1; 897 } 898 899 if (from < 0) from = 0; 900 if (lines < 0) lines = 10; 901 902 // Request source arround current source location. 903 request.arguments = {}; 904 request.arguments.fromLine = from; 905 request.arguments.toLine = from + lines; 906 907 return request.toJSONProtocol(); 908 }; 909 910 911 // Create a JSON request for the scripts command. 912 DebugRequest.prototype.scriptsCommandToJSONRequest_ = function(args) { 913 // Build a evaluate request from the text command. 914 var request = this.createRequest('scripts'); 915 916 // Process arguments if any. 917 if (args && args.length > 0) { 918 args = args.split(/\s*[ ]+\s*/g); 919 920 if (args.length > 1) { 921 throw new Error('Invalid scripts arguments.'); 922 } 923 924 request.arguments = {}; 925 switch (args[0]) { 926 case 'natives': 927 request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Native); 928 break; 929 930 case 'extensions': 931 request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Extension); 932 break; 933 934 case 'all': 935 request.arguments.types = 936 ScriptTypeFlag(Debug.ScriptType.Normal) | 937 ScriptTypeFlag(Debug.ScriptType.Native) | 938 ScriptTypeFlag(Debug.ScriptType.Extension); 939 break; 940 941 default: 942 // If the arg is not one of the know one aboves, then it must be a 943 // filter used for filtering the results: 944 request.arguments.filter = args[0]; 945 break; 946 } 947 } 948 949 return request.toJSONProtocol(); 950 }; 951 952 953 // Create a JSON request for the break command. 954 DebugRequest.prototype.breakCommandToJSONRequest_ = function(args) { 955 // Build a evaluate request from the text command. 956 // Process arguments if any. 957 if (args && args.length > 0) { 958 var target = args; 959 var type = 'function'; 960 var line; 961 var column; 962 var condition; 963 var pos; 964 965 var request = this.createRequest('setbreakpoint'); 966 967 // Break the args into target spec and condition if appropriate. 968 969 // Check for breakpoint condition. 970 pos = args.indexOf(' '); 971 if (pos > 0) { 972 target = args.substring(0, pos); 973 condition = args.substring(pos + 1, args.length); 974 } 975 976 // Check for script breakpoint (name:line[:column]). If no ':' in break 977 // specification it is considered a function break point. 978 pos = target.indexOf(':'); 979 if (pos > 0) { 980 type = 'script'; 981 var tmp = target.substring(pos + 1, target.length); 982 target = target.substring(0, pos); 983 984 // Check for both line and column. 985 pos = tmp.indexOf(':'); 986 if (pos > 0) { 987 column = parseInt(tmp.substring(pos + 1, tmp.length)) - 1; 988 line = parseInt(tmp.substring(0, pos)) - 1; 989 } else { 990 line = parseInt(tmp) - 1; 991 } 992 } else if (target[0] == '#' && target[target.length - 1] == '#') { 993 type = 'handle'; 994 target = target.substring(1, target.length - 1); 995 } else { 996 type = 'function'; 997 } 998 999 request.arguments = {}; 1000 request.arguments.type = type; 1001 request.arguments.target = target; 1002 request.arguments.line = line; 1003 request.arguments.column = column; 1004 request.arguments.condition = condition; 1005 } else { 1006 var request = this.createRequest('suspend'); 1007 } 1008 1009 return request.toJSONProtocol(); 1010 }; 1011 1012 1013 DebugRequest.prototype.breakpointsCommandToJSONRequest_ = function(args) { 1014 if (args && args.length > 0) { 1015 throw new Error('Unexpected arguments.'); 1016 } 1017 var request = this.createRequest('listbreakpoints'); 1018 return request.toJSONProtocol(); 1019 }; 1020 1021 1022 // Create a JSON request for the clear command. 1023 DebugRequest.prototype.clearCommandToJSONRequest_ = function(args) { 1024 // Build a evaluate request from the text command. 1025 var request = this.createRequest('clearbreakpoint'); 1026 1027 // Process arguments if any. 1028 if (args && args.length > 0) { 1029 request.arguments = {}; 1030 request.arguments.breakpoint = parseInt(args); 1031 } else { 1032 throw new Error('Invalid break arguments.'); 1033 } 1034 1035 return request.toJSONProtocol(); 1036 }; 1037 1038 1039 // Create a JSON request for the change breakpoint command. 1040 DebugRequest.prototype.changeBreakpointCommandToJSONRequest_ = 1041 function(args, command) { 1042 1043 var request; 1044 1045 // Check for exception breaks first: 1046 // en[able] exc[eptions] [all|unc[aught]] 1047 // en[able] [all|unc[aught]] exc[eptions] 1048 // dis[able] exc[eptions] [all|unc[aught]] 1049 // dis[able] [all|unc[aught]] exc[eptions] 1050 if ((command == 'enable' || command == 'disable') && 1051 args && args.length > 1) { 1052 var nextPos = args.indexOf(' '); 1053 var arg1 = (nextPos > 0) ? args.substring(0, nextPos) : args; 1054 var excType = null; 1055 1056 // Check for: 1057 // en[able] exc[eptions] [all|unc[aught]] 1058 // dis[able] exc[eptions] [all|unc[aught]] 1059 if (arg1 == 'exc' || arg1 == 'exception' || arg1 == 'exceptions') { 1060 1061 var arg2 = (nextPos > 0) ? 1062 args.substring(nextPos + 1, args.length) : 'all'; 1063 if (!arg2) { 1064 arg2 = 'all'; // if unspecified, set for all. 1065 } if (arg2 == 'unc') { // check for short cut. 1066 arg2 = 'uncaught'; 1067 } 1068 excType = arg2; 1069 1070 // Check for: 1071 // en[able] [all|unc[aught]] exc[eptions] 1072 // dis[able] [all|unc[aught]] exc[eptions] 1073 } else if (arg1 == 'all' || arg1 == 'unc' || arg1 == 'uncaught') { 1074 1075 var arg2 = (nextPos > 0) ? 1076 args.substring(nextPos + 1, args.length) : null; 1077 if (arg2 == 'exc' || arg1 == 'exception' || arg1 == 'exceptions') { 1078 excType = arg1; 1079 if (excType == 'unc') { 1080 excType = 'uncaught'; 1081 } 1082 } 1083 } 1084 1085 // If we matched one of the command formats, then excType will be non-null: 1086 if (excType) { 1087 // Build a evaluate request from the text command. 1088 request = this.createRequest('setexceptionbreak'); 1089 1090 request.arguments = {}; 1091 request.arguments.type = excType; 1092 request.arguments.enabled = (command == 'enable'); 1093 1094 return request.toJSONProtocol(); 1095 } 1096 } 1097 1098 // Build a evaluate request from the text command. 1099 request = this.createRequest('changebreakpoint'); 1100 1101 // Process arguments if any. 1102 if (args && args.length > 0) { 1103 request.arguments = {}; 1104 var pos = args.indexOf(' '); 1105 var breakpointArg = args; 1106 var otherArgs; 1107 if (pos > 0) { 1108 breakpointArg = args.substring(0, pos); 1109 otherArgs = args.substring(pos + 1, args.length); 1110 } 1111 1112 request.arguments.breakpoint = parseInt(breakpointArg); 1113 1114 switch(command) { 1115 case 'cond': 1116 request.arguments.condition = otherArgs ? otherArgs : null; 1117 break; 1118 case 'enable': 1119 request.arguments.enabled = true; 1120 break; 1121 case 'disable': 1122 request.arguments.enabled = false; 1123 break; 1124 case 'ignore': 1125 request.arguments.ignoreCount = parseInt(otherArgs); 1126 break; 1127 default: 1128 throw new Error('Invalid arguments.'); 1129 } 1130 } else { 1131 throw new Error('Invalid arguments.'); 1132 } 1133 1134 return request.toJSONProtocol(); 1135 }; 1136 1137 1138 // Create a JSON request for the disconnect command. 1139 DebugRequest.prototype.disconnectCommandToJSONRequest_ = function(args) { 1140 var request; 1141 request = this.createRequest('disconnect'); 1142 return request.toJSONProtocol(); 1143 }; 1144 1145 1146 // Create a JSON request for the info command. 1147 DebugRequest.prototype.infoCommandToJSONRequest_ = function(args) { 1148 var request; 1149 if (args && (args == 'break' || args == 'br')) { 1150 // Build a evaluate request from the text command. 1151 request = this.createRequest('listbreakpoints'); 1152 last_cmd = 'info break'; 1153 } else if (args && (args == 'locals' || args == 'lo')) { 1154 // Build a evaluate request from the text command. 1155 request = this.createRequest('frame'); 1156 last_cmd = 'info locals'; 1157 } else if (args && (args == 'args' || args == 'ar')) { 1158 // Build a evaluate request from the text command. 1159 request = this.createRequest('frame'); 1160 last_cmd = 'info args'; 1161 } else if (lol_is_enabled && 1162 args && (args == 'liveobjectlist' || args == 'lol')) { 1163 // Build a evaluate request from the text command. 1164 return this.liveObjectListToJSONRequest_(null); 1165 } else { 1166 throw new Error('Invalid info arguments.'); 1167 } 1168 1169 return request.toJSONProtocol(); 1170 }; 1171 1172 1173 DebugRequest.prototype.v8FlagsToJSONRequest_ = function(args) { 1174 var request; 1175 request = this.createRequest('v8flags'); 1176 request.arguments = {}; 1177 request.arguments.flags = args; 1178 return request.toJSONProtocol(); 1179 }; 1180 1181 1182 DebugRequest.prototype.gcToJSONRequest_ = function(args) { 1183 var request; 1184 if (!args) { 1185 args = 'all'; 1186 } 1187 var args = args.split(/\s+/g); 1188 var cmd = args[0]; 1189 1190 switch(cmd) { 1191 case 'all': 1192 case 'quick': 1193 case 'full': 1194 case 'young': 1195 case 'old': 1196 case 'compact': 1197 case 'sweep': 1198 case 'scavenge': { 1199 if (cmd == 'young') { cmd = 'quick'; } 1200 else if (cmd == 'old') { cmd = 'full'; } 1201 1202 request = this.createRequest('gc'); 1203 request.arguments = {}; 1204 request.arguments.type = cmd; 1205 break; 1206 } 1207 // Else fall thru to the default case below to report the error. 1208 default: 1209 throw new Error('Missing arguments after ' + cmd + '.'); 1210 } 1211 return request.toJSONProtocol(); 1212 }; 1213 1214 1215 // Args: [v[erbose]] [<N>] [i[ndex] <i>] [t[ype] <type>] [sp[ace] <space>] 1216 DebugRequest.prototype.lolMakeListRequest = 1217 function(cmd, args, first_arg_index, is_repeating) { 1218 1219 var request; 1220 var start_index = 0; 1221 var dump_limit = void 0; 1222 var type_filter = void 0; 1223 var space_filter = void 0; 1224 var prop_filter = void 0; 1225 var is_verbose = false; 1226 var i; 1227 1228 for (i = first_arg_index; i < args.length; i++) { 1229 var arg = args[i]; 1230 // Check for [v[erbose]]: 1231 if (arg === 'verbose' || arg === 'v') { 1232 // Nothing to do. This is already implied by args.length > 3. 1233 is_verbose = true; 1234 1235 // Check for [<N>]: 1236 } else if (arg.match(/^[0-9]+$/)) { 1237 dump_limit = arg; 1238 is_verbose = true; 1239 1240 // Check for i[ndex] <i>: 1241 } else if (arg === 'index' || arg === 'i') { 1242 i++; 1243 if (args.length < i) { 1244 throw new Error('Missing index after ' + arg + '.'); 1245 } 1246 start_index = parseInt(args[i]); 1247 // The user input start index starts at 1: 1248 if (start_index <= 0) { 1249 throw new Error('Invalid index ' + args[i] + '.'); 1250 } 1251 start_index -= 1; 1252 is_verbose = true; 1253 1254 // Check for t[ype] <type>: 1255 } else if (arg === 'type' || arg === 't') { 1256 i++; 1257 if (args.length < i) { 1258 throw new Error('Missing type after ' + arg + '.'); 1259 } 1260 type_filter = args[i]; 1261 1262 // Check for space <heap space name>: 1263 } else if (arg === 'space' || arg === 'sp') { 1264 i++; 1265 if (args.length < i) { 1266 throw new Error('Missing space name after ' + arg + '.'); 1267 } 1268 space_filter = args[i]; 1269 1270 // Check for property <prop name>: 1271 } else if (arg === 'property' || arg === 'prop') { 1272 i++; 1273 if (args.length < i) { 1274 throw new Error('Missing property name after ' + arg + '.'); 1275 } 1276 prop_filter = args[i]; 1277 1278 } else { 1279 throw new Error('Unknown args at ' + arg + '.'); 1280 } 1281 } 1282 1283 // Build the verbose request: 1284 if (is_verbose) { 1285 request = this.createLOLRequest('lol-'+cmd, 1286 start_index, 1287 dump_limit, 1288 is_repeating); 1289 request.arguments.verbose = true; 1290 } else { 1291 request = this.createRequest('lol-'+cmd); 1292 request.arguments = {}; 1293 } 1294 1295 request.arguments.filter = {}; 1296 if (type_filter) { 1297 request.arguments.filter.type = type_filter; 1298 } 1299 if (space_filter) { 1300 request.arguments.filter.space = space_filter; 1301 } 1302 if (prop_filter) { 1303 request.arguments.filter.prop = prop_filter; 1304 } 1305 1306 return request; 1307 } 1308 1309 1310 function extractObjId(args) { 1311 var id = args; 1312 id = id.match(/^@([0-9]+)$/); 1313 if (id) { 1314 id = id[1]; 1315 } else { 1316 throw new Error('Invalid obj id ' + args + '.'); 1317 } 1318 return parseInt(id); 1319 } 1320 1321 1322 DebugRequest.prototype.lolToJSONRequest_ = function(args, is_repeating) { 1323 var request; 1324 // Use default command if one is not specified: 1325 if (!args) { 1326 args = 'info'; 1327 } 1328 1329 var orig_args = args; 1330 var first_arg_index; 1331 1332 var arg, i; 1333 var args = args.split(/\s+/g); 1334 var cmd = args[0]; 1335 var id; 1336 1337 // Command: <id> [v[erbose]] ... 1338 if (cmd.match(/^[0-9]+$/)) { 1339 // Convert to the padded list command: 1340 // Command: l[ist] <dummy> <id> [v[erbose]] ... 1341 1342 // Insert the implicit 'list' in front and process as normal: 1343 cmd = 'list'; 1344 args.unshift(cmd); 1345 } 1346 1347 switch(cmd) { 1348 // Command: c[apture] 1349 case 'capture': 1350 case 'c': 1351 request = this.createRequest('lol-capture'); 1352 break; 1353 1354 // Command: clear|d[elete] <id>|all 1355 case 'clear': 1356 case 'delete': 1357 case 'del': { 1358 if (args.length < 2) { 1359 throw new Error('Missing argument after ' + cmd + '.'); 1360 } else if (args.length > 2) { 1361 throw new Error('Too many arguments after ' + cmd + '.'); 1362 } 1363 id = args[1]; 1364 if (id.match(/^[0-9]+$/)) { 1365 // Delete a specific lol record: 1366 request = this.createRequest('lol-delete'); 1367 request.arguments = {}; 1368 request.arguments.id = parseInt(id); 1369 } else if (id === 'all') { 1370 // Delete all: 1371 request = this.createRequest('lol-reset'); 1372 } else { 1373 throw new Error('Invalid argument after ' + cmd + '.'); 1374 } 1375 break; 1376 } 1377 1378 // Command: diff <id1> <id2> [<dump options>] 1379 case 'diff': 1380 first_arg_index = 3; 1381 1382 // Command: list <dummy> <id> [<dump options>] 1383 case 'list': 1384 1385 // Command: ret[ainers] <obj id> [<dump options>] 1386 case 'retainers': 1387 case 'ret': 1388 case 'retaining-paths': 1389 case 'rp': { 1390 if (cmd === 'ret') cmd = 'retainers'; 1391 else if (cmd === 'rp') cmd = 'retaining-paths'; 1392 1393 if (!first_arg_index) first_arg_index = 2; 1394 1395 if (args.length < first_arg_index) { 1396 throw new Error('Too few arguments after ' + cmd + '.'); 1397 } 1398 1399 var request_cmd = (cmd === 'list') ? 'diff':cmd; 1400 request = this.lolMakeListRequest(request_cmd, 1401 args, 1402 first_arg_index, 1403 is_repeating); 1404 1405 if (cmd === 'diff') { 1406 request.arguments.id1 = parseInt(args[1]); 1407 request.arguments.id2 = parseInt(args[2]); 1408 } else if (cmd == 'list') { 1409 request.arguments.id1 = 0; 1410 request.arguments.id2 = parseInt(args[1]); 1411 } else { 1412 request.arguments.id = extractObjId(args[1]); 1413 } 1414 break; 1415 } 1416 1417 // Command: getid 1418 case 'getid': { 1419 request = this.createRequest('lol-getid'); 1420 request.arguments = {}; 1421 request.arguments.address = args[1]; 1422 break; 1423 } 1424 1425 // Command: inf[o] [<N>] 1426 case 'info': 1427 case 'inf': { 1428 if (args.length > 2) { 1429 throw new Error('Too many arguments after ' + cmd + '.'); 1430 } 1431 // Built the info request: 1432 request = this.createLOLRequest('lol-info', 0, args[1], is_repeating); 1433 break; 1434 } 1435 1436 // Command: path <obj id 1> <obj id 2> 1437 case 'path': { 1438 request = this.createRequest('lol-path'); 1439 request.arguments = {}; 1440 if (args.length > 2) { 1441 request.arguments.id1 = extractObjId(args[1]); 1442 request.arguments.id2 = extractObjId(args[2]); 1443 } else { 1444 request.arguments.id1 = 0; 1445 request.arguments.id2 = extractObjId(args[1]); 1446 } 1447 break; 1448 } 1449 1450 // Command: print 1451 case 'print': { 1452 request = this.createRequest('lol-print'); 1453 request.arguments = {}; 1454 request.arguments.id = extractObjId(args[1]); 1455 break; 1456 } 1457 1458 // Command: reset 1459 case 'reset': { 1460 request = this.createRequest('lol-reset'); 1461 break; 1462 } 1463 1464 default: 1465 throw new Error('Invalid arguments.'); 1466 } 1467 return request.toJSONProtocol(); 1468 }; 1469 1470 1471 // Create a JSON request for the threads command. 1472 DebugRequest.prototype.threadsCommandToJSONRequest_ = function(args) { 1473 // Build a threads request from the text command. 1474 var request = this.createRequest('threads'); 1475 return request.toJSONProtocol(); 1476 }; 1477 1478 1479 // Handle the trace command. 1480 DebugRequest.prototype.traceCommand_ = function(args) { 1481 // Process arguments. 1482 if (args && args.length > 0) { 1483 if (args == 'compile') { 1484 trace_compile = !trace_compile; 1485 print('Tracing of compiled scripts ' + (trace_compile ? 'on' : 'off')); 1486 } else if (args === 'debug json' || args === 'json' || args === 'packets') { 1487 trace_debug_json = !trace_debug_json; 1488 print('Tracing of debug json packets ' + 1489 (trace_debug_json ? 'on' : 'off')); 1490 } else { 1491 throw new Error('Invalid trace arguments.'); 1492 } 1493 } else { 1494 throw new Error('Invalid trace arguments.'); 1495 } 1496 } 1497 1498 // Handle the help command. 1499 DebugRequest.prototype.helpCommand_ = function(args) { 1500 // Help os quite simple. 1501 if (args && args.length > 0) { 1502 print('warning: arguments to \'help\' are ignored'); 1503 } 1504 1505 print('Note: <> denotes symbollic values to be replaced with real values.'); 1506 print('Note: [] denotes optional parts of commands, or optional options / arguments.'); 1507 print(' e.g. d[elete] - you get the same command if you type d or delete.'); 1508 print(''); 1509 print('[break] - break as soon as possible'); 1510 print('b[reak] location [condition]'); 1511 print(' - break on named function: location is a function name'); 1512 print(' - break on function: location is #<id>#'); 1513 print(' - break on script position: location is name:line[:column]'); 1514 print(''); 1515 print('clear <breakpoint #> - deletes the specified user defined breakpoint'); 1516 print('d[elete] <breakpoint #> - deletes the specified user defined breakpoint'); 1517 print('dis[able] <breakpoint #> - disables the specified user defined breakpoint'); 1518 print('dis[able] exc[eptions] [[all] | unc[aught]]'); 1519 print(' - disables breaking on exceptions'); 1520 print('en[able] <breakpoint #> - enables the specified user defined breakpoint'); 1521 print('en[able] exc[eptions] [[all] | unc[aught]]'); 1522 print(' - enables breaking on exceptions'); 1523 print(''); 1524 print('b[ack]t[race] [n] | [-n] | [from to]'); 1525 print(' - prints the stack back trace'); 1526 print('f[rame] - prints info about the current frame context'); 1527 print('f[rame] <frame #> - set context to specified frame #'); 1528 print('scopes'); 1529 print('scope <scope #>'); 1530 print(''); 1531 print('up - set context to caller of current frame'); 1532 print('do[wn] - set context to callee of current frame'); 1533 print('inf[o] br[eak] - prints info about breakpoints in use'); 1534 print('inf[o] ar[gs] - prints info about arguments of the current function'); 1535 print('inf[o] lo[cals] - prints info about locals in the current function'); 1536 print('inf[o] liveobjectlist|lol - same as \'lol info\''); 1537 print(''); 1538 print('step [in | next | out| min [step count]]'); 1539 print('c[ontinue] - continue executing after a breakpoint'); 1540 print('s[tep] [<N>] - step into the next N callees (default N is 1)'); 1541 print('s[tep]i [<N>] - step into the next N callees (default N is 1)'); 1542 print('n[ext] [<N>] - step over the next N callees (default N is 1)'); 1543 print('fin[ish] [<N>] - step out of N frames (default N is 1)'); 1544 print(''); 1545 print('p[rint] <expression> - prints the result of the specified expression'); 1546 print('dir <expression> - prints the object structure of the result'); 1547 print('set <var> = <expression> - executes the specified statement'); 1548 print(''); 1549 print('l[ist] - list the source code around for the current pc'); 1550 print('l[ist] [- | <start>,<end>] - list the specified range of source code'); 1551 print('source [from line [num lines]]'); 1552 print('scr[ipts] [native|extensions|all]'); 1553 print('scr[ipts] [<filter text>] - list scripts with the specified text in its description'); 1554 print(''); 1555 print('gc - runs the garbage collector'); 1556 print(''); 1557 1558 if (lol_is_enabled) { 1559 print('liveobjectlist|lol <command> - live object list tracking.'); 1560 print(' where <command> can be:'); 1561 print(' c[apture] - captures a LOL list.'); 1562 print(' clear|del[ete] <id>|all - clears LOL of id <id>.'); 1563 print(' If \'all\' is unspecified instead, will clear all.'); 1564 print(' diff <id1> <id2> [<dump options>]'); 1565 print(' - prints the diff between LOLs id1 and id2.'); 1566 print(' - also see <dump options> below.'); 1567 print(' getid <address> - gets the obj id for the specified address if available.'); 1568 print(' The address must be in hex form prefixed with 0x.'); 1569 print(' inf[o] [<N>] - lists summary info of all LOL lists.'); 1570 print(' If N is specified, will print N items at a time.'); 1571 print(' [l[ist]] <id> [<dump options>]'); 1572 print(' - prints the listing of objects in LOL id.'); 1573 print(' - also see <dump options> below.'); 1574 print(' reset - clears all LOL lists.'); 1575 print(' ret[ainers] <id> [<dump options>]'); 1576 print(' - prints the list of retainers of obj id.'); 1577 print(' - also see <dump options> below.'); 1578 print(' path <id1> <id2> - prints the retaining path from obj id1 to id2.'); 1579 print(' If only one id is specified, will print the path from'); 1580 print(' roots to the specified object if available.'); 1581 print(' print <id> - prints the obj for the specified obj id if available.'); 1582 print(''); 1583 print(' <dump options> includes:'); 1584 print(' [v[erbose]] - do verbose dump.'); 1585 print(' [<N>] - dump N items at a time. Implies verbose dump.'); 1586 print(' If unspecified, N will default to '+ 1587 kDefaultLolLinesToPrintAtATime+'. Max N is '+ 1588 kMaxLolLinesToPrintAtATime+'.'); 1589 print(' [i[ndex] <i>] - start dump from index i. Implies verbose dump.'); 1590 print(' [t[ype] <type>] - filter by type.'); 1591 print(' [sp[ace] <space name>] - filter by heap space where <space name> is one of'); 1592 print(' { cell, code, lo, map, new, old-data, old-pointer }.'); 1593 print(''); 1594 print(' If the verbose option, or an option that implies a verbose dump'); 1595 print(' is specified, then a verbose dump will requested. Else, a summary dump'); 1596 print(' will be requested.'); 1597 print(''); 1598 } 1599 1600 print('trace compile'); 1601 // hidden command: trace debug json - toggles tracing of debug json packets 1602 print(''); 1603 print('disconnect|exit|quit - disconnects and quits the debugger'); 1604 print('help - prints this help information'); 1605 } 1606 1607 1608 function formatHandleReference_(value) { 1609 if (value.handle() >= 0) { 1610 return '#' + value.handle() + '#'; 1611 } else { 1612 return '#Transient#'; 1613 } 1614 } 1615 1616 1617 function formatObject_(value, include_properties) { 1618 var result = ''; 1619 result += formatHandleReference_(value); 1620 result += ', type: object' 1621 result += ', constructor '; 1622 var ctor = value.constructorFunctionValue(); 1623 result += formatHandleReference_(ctor); 1624 result += ', __proto__ '; 1625 var proto = value.protoObjectValue(); 1626 result += formatHandleReference_(proto); 1627 result += ', '; 1628 result += value.propertyCount(); 1629 result += ' properties.'; 1630 if (include_properties) { 1631 result += '\n'; 1632 for (var i = 0; i < value.propertyCount(); i++) { 1633 result += ' '; 1634 result += value.propertyName(i); 1635 result += ': '; 1636 var property_value = value.propertyValue(i); 1637 if (property_value instanceof ProtocolReference) { 1638 result += '<no type>'; 1639 } else { 1640 if (property_value && property_value.type()) { 1641 result += property_value.type(); 1642 } else { 1643 result += '<no type>'; 1644 } 1645 } 1646 result += ' '; 1647 result += formatHandleReference_(property_value); 1648 result += '\n'; 1649 } 1650 } 1651 return result; 1652 } 1653 1654 1655 function formatScope_(scope) { 1656 var result = ''; 1657 var index = scope.index; 1658 result += '#' + (index <= 9 ? '0' : '') + index; 1659 result += ' '; 1660 switch (scope.type) { 1661 case Debug.ScopeType.Global: 1662 result += 'Global, '; 1663 result += '#' + scope.object.ref + '#'; 1664 break; 1665 case Debug.ScopeType.Local: 1666 result += 'Local'; 1667 break; 1668 case Debug.ScopeType.With: 1669 result += 'With, '; 1670 result += '#' + scope.object.ref + '#'; 1671 break; 1672 case Debug.ScopeType.Catch: 1673 result += 'Catch, '; 1674 result += '#' + scope.object.ref + '#'; 1675 break; 1676 case Debug.ScopeType.Closure: 1677 result += 'Closure'; 1678 break; 1679 default: 1680 result += 'UNKNOWN'; 1681 } 1682 return result; 1683 } 1684 1685 1686 function refObjectToString_(protocolPackage, handle) { 1687 var value = protocolPackage.lookup(handle); 1688 var result = ''; 1689 if (value.isString()) { 1690 result = '"' + value.value() + '"'; 1691 } else if (value.isPrimitive()) { 1692 result = value.valueString(); 1693 } else if (value.isObject()) { 1694 result += formatObject_(value, true); 1695 } 1696 return result; 1697 } 1698 1699 1700 function decodeLolCaptureResponse(body) { 1701 var result; 1702 result = 'Captured live object list '+ body.id + 1703 ': count '+ body.count + ' size ' + body.size; 1704 return result; 1705 } 1706 1707 1708 function decodeLolDeleteResponse(body) { 1709 var result; 1710 result = 'Deleted live object list '+ body.id; 1711 return result; 1712 } 1713 1714 1715 function digitsIn(value) { 1716 var digits = 0; 1717 if (value === 0) value = 1; 1718 while (value >= 1) { 1719 digits++; 1720 value /= 10; 1721 } 1722 return digits; 1723 } 1724 1725 1726 function padding(value, max_digits) { 1727 var padding_digits = max_digits - digitsIn(value); 1728 var padding = ''; 1729 while (padding_digits > 0) { 1730 padding += ' '; 1731 padding_digits--; 1732 } 1733 return padding; 1734 } 1735 1736 1737 function decodeLolInfoResponse(body) { 1738 var result; 1739 var lists = body.lists; 1740 var length = lists.length; 1741 var first_index = body.first_index + 1; 1742 var has_more = ((first_index + length) <= body.count); 1743 result = 'captured live object lists'; 1744 if (has_more || (first_index != 1)) { 1745 result += ' ['+ length +' of '+ body.count + 1746 ': starting from '+ first_index +']'; 1747 } 1748 result += ':\n'; 1749 var max_digits = digitsIn(body.count); 1750 var last_count = 0; 1751 var last_size = 0; 1752 for (var i = 0; i < length; i++) { 1753 var entry = lists[i]; 1754 var count = entry.count; 1755 var size = entry.size; 1756 var index = first_index + i; 1757 result += ' [' + padding(index, max_digits) + index + '] id '+ entry.id + 1758 ': count '+ count; 1759 if (last_count > 0) { 1760 result += '(+' + (count - last_count) + ')'; 1761 } 1762 result += ' size '+ size; 1763 if (last_size > 0) { 1764 result += '(+' + (size - last_size) + ')'; 1765 } 1766 result += '\n'; 1767 last_count = count; 1768 last_size = size; 1769 } 1770 result += ' total: '+length+' lists\n'; 1771 if (has_more) { 1772 result += ' -- press <enter> for more --\n'; 1773 } else { 1774 repeat_cmd_line = ''; 1775 } 1776 if (length === 0) result += ' none\n'; 1777 1778 return result; 1779 } 1780 1781 1782 function decodeLolListResponse(body, title) { 1783 1784 var result; 1785 var total_count = body.count; 1786 var total_size = body.size; 1787 var length; 1788 var max_digits; 1789 var i; 1790 var entry; 1791 var index; 1792 1793 var max_count_digits = digitsIn(total_count); 1794 var max_size_digits; 1795 1796 var summary = body.summary; 1797 if (summary) { 1798 1799 var roots_count = 0; 1800 var found_root = body.found_root || 0; 1801 var found_weak_root = body.found_weak_root || 0; 1802 1803 // Print the summary result: 1804 result = 'summary of objects:\n'; 1805 length = summary.length; 1806 if (found_root !== 0) { 1807 roots_count++; 1808 } 1809 if (found_weak_root !== 0) { 1810 roots_count++; 1811 } 1812 max_digits = digitsIn(length + roots_count); 1813 max_size_digits = digitsIn(total_size); 1814 1815 index = 1; 1816 if (found_root !== 0) { 1817 result += ' [' + padding(index, max_digits) + index + '] ' + 1818 ' count '+ 1 + padding(0, max_count_digits) + 1819 ' '+ padding(0, max_size_digits+1) + 1820 ' : <root>\n'; 1821 index++; 1822 } 1823 if (found_weak_root !== 0) { 1824 result += ' [' + padding(index, max_digits) + index + '] ' + 1825 ' count '+ 1 + padding(0, max_count_digits) + 1826 ' '+ padding(0, max_size_digits+1) + 1827 ' : <weak root>\n'; 1828 index++; 1829 } 1830 1831 for (i = 0; i < length; i++) { 1832 entry = summary[i]; 1833 var count = entry.count; 1834 var size = entry.size; 1835 result += ' [' + padding(index, max_digits) + index + '] ' + 1836 ' count '+ count + padding(count, max_count_digits) + 1837 ' size '+ size + padding(size, max_size_digits) + 1838 ' : <' + entry.desc + '>\n'; 1839 index++; 1840 } 1841 result += '\n total count: '+(total_count+roots_count)+'\n'; 1842 if (body.size) { 1843 result += ' total size: '+body.size+'\n'; 1844 } 1845 1846 } else { 1847 // Print the full dump result: 1848 var first_index = body.first_index + 1; 1849 var elements = body.elements; 1850 length = elements.length; 1851 var has_more = ((first_index + length) <= total_count); 1852 result = title; 1853 if (has_more || (first_index != 1)) { 1854 result += ' ['+ length +' of '+ total_count + 1855 ': starting from '+ first_index +']'; 1856 } 1857 result += ':\n'; 1858 if (length === 0) result += ' none\n'; 1859 max_digits = digitsIn(length); 1860 1861 var max_id = 0; 1862 var max_size = 0; 1863 for (i = 0; i < length; i++) { 1864 entry = elements[i]; 1865 if (entry.id > max_id) max_id = entry.id; 1866 if (entry.size > max_size) max_size = entry.size; 1867 } 1868 var max_id_digits = digitsIn(max_id); 1869 max_size_digits = digitsIn(max_size); 1870 1871 for (i = 0; i < length; i++) { 1872 entry = elements[i]; 1873 index = first_index + i; 1874 result += ' ['+ padding(index, max_digits) + index +']'; 1875 if (entry.id !== 0) { 1876 result += ' @' + entry.id + padding(entry.id, max_id_digits) + 1877 ': size ' + entry.size + ', ' + 1878 padding(entry.size, max_size_digits) + entry.desc + '\n'; 1879 } else { 1880 // Must be a root or weak root: 1881 result += ' ' + entry.desc + '\n'; 1882 } 1883 } 1884 if (has_more) { 1885 result += ' -- press <enter> for more --\n'; 1886 } else { 1887 repeat_cmd_line = ''; 1888 } 1889 if (length === 0) result += ' none\n'; 1890 } 1891 1892 return result; 1893 } 1894 1895 1896 function decodeLolDiffResponse(body) { 1897 var title = 'objects'; 1898 return decodeLolListResponse(body, title); 1899 } 1900 1901 1902 function decodeLolRetainersResponse(body) { 1903 var title = 'retainers for @' + body.id; 1904 return decodeLolListResponse(body, title); 1905 } 1906 1907 1908 function decodeLolPathResponse(body) { 1909 return body.path; 1910 } 1911 1912 1913 function decodeLolResetResponse(body) { 1914 return 'Reset all live object lists.'; 1915 } 1916 1917 1918 function decodeLolGetIdResponse(body) { 1919 if (body.id == 0) { 1920 return 'Address is invalid, or object has been moved or collected'; 1921 } 1922 return 'obj id is @' + body.id; 1923 } 1924 1925 1926 function decodeLolPrintResponse(body) { 1927 return body.dump; 1928 } 1929 1930 1931 // Rounds number 'num' to 'length' decimal places. 1932 function roundNumber(num, length) { 1933 var factor = Math.pow(10, length); 1934 return Math.round(num * factor) / factor; 1935 } 1936 1937 1938 // Convert a JSON response to text for display in a text based debugger. 1939 function DebugResponseDetails(response) { 1940 details = {text:'', running:false} 1941 1942 try { 1943 if (!response.success()) { 1944 details.text = response.message(); 1945 return details; 1946 } 1947 1948 // Get the running state. 1949 details.running = response.running(); 1950 1951 var body = response.body(); 1952 var result = ''; 1953 switch (response.command()) { 1954 case 'suspend': 1955 details.text = 'stopped'; 1956 break; 1957 1958 case 'setbreakpoint': 1959 result = 'set breakpoint #'; 1960 result += body.breakpoint; 1961 details.text = result; 1962 break; 1963 1964 case 'clearbreakpoint': 1965 result = 'cleared breakpoint #'; 1966 result += body.breakpoint; 1967 details.text = result; 1968 break; 1969 1970 case 'changebreakpoint': 1971 result = 'successfully changed breakpoint'; 1972 details.text = result; 1973 break; 1974 1975 case 'listbreakpoints': 1976 result = 'breakpoints: (' + body.breakpoints.length + ')'; 1977 for (var i = 0; i < body.breakpoints.length; i++) { 1978 var breakpoint = body.breakpoints[i]; 1979 result += '\n id=' + breakpoint.number; 1980 result += ' type=' + breakpoint.type; 1981 if (breakpoint.script_id) { 1982 result += ' script_id=' + breakpoint.script_id; 1983 } 1984 if (breakpoint.script_name) { 1985 result += ' script_name=' + breakpoint.script_name; 1986 } 1987 result += ' line=' + (breakpoint.line + 1); 1988 if (breakpoint.column != null) { 1989 result += ' column=' + (breakpoint.column + 1); 1990 } 1991 if (breakpoint.groupId) { 1992 result += ' groupId=' + breakpoint.groupId; 1993 } 1994 if (breakpoint.ignoreCount) { 1995 result += ' ignoreCount=' + breakpoint.ignoreCount; 1996 } 1997 if (breakpoint.active === false) { 1998 result += ' inactive'; 1999 } 2000 if (breakpoint.condition) { 2001 result += ' condition=' + breakpoint.condition; 2002 } 2003 result += ' hit_count=' + breakpoint.hit_count; 2004 } 2005 if (body.breakpoints.length === 0) { 2006 result = "No user defined breakpoints\n"; 2007 } else { 2008 result += '\n'; 2009 } 2010 if (body.breakOnExceptions) { 2011 result += '* breaking on ALL exceptions is enabled\n'; 2012 } else if (body.breakOnUncaughtExceptions) { 2013 result += '* breaking on UNCAUGHT exceptions is enabled\n'; 2014 } else { 2015 result += '* all exception breakpoints are disabled\n'; 2016 } 2017 details.text = result; 2018 break; 2019 2020 case 'setexceptionbreak': 2021 result = 'Break on ' + body.type + ' exceptions: '; 2022 result += body.enabled ? 'enabled' : 'disabled'; 2023 details.text = result; 2024 break; 2025 2026 case 'backtrace': 2027 if (body.totalFrames == 0) { 2028 result = '(empty stack)'; 2029 } else { 2030 var result = 'Frames #' + body.fromFrame + ' to #' + 2031 (body.toFrame - 1) + ' of ' + body.totalFrames + '\n'; 2032 for (i = 0; i < body.frames.length; i++) { 2033 if (i != 0) result += '\n'; 2034 result += body.frames[i].text; 2035 } 2036 } 2037 details.text = result; 2038 break; 2039 2040 case 'frame': 2041 if (last_cmd === 'info locals') { 2042 var locals = body.locals; 2043 if (locals.length === 0) { 2044 result = 'No locals'; 2045 } else { 2046 for (var i = 0; i < locals.length; i++) { 2047 var local = locals[i]; 2048 result += local.name + ' = '; 2049 result += refObjectToString_(response, local.value.ref); 2050 result += '\n'; 2051 } 2052 } 2053 } else if (last_cmd === 'info args') { 2054 var args = body.arguments; 2055 if (args.length === 0) { 2056 result = 'No arguments'; 2057 } else { 2058 for (var i = 0; i < args.length; i++) { 2059 var arg = args[i]; 2060 result += arg.name + ' = '; 2061 result += refObjectToString_(response, arg.value.ref); 2062 result += '\n'; 2063 } 2064 } 2065 } else { 2066 result = SourceUnderline(body.sourceLineText, 2067 body.column); 2068 Debug.State.currentSourceLine = body.line; 2069 Debug.State.currentFrame = body.index; 2070 Debug.State.displaySourceStartLine = -1; 2071 Debug.State.displaySourceEndLine = -1; 2072 } 2073 details.text = result; 2074 break; 2075 2076 case 'scopes': 2077 if (body.totalScopes == 0) { 2078 result = '(no scopes)'; 2079 } else { 2080 result = 'Scopes #' + body.fromScope + ' to #' + 2081 (body.toScope - 1) + ' of ' + body.totalScopes + '\n'; 2082 for (i = 0; i < body.scopes.length; i++) { 2083 if (i != 0) { 2084 result += '\n'; 2085 } 2086 result += formatScope_(body.scopes[i]); 2087 } 2088 } 2089 details.text = result; 2090 break; 2091 2092 case 'scope': 2093 result += formatScope_(body); 2094 result += '\n'; 2095 var scope_object_value = response.lookup(body.object.ref); 2096 result += formatObject_(scope_object_value, true); 2097 details.text = result; 2098 break; 2099 2100 case 'evaluate': 2101 case 'lookup': 2102 case 'getobj': 2103 if (last_cmd == 'p' || last_cmd == 'print') { 2104 result = body.text; 2105 } else { 2106 var value; 2107 if (lookup_handle) { 2108 value = response.bodyValue(lookup_handle); 2109 } else { 2110 value = response.bodyValue(); 2111 } 2112 if (value.isObject()) { 2113 result += formatObject_(value, true); 2114 } else { 2115 result += 'type: '; 2116 result += value.type(); 2117 if (!value.isUndefined() && !value.isNull()) { 2118 result += ', '; 2119 if (value.isString()) { 2120 result += '"'; 2121 } 2122 result += value.value(); 2123 if (value.isString()) { 2124 result += '"'; 2125 } 2126 } 2127 result += '\n'; 2128 } 2129 } 2130 details.text = result; 2131 break; 2132 2133 case 'references': 2134 var count = body.length; 2135 result += 'found ' + count + ' objects'; 2136 result += '\n'; 2137 for (var i = 0; i < count; i++) { 2138 var value = response.bodyValue(i); 2139 result += formatObject_(value, false); 2140 result += '\n'; 2141 } 2142 details.text = result; 2143 break; 2144 2145 case 'source': 2146 // Get the source from the response. 2147 var source = body.source; 2148 var from_line = body.fromLine + 1; 2149 var lines = source.split('\n'); 2150 var maxdigits = 1 + Math.floor(log10(from_line + lines.length)); 2151 if (maxdigits < 3) { 2152 maxdigits = 3; 2153 } 2154 var result = ''; 2155 for (var num = 0; num < lines.length; num++) { 2156 // Check if there's an extra newline at the end. 2157 if (num == (lines.length - 1) && lines[num].length == 0) { 2158 break; 2159 } 2160 2161 var current_line = from_line + num; 2162 spacer = maxdigits - (1 + Math.floor(log10(current_line))); 2163 if (current_line == Debug.State.currentSourceLine + 1) { 2164 for (var i = 0; i < maxdigits; i++) { 2165 result += '>'; 2166 } 2167 result += ' '; 2168 } else { 2169 for (var i = 0; i < spacer; i++) { 2170 result += ' '; 2171 } 2172 result += current_line + ': '; 2173 } 2174 result += lines[num]; 2175 result += '\n'; 2176 } 2177 details.text = result; 2178 break; 2179 2180 case 'scripts': 2181 var result = ''; 2182 for (i = 0; i < body.length; i++) { 2183 if (i != 0) result += '\n'; 2184 if (body[i].id) { 2185 result += body[i].id; 2186 } else { 2187 result += '[no id]'; 2188 } 2189 result += ', '; 2190 if (body[i].name) { 2191 result += body[i].name; 2192 } else { 2193 if (body[i].compilationType == Debug.ScriptCompilationType.Eval 2194 && body[i].evalFromScript 2195 ) { 2196 result += 'eval from '; 2197 var script_value = response.lookup(body[i].evalFromScript.ref); 2198 result += ' ' + script_value.field('name'); 2199 result += ':' + (body[i].evalFromLocation.line + 1); 2200 result += ':' + body[i].evalFromLocation.column; 2201 } else if (body[i].compilationType == 2202 Debug.ScriptCompilationType.JSON) { 2203 result += 'JSON '; 2204 } else { // body[i].compilation == Debug.ScriptCompilationType.Host 2205 result += '[unnamed] '; 2206 } 2207 } 2208 result += ' (lines: '; 2209 result += body[i].lineCount; 2210 result += ', length: '; 2211 result += body[i].sourceLength; 2212 if (body[i].type == Debug.ScriptType.Native) { 2213 result += ', native'; 2214 } else if (body[i].type == Debug.ScriptType.Extension) { 2215 result += ', extension'; 2216 } 2217 result += '), ['; 2218 var sourceStart = body[i].sourceStart; 2219 if (sourceStart.length > 40) { 2220 sourceStart = sourceStart.substring(0, 37) + '...'; 2221 } 2222 result += sourceStart; 2223 result += ']'; 2224 } 2225 if (body.length == 0) { 2226 result = "no matching scripts found"; 2227 } 2228 details.text = result; 2229 break; 2230 2231 case 'threads': 2232 var result = 'Active V8 threads: ' + body.totalThreads + '\n'; 2233 body.threads.sort(function(a, b) { return a.id - b.id; }); 2234 for (i = 0; i < body.threads.length; i++) { 2235 result += body.threads[i].current ? '*' : ' '; 2236 result += ' '; 2237 result += body.threads[i].id; 2238 result += '\n'; 2239 } 2240 details.text = result; 2241 break; 2242 2243 case 'continue': 2244 details.text = "(running)"; 2245 break; 2246 2247 case 'v8flags': 2248 details.text = "flags set"; 2249 break; 2250 2251 case 'gc': 2252 details.text = "GC " + body.before + " => " + body.after; 2253 if (body.after > (1024*1024)) { 2254 details.text += 2255 " (" + roundNumber(body.before/(1024*1024), 1) + "M => " + 2256 roundNumber(body.after/(1024*1024), 1) + "M)"; 2257 } else if (body.after > 1024) { 2258 details.text += 2259 " (" + roundNumber(body.before/1024, 1) + "K => " + 2260 roundNumber(body.after/1024, 1) + "K)"; 2261 } 2262 break; 2263 2264 case 'lol-capture': 2265 details.text = decodeLolCaptureResponse(body); 2266 break; 2267 case 'lol-delete': 2268 details.text = decodeLolDeleteResponse(body); 2269 break; 2270 case 'lol-diff': 2271 details.text = decodeLolDiffResponse(body); 2272 break; 2273 case 'lol-getid': 2274 details.text = decodeLolGetIdResponse(body); 2275 break; 2276 case 'lol-info': 2277 details.text = decodeLolInfoResponse(body); 2278 break; 2279 case 'lol-print': 2280 details.text = decodeLolPrintResponse(body); 2281 break; 2282 case 'lol-reset': 2283 details.text = decodeLolResetResponse(body); 2284 break; 2285 case 'lol-retainers': 2286 details.text = decodeLolRetainersResponse(body); 2287 break; 2288 case 'lol-path': 2289 details.text = decodeLolPathResponse(body); 2290 break; 2291 2292 default: 2293 details.text = 2294 'Response for unknown command \'' + response.command() + '\'' + 2295 ' (' + response.raw_json() + ')'; 2296 } 2297 } catch (e) { 2298 details.text = 'Error: "' + e + '" formatting response'; 2299 } 2300 2301 return details; 2302 }; 2303 2304 2305 /** 2306 * Protocol packages send from the debugger. 2307 * @param {string} json - raw protocol packet as JSON string. 2308 * @constructor 2309 */ 2310 function ProtocolPackage(json) { 2311 this.raw_json_ = json; 2312 this.packet_ = JSON.parse(json); 2313 this.refs_ = []; 2314 if (this.packet_.refs) { 2315 for (var i = 0; i < this.packet_.refs.length; i++) { 2316 this.refs_[this.packet_.refs[i].handle] = this.packet_.refs[i]; 2317 } 2318 } 2319 } 2320 2321 2322 /** 2323 * Get the packet type. 2324 * @return {String} the packet type 2325 */ 2326 ProtocolPackage.prototype.type = function() { 2327 return this.packet_.type; 2328 } 2329 2330 2331 /** 2332 * Get the packet event. 2333 * @return {Object} the packet event 2334 */ 2335 ProtocolPackage.prototype.event = function() { 2336 return this.packet_.event; 2337 } 2338 2339 2340 /** 2341 * Get the packet request sequence. 2342 * @return {number} the packet request sequence 2343 */ 2344 ProtocolPackage.prototype.requestSeq = function() { 2345 return this.packet_.request_seq; 2346 } 2347 2348 2349 /** 2350 * Get the packet request sequence. 2351 * @return {number} the packet request sequence 2352 */ 2353 ProtocolPackage.prototype.running = function() { 2354 return this.packet_.running ? true : false; 2355 } 2356 2357 2358 ProtocolPackage.prototype.success = function() { 2359 return this.packet_.success ? true : false; 2360 } 2361 2362 2363 ProtocolPackage.prototype.message = function() { 2364 return this.packet_.message; 2365 } 2366 2367 2368 ProtocolPackage.prototype.command = function() { 2369 return this.packet_.command; 2370 } 2371 2372 2373 ProtocolPackage.prototype.body = function() { 2374 return this.packet_.body; 2375 } 2376 2377 2378 ProtocolPackage.prototype.bodyValue = function(index) { 2379 if (index != null) { 2380 return new ProtocolValue(this.packet_.body[index], this); 2381 } else { 2382 return new ProtocolValue(this.packet_.body, this); 2383 } 2384 } 2385 2386 2387 ProtocolPackage.prototype.body = function() { 2388 return this.packet_.body; 2389 } 2390 2391 2392 ProtocolPackage.prototype.lookup = function(handle) { 2393 var value = this.refs_[handle]; 2394 if (value) { 2395 return new ProtocolValue(value, this); 2396 } else { 2397 return new ProtocolReference(handle); 2398 } 2399 } 2400 2401 2402 ProtocolPackage.prototype.raw_json = function() { 2403 return this.raw_json_; 2404 } 2405 2406 2407 function ProtocolValue(value, packet) { 2408 this.value_ = value; 2409 this.packet_ = packet; 2410 } 2411 2412 2413 /** 2414 * Get the value type. 2415 * @return {String} the value type 2416 */ 2417 ProtocolValue.prototype.type = function() { 2418 return this.value_.type; 2419 } 2420 2421 2422 /** 2423 * Get a metadata field from a protocol value. 2424 * @return {Object} the metadata field value 2425 */ 2426 ProtocolValue.prototype.field = function(name) { 2427 return this.value_[name]; 2428 } 2429 2430 2431 /** 2432 * Check is the value is a primitive value. 2433 * @return {boolean} true if the value is primitive 2434 */ 2435 ProtocolValue.prototype.isPrimitive = function() { 2436 return this.isUndefined() || this.isNull() || this.isBoolean() || 2437 this.isNumber() || this.isString(); 2438 } 2439 2440 2441 /** 2442 * Get the object handle. 2443 * @return {number} the value handle 2444 */ 2445 ProtocolValue.prototype.handle = function() { 2446 return this.value_.handle; 2447 } 2448 2449 2450 /** 2451 * Check is the value is undefined. 2452 * @return {boolean} true if the value is undefined 2453 */ 2454 ProtocolValue.prototype.isUndefined = function() { 2455 return this.value_.type == 'undefined'; 2456 } 2457 2458 2459 /** 2460 * Check is the value is null. 2461 * @return {boolean} true if the value is null 2462 */ 2463 ProtocolValue.prototype.isNull = function() { 2464 return this.value_.type == 'null'; 2465 } 2466 2467 2468 /** 2469 * Check is the value is a boolean. 2470 * @return {boolean} true if the value is a boolean 2471 */ 2472 ProtocolValue.prototype.isBoolean = function() { 2473 return this.value_.type == 'boolean'; 2474 } 2475 2476 2477 /** 2478 * Check is the value is a number. 2479 * @return {boolean} true if the value is a number 2480 */ 2481 ProtocolValue.prototype.isNumber = function() { 2482 return this.value_.type == 'number'; 2483 } 2484 2485 2486 /** 2487 * Check is the value is a string. 2488 * @return {boolean} true if the value is a string 2489 */ 2490 ProtocolValue.prototype.isString = function() { 2491 return this.value_.type == 'string'; 2492 } 2493 2494 2495 /** 2496 * Check is the value is an object. 2497 * @return {boolean} true if the value is an object 2498 */ 2499 ProtocolValue.prototype.isObject = function() { 2500 return this.value_.type == 'object' || this.value_.type == 'function' || 2501 this.value_.type == 'error' || this.value_.type == 'regexp'; 2502 } 2503 2504 2505 /** 2506 * Get the constructor function 2507 * @return {ProtocolValue} constructor function 2508 */ 2509 ProtocolValue.prototype.constructorFunctionValue = function() { 2510 var ctor = this.value_.constructorFunction; 2511 return this.packet_.lookup(ctor.ref); 2512 } 2513 2514 2515 /** 2516 * Get the __proto__ value 2517 * @return {ProtocolValue} __proto__ value 2518 */ 2519 ProtocolValue.prototype.protoObjectValue = function() { 2520 var proto = this.value_.protoObject; 2521 return this.packet_.lookup(proto.ref); 2522 } 2523 2524 2525 /** 2526 * Get the number og properties. 2527 * @return {number} the number of properties 2528 */ 2529 ProtocolValue.prototype.propertyCount = function() { 2530 return this.value_.properties ? this.value_.properties.length : 0; 2531 } 2532 2533 2534 /** 2535 * Get the specified property name. 2536 * @return {string} property name 2537 */ 2538 ProtocolValue.prototype.propertyName = function(index) { 2539 var property = this.value_.properties[index]; 2540 return property.name; 2541 } 2542 2543 2544 /** 2545 * Return index for the property name. 2546 * @param name The property name to look for 2547 * @return {number} index for the property name 2548 */ 2549 ProtocolValue.prototype.propertyIndex = function(name) { 2550 for (var i = 0; i < this.propertyCount(); i++) { 2551 if (this.value_.properties[i].name == name) { 2552 return i; 2553 } 2554 } 2555 return null; 2556 } 2557 2558 2559 /** 2560 * Get the specified property value. 2561 * @return {ProtocolValue} property value 2562 */ 2563 ProtocolValue.prototype.propertyValue = function(index) { 2564 var property = this.value_.properties[index]; 2565 return this.packet_.lookup(property.ref); 2566 } 2567 2568 2569 /** 2570 * Check is the value is a string. 2571 * @return {boolean} true if the value is a string 2572 */ 2573 ProtocolValue.prototype.value = function() { 2574 return this.value_.value; 2575 } 2576 2577 2578 ProtocolValue.prototype.valueString = function() { 2579 return this.value_.text; 2580 } 2581 2582 2583 function ProtocolReference(handle) { 2584 this.handle_ = handle; 2585 } 2586 2587 2588 ProtocolReference.prototype.handle = function() { 2589 return this.handle_; 2590 } 2591 2592 2593 function MakeJSONPair_(name, value) { 2594 return '"' + name + '":' + value; 2595 } 2596 2597 2598 function ArrayToJSONObject_(content) { 2599 return '{' + content.join(',') + '}'; 2600 } 2601 2602 2603 function ArrayToJSONArray_(content) { 2604 return '[' + content.join(',') + ']'; 2605 } 2606 2607 2608 function BooleanToJSON_(value) { 2609 return String(value); 2610 } 2611 2612 2613 function NumberToJSON_(value) { 2614 return String(value); 2615 } 2616 2617 2618 // Mapping of some control characters to avoid the \uXXXX syntax for most 2619 // commonly used control cahracters. 2620 const ctrlCharMap_ = { 2621 '\b': '\\b', 2622 '\t': '\\t', 2623 '\n': '\\n', 2624 '\f': '\\f', 2625 '\r': '\\r', 2626 '"' : '\\"', 2627 '\\': '\\\\' 2628 }; 2629 2630 2631 // Regular expression testing for ", \ and control characters (0x00 - 0x1F). 2632 const ctrlCharTest_ = new RegExp('["\\\\\x00-\x1F]'); 2633 2634 2635 // Regular expression matching ", \ and control characters (0x00 - 0x1F) 2636 // globally. 2637 const ctrlCharMatch_ = new RegExp('["\\\\\x00-\x1F]', 'g'); 2638 2639 2640 /** 2641 * Convert a String to its JSON representation (see http://www.json.org/). To 2642 * avoid depending on the String object this method calls the functions in 2643 * string.js directly and not through the value. 2644 * @param {String} value The String value to format as JSON 2645 * @return {string} JSON formatted String value 2646 */ 2647 function StringToJSON_(value) { 2648 // Check for" , \ and control characters (0x00 - 0x1F). No need to call 2649 // RegExpTest as ctrlchar is constructed using RegExp. 2650 if (ctrlCharTest_.test(value)) { 2651 // Replace ", \ and control characters (0x00 - 0x1F). 2652 return '"' + 2653 value.replace(ctrlCharMatch_, function (char) { 2654 // Use charmap if possible. 2655 var mapped = ctrlCharMap_[char]; 2656 if (mapped) return mapped; 2657 mapped = char.charCodeAt(); 2658 // Convert control character to unicode escape sequence. 2659 return '\\u00' + 2660 '0' + // TODO %NumberToRadixString(Math.floor(mapped / 16), 16) + 2661 '0' // TODO %NumberToRadixString(mapped % 16, 16); 2662 }) 2663 + '"'; 2664 } 2665 2666 // Simple string with no special characters. 2667 return '"' + value + '"'; 2668 } 2669 2670 2671 /** 2672 * Convert a Date to ISO 8601 format. To avoid depending on the Date object 2673 * this method calls the functions in date.js directly and not through the 2674 * value. 2675 * @param {Date} value The Date value to format as JSON 2676 * @return {string} JSON formatted Date value 2677 */ 2678 function DateToISO8601_(value) { 2679 function f(n) { 2680 return n < 10 ? '0' + n : n; 2681 } 2682 function g(n) { 2683 return n < 10 ? '00' + n : n < 100 ? '0' + n : n; 2684 } 2685 return builtins.GetUTCFullYearFrom(value) + '-' + 2686 f(builtins.GetUTCMonthFrom(value) + 1) + '-' + 2687 f(builtins.GetUTCDateFrom(value)) + 'T' + 2688 f(builtins.GetUTCHoursFrom(value)) + ':' + 2689 f(builtins.GetUTCMinutesFrom(value)) + ':' + 2690 f(builtins.GetUTCSecondsFrom(value)) + '.' + 2691 g(builtins.GetUTCMillisecondsFrom(value)) + 'Z'; 2692 } 2693 2694 2695 /** 2696 * Convert a Date to ISO 8601 format. To avoid depending on the Date object 2697 * this method calls the functions in date.js directly and not through the 2698 * value. 2699 * @param {Date} value The Date value to format as JSON 2700 * @return {string} JSON formatted Date value 2701 */ 2702 function DateToJSON_(value) { 2703 return '"' + DateToISO8601_(value) + '"'; 2704 } 2705 2706 2707 /** 2708 * Convert an Object to its JSON representation (see http://www.json.org/). 2709 * This implementation simply runs through all string property names and adds 2710 * each property to the JSON representation for some predefined types. For type 2711 * "object" the function calls itself recursively unless the object has the 2712 * function property "toJSONProtocol" in which case that is used. This is not 2713 * a general implementation but sufficient for the debugger. Note that circular 2714 * structures will cause infinite recursion. 2715 * @param {Object} object The object to format as JSON 2716 * @return {string} JSON formatted object value 2717 */ 2718 function SimpleObjectToJSON_(object) { 2719 var content = []; 2720 for (var key in object) { 2721 // Only consider string keys. 2722 if (typeof key == 'string') { 2723 var property_value = object[key]; 2724 2725 // Format the value based on its type. 2726 var property_value_json; 2727 switch (typeof property_value) { 2728 case 'object': 2729 if (property_value === null) { 2730 property_value_json = 'null'; 2731 } else if (typeof property_value.toJSONProtocol == 'function') { 2732 property_value_json = property_value.toJSONProtocol(true) 2733 } else if (property_value.constructor.name == 'Array'){ 2734 property_value_json = SimpleArrayToJSON_(property_value); 2735 } else { 2736 property_value_json = SimpleObjectToJSON_(property_value); 2737 } 2738 break; 2739 2740 case 'boolean': 2741 property_value_json = BooleanToJSON_(property_value); 2742 break; 2743 2744 case 'number': 2745 property_value_json = NumberToJSON_(property_value); 2746 break; 2747 2748 case 'string': 2749 property_value_json = StringToJSON_(property_value); 2750 break; 2751 2752 default: 2753 property_value_json = null; 2754 } 2755 2756 // Add the property if relevant. 2757 if (property_value_json) { 2758 content.push(StringToJSON_(key) + ':' + property_value_json); 2759 } 2760 } 2761 } 2762 2763 // Make JSON object representation. 2764 return '{' + content.join(',') + '}'; 2765 } 2766 2767 2768 /** 2769 * Convert an array to its JSON representation. This is a VERY simple 2770 * implementation just to support what is needed for the debugger. 2771 * @param {Array} arrya The array to format as JSON 2772 * @return {string} JSON formatted array value 2773 */ 2774 function SimpleArrayToJSON_(array) { 2775 // Make JSON array representation. 2776 var json = '['; 2777 for (var i = 0; i < array.length; i++) { 2778 if (i != 0) { 2779 json += ','; 2780 } 2781 var elem = array[i]; 2782 if (elem.toJSONProtocol) { 2783 json += elem.toJSONProtocol(true) 2784 } else if (typeof(elem) === 'object') { 2785 json += SimpleObjectToJSON_(elem); 2786 } else if (typeof(elem) === 'boolean') { 2787 json += BooleanToJSON_(elem); 2788 } else if (typeof(elem) === 'number') { 2789 json += NumberToJSON_(elem); 2790 } else if (typeof(elem) === 'string') { 2791 json += StringToJSON_(elem); 2792 } else { 2793 json += elem; 2794 } 2795 } 2796 json += ']'; 2797 return json; 2798 } 2799