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