1 // Copyright 2008 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 "use strict"; 6 7 String.prototype.startsWith = function (str) { 8 if (str.length > this.length) { 9 return false; 10 } 11 return this.substr(0, str.length) == str; 12 }; 13 14 function log10(num) { 15 return Math.log(num)/Math.log(10); 16 } 17 18 function ToInspectableObject(obj) { 19 if (!obj && typeof obj === 'object') { 20 return UNDEFINED; 21 } else { 22 return Object(obj); 23 } 24 } 25 26 function GetCompletions(global, last, full) { 27 var full_tokens = full.split(); 28 full = full_tokens.pop(); 29 var parts = full.split('.'); 30 parts.pop(); 31 var current = global; 32 for (var i = 0; i < parts.length; i++) { 33 var part = parts[i]; 34 var next = current[part]; 35 if (!next) { 36 return []; 37 } 38 current = next; 39 } 40 var result = []; 41 current = ToInspectableObject(current); 42 while (typeof current !== 'undefined') { 43 var mirror = new $debug.ObjectMirror(current); 44 var properties = mirror.properties(); 45 for (var i = 0; i < properties.length; i++) { 46 var name = properties[i].name(); 47 if (typeof name === 'string' && name.startsWith(last)) { 48 result.push(name); 49 } 50 } 51 current = ToInspectableObject(Object.getPrototypeOf(current)); 52 } 53 return result; 54 } 55 56 57 // Global object holding debugger related constants and state. 58 var Debug = {}; 59 60 61 // Debug events which can occour in the V8 JavaScript engine. These originate 62 // from the API include file v8-debug.h. 63 Debug.DebugEvent = { Break: 1, 64 Exception: 2, 65 NewFunction: 3, 66 BeforeCompile: 4, 67 AfterCompile: 5 }; 68 69 70 // The different types of scripts matching enum ScriptType in objects.h. 71 Debug.ScriptType = { Native: 0, 72 Extension: 1, 73 Normal: 2 }; 74 75 76 // The different types of script compilations matching enum 77 // Script::CompilationType in objects.h. 78 Debug.ScriptCompilationType = { Host: 0, 79 Eval: 1, 80 JSON: 2 }; 81 82 83 // The different types of scopes matching constants runtime.cc. 84 Debug.ScopeType = { Global: 0, 85 Local: 1, 86 With: 2, 87 Closure: 3, 88 Catch: 4, 89 Block: 5 }; 90 91 92 // Current debug state. 93 var kNoFrame = -1; 94 Debug.State = { 95 currentFrame: kNoFrame, 96 displaySourceStartLine: -1, 97 displaySourceEndLine: -1, 98 currentSourceLine: -1 99 }; 100 var trace_compile = false; // Tracing all compile events? 101 var trace_debug_json = false; // Tracing all debug json packets? 102 var last_cmd = ''; 103 var repeat_cmd_line = ''; 104 var is_running = true; 105 // Global variable used to store whether a handle was requested. 106 var lookup_handle = null; 107 108 // Copied from debug-delay.js. This is needed below: 109 function ScriptTypeFlag(type) { 110 return (1 << type); 111 } 112 113 114 // Process a debugger JSON message into a display text and a running status. 115 // This function returns an object with properties "text" and "running" holding 116 // this information. 117 function DebugMessageDetails(message) { 118 if (trace_debug_json) { 119 print("received: '" + message + "'"); 120 } 121 // Convert the JSON string to an object. 122 var response = new ProtocolPackage(message); 123 is_running = response.running(); 124 125 if (response.type() == 'event') { 126 return DebugEventDetails(response); 127 } else { 128 return DebugResponseDetails(response); 129 } 130 } 131 132 function DebugEventDetails(response) { 133 var details = {text:'', running:false}; 134 135 // Get the running state. 136 details.running = response.running(); 137 138 var body = response.body(); 139 var result = ''; 140 switch (response.event()) { 141 case 'break': 142 if (body.breakpoints) { 143 result += 'breakpoint'; 144 if (body.breakpoints.length > 1) { 145 result += 's'; 146 } 147 result += ' #'; 148 for (var i = 0; i < body.breakpoints.length; i++) { 149 if (i > 0) { 150 result += ', #'; 151 } 152 result += body.breakpoints[i]; 153 } 154 } else { 155 result += 'break'; 156 } 157 result += ' in '; 158 result += body.invocationText; 159 result += ', '; 160 result += SourceInfo(body); 161 result += '\n'; 162 result += SourceUnderline(body.sourceLineText, body.sourceColumn); 163 Debug.State.currentSourceLine = body.sourceLine; 164 Debug.State.displaySourceStartLine = -1; 165 Debug.State.displaySourceEndLine = -1; 166 Debug.State.currentFrame = 0; 167 details.text = result; 168 break; 169 170 case 'exception': 171 if (body.uncaught) { 172 result += 'Uncaught: '; 173 } else { 174 result += 'Exception: '; 175 } 176 result += '"'; 177 result += body.exception.text; 178 result += '"'; 179 if (body.sourceLine >= 0) { 180 result += ', '; 181 result += SourceInfo(body); 182 result += '\n'; 183 result += SourceUnderline(body.sourceLineText, body.sourceColumn); 184 Debug.State.currentSourceLine = body.sourceLine; 185 Debug.State.displaySourceStartLine = -1; 186 Debug.State.displaySourceEndLine = -1; 187 Debug.State.currentFrame = 0; 188 } else { 189 result += ' (empty stack)'; 190 Debug.State.currentSourceLine = -1; 191 Debug.State.displaySourceStartLine = -1; 192 Debug.State.displaySourceEndLine = -1; 193 Debug.State.currentFrame = kNoFrame; 194 } 195 details.text = result; 196 break; 197 198 case 'afterCompile': 199 if (trace_compile) { 200 result = 'Source ' + body.script.name + ' compiled:\n'; 201 var source = body.script.source; 202 if (!(source[source.length - 1] == '\n')) { 203 result += source; 204 } else { 205 result += source.substring(0, source.length - 1); 206 } 207 } 208 details.text = result; 209 break; 210 211 case 'scriptCollected': 212 details.text = result; 213 break; 214 215 default: 216 details.text = 'Unknown debug event ' + response.event(); 217 } 218 219 return details; 220 } 221 222 223 function SourceInfo(body) { 224 var result = ''; 225 226 if (body.script) { 227 if (body.script.name) { 228 result += body.script.name; 229 } else { 230 result += '[unnamed]'; 231 } 232 } 233 result += ' line '; 234 result += body.sourceLine + 1; 235 result += ' column '; 236 result += body.sourceColumn + 1; 237 238 return result; 239 } 240 241 242 function SourceUnderline(source_text, position) { 243 if (!source_text) { 244 return; 245 } 246 247 // Create an underline with a caret pointing to the source position. If the 248 // source contains a tab character the underline will have a tab character in 249 // the same place otherwise the underline will have a space character. 250 var underline = ''; 251 for (var i = 0; i < position; i++) { 252 if (source_text[i] == '\t') { 253 underline += '\t'; 254 } else { 255 underline += ' '; 256 } 257 } 258 underline += '^'; 259 260 // Return the source line text with the underline beneath. 261 return source_text + '\n' + underline; 262 } 263 264 265 // Converts a text command to a JSON request. 266 function DebugCommandToJSONRequest(cmd_line) { 267 var result = new DebugRequest(cmd_line).JSONRequest(); 268 if (trace_debug_json && result) { 269 print("sending: '" + result + "'"); 270 } 271 return result; 272 } 273 274 275 function DebugRequest(cmd_line) { 276 // If the very first character is a { assume that a JSON request have been 277 // entered as a command. Converting that to a JSON request is trivial. 278 if (cmd_line && cmd_line.length > 0 && cmd_line.charAt(0) == '{') { 279 this.request_ = cmd_line; 280 return; 281 } 282 283 // Check for a simple carriage return to repeat the last command: 284 var is_repeating = false; 285 if (cmd_line == '\n') { 286 if (is_running) { 287 cmd_line = 'break'; // Not in debugger mode, break with a frame request. 288 } else { 289 cmd_line = repeat_cmd_line; // use command to repeat. 290 is_repeating = true; 291 } 292 } 293 if (!is_running) { // Only save the command if in debugger mode. 294 repeat_cmd_line = cmd_line; // save last command. 295 } 296 297 // Trim string for leading and trailing whitespace. 298 cmd_line = cmd_line.replace(/^\s+|\s+$/g, ''); 299 300 // Find the command. 301 var pos = cmd_line.indexOf(' '); 302 var cmd; 303 var args; 304 if (pos == -1) { 305 cmd = cmd_line; 306 args = ''; 307 } else { 308 cmd = cmd_line.slice(0, pos); 309 args = cmd_line.slice(pos).replace(/^\s+|\s+$/g, ''); 310 } 311 312 if ((cmd === undefined) || !cmd) { 313 this.request_ = UNDEFINED; 314 return; 315 } 316 317 last_cmd = cmd; 318 319 // Switch on command. 320 switch (cmd) { 321 case 'continue': 322 case 'c': 323 this.request_ = this.continueCommandToJSONRequest_(args); 324 break; 325 326 case 'step': 327 case 's': 328 this.request_ = this.stepCommandToJSONRequest_(args, 'in'); 329 break; 330 331 case 'stepi': 332 case 'si': 333 this.request_ = this.stepCommandToJSONRequest_(args, 'min'); 334 break; 335 336 case 'next': 337 case 'n': 338 this.request_ = this.stepCommandToJSONRequest_(args, 'next'); 339 break; 340 341 case 'finish': 342 case 'fin': 343 this.request_ = this.stepCommandToJSONRequest_(args, 'out'); 344 break; 345 346 case 'backtrace': 347 case 'bt': 348 this.request_ = this.backtraceCommandToJSONRequest_(args); 349 break; 350 351 case 'frame': 352 case 'f': 353 this.request_ = this.frameCommandToJSONRequest_(args); 354 break; 355 356 case 'scopes': 357 this.request_ = this.scopesCommandToJSONRequest_(args); 358 break; 359 360 case 'scope': 361 this.request_ = this.scopeCommandToJSONRequest_(args); 362 break; 363 364 case 'disconnect': 365 case 'exit': 366 case 'quit': 367 this.request_ = this.disconnectCommandToJSONRequest_(args); 368 break; 369 370 case 'up': 371 this.request_ = 372 this.frameCommandToJSONRequest_('' + 373 (Debug.State.currentFrame + 1)); 374 break; 375 376 case 'down': 377 case 'do': 378 this.request_ = 379 this.frameCommandToJSONRequest_('' + 380 (Debug.State.currentFrame - 1)); 381 break; 382 383 case 'set': 384 case 'print': 385 case 'p': 386 this.request_ = this.printCommandToJSONRequest_(args); 387 break; 388 389 case 'dir': 390 this.request_ = this.dirCommandToJSONRequest_(args); 391 break; 392 393 case 'references': 394 this.request_ = this.referencesCommandToJSONRequest_(args); 395 break; 396 397 case 'instances': 398 this.request_ = this.instancesCommandToJSONRequest_(args); 399 break; 400 401 case 'list': 402 case 'l': 403 this.request_ = this.listCommandToJSONRequest_(args); 404 break; 405 case 'source': 406 this.request_ = this.sourceCommandToJSONRequest_(args); 407 break; 408 409 case 'scripts': 410 case 'script': 411 case 'scr': 412 this.request_ = this.scriptsCommandToJSONRequest_(args); 413 break; 414 415 case 'break': 416 case 'b': 417 this.request_ = this.breakCommandToJSONRequest_(args); 418 break; 419 420 case 'breakpoints': 421 case 'bb': 422 this.request_ = this.breakpointsCommandToJSONRequest_(args); 423 break; 424 425 case 'clear': 426 case 'delete': 427 case 'd': 428 this.request_ = this.clearCommandToJSONRequest_(args); 429 break; 430 431 case 'threads': 432 this.request_ = this.threadsCommandToJSONRequest_(args); 433 break; 434 435 case 'cond': 436 this.request_ = this.changeBreakpointCommandToJSONRequest_(args, 'cond'); 437 break; 438 439 case 'enable': 440 case 'en': 441 this.request_ = 442 this.changeBreakpointCommandToJSONRequest_(args, 'enable'); 443 break; 444 445 case 'disable': 446 case 'dis': 447 this.request_ = 448 this.changeBreakpointCommandToJSONRequest_(args, 'disable'); 449 break; 450 451 case 'ignore': 452 this.request_ = 453 this.changeBreakpointCommandToJSONRequest_(args, 'ignore'); 454 break; 455 456 case 'info': 457 case 'inf': 458 this.request_ = this.infoCommandToJSONRequest_(args); 459 break; 460 461 case 'flags': 462 this.request_ = this.v8FlagsToJSONRequest_(args); 463 break; 464 465 case 'gc': 466 this.request_ = this.gcToJSONRequest_(args); 467 break; 468 469 case 'trace': 470 case 'tr': 471 // Return undefined to indicate command handled internally (no JSON). 472 this.request_ = UNDEFINED; 473 this.traceCommand_(args); 474 break; 475 476 case 'help': 477 case '?': 478 this.helpCommand_(args); 479 // Return undefined to indicate command handled internally (no JSON). 480 this.request_ = UNDEFINED; 481 break; 482 483 default: 484 throw new Error('Unknown command "' + cmd + '"'); 485 } 486 } 487 488 DebugRequest.prototype.JSONRequest = function() { 489 return this.request_; 490 }; 491 492 493 function RequestPacket(command) { 494 this.seq = 0; 495 this.type = 'request'; 496 this.command = command; 497 } 498 499 500 RequestPacket.prototype.toJSONProtocol = function() { 501 // Encode the protocol header. 502 var json = '{'; 503 json += '"seq":' + this.seq; 504 json += ',"type":"' + this.type + '"'; 505 if (this.command) { 506 json += ',"command":' + JSON.stringify(this.command); 507 } 508 if (this.arguments) { 509 json += ',"arguments":'; 510 // Encode the arguments part. 511 if (this.arguments.toJSONProtocol) { 512 json += this.arguments.toJSONProtocol(); 513 } else { 514 json += JSON.stringify(this.arguments); 515 } 516 } 517 json += '}'; 518 return json; 519 }; 520 521 522 DebugRequest.prototype.createRequest = function(command) { 523 return new RequestPacket(command); 524 }; 525 526 527 // Create a JSON request for the evaluation command. 528 DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) { 529 lookup_handle = null; 530 531 // Check if the expression is a handle id in the form #<handle>#. 532 var handle_match = expression.match(/^#([0-9]*)#$/); 533 if (handle_match) { 534 // Remember the handle requested in a global variable. 535 lookup_handle = parseInt(handle_match[1]); 536 // Build a lookup request. 537 var request = this.createRequest('lookup'); 538 request.arguments = {}; 539 request.arguments.handles = [ lookup_handle ]; 540 return request.toJSONProtocol(); 541 } else { 542 // Build an evaluate request. 543 var request = this.createRequest('evaluate'); 544 request.arguments = {}; 545 request.arguments.expression = expression; 546 // Request a global evaluation if there is no current frame. 547 if (Debug.State.currentFrame == kNoFrame) { 548 request.arguments.global = true; 549 } 550 return request.toJSONProtocol(); 551 } 552 }; 553 554 555 // Create a JSON request for the references/instances command. 556 DebugRequest.prototype.makeReferencesJSONRequest_ = function(handle, type) { 557 // Build a references request. 558 var handle_match = handle.match(/^#([0-9]*)#$/); 559 if (handle_match) { 560 var request = this.createRequest('references'); 561 request.arguments = {}; 562 request.arguments.type = type; 563 request.arguments.handle = parseInt(handle_match[1]); 564 return request.toJSONProtocol(); 565 } else { 566 throw new Error('Invalid object id.'); 567 } 568 }; 569 570 571 // Create a JSON request for the continue command. 572 DebugRequest.prototype.continueCommandToJSONRequest_ = function(args) { 573 var request = this.createRequest('continue'); 574 return request.toJSONProtocol(); 575 }; 576 577 578 // Create a JSON request for the step command. 579 DebugRequest.prototype.stepCommandToJSONRequest_ = function(args, type) { 580 // Requesting a step is through the continue command with additional 581 // arguments. 582 var request = this.createRequest('continue'); 583 request.arguments = {}; 584 585 // Process arguments if any. 586 587 // Only process args if the command is 'step' which is indicated by type being 588 // set to 'in'. For all other commands, ignore the args. 589 if (args && args.length > 0) { 590 args = args.split(/\s+/g); 591 592 if (args.length > 2) { 593 throw new Error('Invalid step arguments.'); 594 } 595 596 if (args.length > 0) { 597 // Check if we have a gdb stype step command. If so, the 1st arg would 598 // be the step count. If it's not a number, then assume that we're 599 // parsing for the legacy v8 step command. 600 var stepcount = Number(args[0]); 601 if (stepcount == Number.NaN) { 602 // No step count at arg 1. Process as legacy d8 step command: 603 if (args.length == 2) { 604 var stepcount = parseInt(args[1]); 605 if (isNaN(stepcount) || stepcount <= 0) { 606 throw new Error('Invalid step count argument "' + args[0] + '".'); 607 } 608 request.arguments.stepcount = stepcount; 609 } 610 611 // Get the step action. 612 switch (args[0]) { 613 case 'in': 614 case 'i': 615 request.arguments.stepaction = 'in'; 616 break; 617 618 case 'min': 619 case 'm': 620 request.arguments.stepaction = 'min'; 621 break; 622 623 case 'next': 624 case 'n': 625 request.arguments.stepaction = 'next'; 626 break; 627 628 case 'out': 629 case 'o': 630 request.arguments.stepaction = 'out'; 631 break; 632 633 default: 634 throw new Error('Invalid step argument "' + args[0] + '".'); 635 } 636 637 } else { 638 // gdb style step commands: 639 request.arguments.stepaction = type; 640 request.arguments.stepcount = stepcount; 641 } 642 } 643 } else { 644 // Default is step of the specified type. 645 request.arguments.stepaction = type; 646 } 647 648 return request.toJSONProtocol(); 649 }; 650 651 652 // Create a JSON request for the backtrace command. 653 DebugRequest.prototype.backtraceCommandToJSONRequest_ = function(args) { 654 // Build a backtrace request from the text command. 655 var request = this.createRequest('backtrace'); 656 657 // Default is to show top 10 frames. 658 request.arguments = {}; 659 request.arguments.fromFrame = 0; 660 request.arguments.toFrame = 10; 661 662 args = args.split(/\s*[ ]+\s*/g); 663 if (args.length == 1 && args[0].length > 0) { 664 var frameCount = parseInt(args[0]); 665 if (frameCount > 0) { 666 // Show top frames. 667 request.arguments.fromFrame = 0; 668 request.arguments.toFrame = frameCount; 669 } else { 670 // Show bottom frames. 671 request.arguments.fromFrame = 0; 672 request.arguments.toFrame = -frameCount; 673 request.arguments.bottom = true; 674 } 675 } else if (args.length == 2) { 676 var fromFrame = parseInt(args[0]); 677 var toFrame = parseInt(args[1]); 678 if (isNaN(fromFrame) || fromFrame < 0) { 679 throw new Error('Invalid start frame argument "' + args[0] + '".'); 680 } 681 if (isNaN(toFrame) || toFrame < 0) { 682 throw new Error('Invalid end frame argument "' + args[1] + '".'); 683 } 684 if (fromFrame > toFrame) { 685 throw new Error('Invalid arguments start frame cannot be larger ' + 686 'than end frame.'); 687 } 688 // Show frame range. 689 request.arguments.fromFrame = fromFrame; 690 request.arguments.toFrame = toFrame + 1; 691 } else if (args.length > 2) { 692 throw new Error('Invalid backtrace arguments.'); 693 } 694 695 return request.toJSONProtocol(); 696 }; 697 698 699 // Create a JSON request for the frame command. 700 DebugRequest.prototype.frameCommandToJSONRequest_ = function(args) { 701 // Build a frame request from the text command. 702 var request = this.createRequest('frame'); 703 args = args.split(/\s*[ ]+\s*/g); 704 if (args.length > 0 && args[0].length > 0) { 705 request.arguments = {}; 706 request.arguments.number = args[0]; 707 } 708 return request.toJSONProtocol(); 709 }; 710 711 712 // Create a JSON request for the scopes command. 713 DebugRequest.prototype.scopesCommandToJSONRequest_ = function(args) { 714 // Build a scopes request from the text command. 715 var request = this.createRequest('scopes'); 716 return request.toJSONProtocol(); 717 }; 718 719 720 // Create a JSON request for the scope command. 721 DebugRequest.prototype.scopeCommandToJSONRequest_ = function(args) { 722 // Build a scope request from the text command. 723 var request = this.createRequest('scope'); 724 args = args.split(/\s*[ ]+\s*/g); 725 if (args.length > 0 && args[0].length > 0) { 726 request.arguments = {}; 727 request.arguments.number = args[0]; 728 } 729 return request.toJSONProtocol(); 730 }; 731 732 733 // Create a JSON request for the print command. 734 DebugRequest.prototype.printCommandToJSONRequest_ = function(args) { 735 // Build an evaluate request from the text command. 736 if (args.length == 0) { 737 throw new Error('Missing expression.'); 738 } 739 return this.makeEvaluateJSONRequest_(args); 740 }; 741 742 743 // Create a JSON request for the dir command. 744 DebugRequest.prototype.dirCommandToJSONRequest_ = function(args) { 745 // Build an evaluate request from the text command. 746 if (args.length == 0) { 747 throw new Error('Missing expression.'); 748 } 749 return this.makeEvaluateJSONRequest_(args); 750 }; 751 752 753 // Create a JSON request for the references command. 754 DebugRequest.prototype.referencesCommandToJSONRequest_ = function(args) { 755 // Build an evaluate request from the text command. 756 if (args.length == 0) { 757 throw new Error('Missing object id.'); 758 } 759 760 return this.makeReferencesJSONRequest_(args, 'referencedBy'); 761 }; 762 763 764 // Create a JSON request for the instances command. 765 DebugRequest.prototype.instancesCommandToJSONRequest_ = function(args) { 766 // Build an evaluate request from the text command. 767 if (args.length == 0) { 768 throw new Error('Missing object id.'); 769 } 770 771 // Build a references request. 772 return this.makeReferencesJSONRequest_(args, 'constructedBy'); 773 }; 774 775 776 // Create a JSON request for the list command. 777 DebugRequest.prototype.listCommandToJSONRequest_ = function(args) { 778 779 // Default is ten lines starting five lines before the current location. 780 if (Debug.State.displaySourceEndLine == -1) { 781 // If we list forwards, we will start listing after the last source end 782 // line. Set it to start from 5 lines before the current location. 783 Debug.State.displaySourceEndLine = Debug.State.currentSourceLine - 5; 784 // If we list backwards, we will start listing backwards from the last 785 // source start line. Set it to start from 1 lines before the current 786 // location. 787 Debug.State.displaySourceStartLine = Debug.State.currentSourceLine + 1; 788 } 789 790 var from = Debug.State.displaySourceEndLine + 1; 791 var lines = 10; 792 793 // Parse the arguments. 794 args = args.split(/\s*,\s*/g); 795 if (args == '') { 796 } else if ((args.length == 1) && (args[0] == '-')) { 797 from = Debug.State.displaySourceStartLine - lines; 798 } else if (args.length == 2) { 799 from = parseInt(args[0]); 800 lines = parseInt(args[1]) - from + 1; // inclusive of the ending line. 801 } else { 802 throw new Error('Invalid list arguments.'); 803 } 804 Debug.State.displaySourceStartLine = from; 805 Debug.State.displaySourceEndLine = from + lines - 1; 806 var sourceArgs = '' + from + ' ' + lines; 807 return this.sourceCommandToJSONRequest_(sourceArgs); 808 }; 809 810 811 // Create a JSON request for the source command. 812 DebugRequest.prototype.sourceCommandToJSONRequest_ = function(args) { 813 // Build a evaluate request from the text command. 814 var request = this.createRequest('source'); 815 816 // Default is ten lines starting five lines before the current location. 817 var from = Debug.State.currentSourceLine - 5; 818 var lines = 10; 819 820 // Parse the arguments. 821 args = args.split(/\s*[ ]+\s*/g); 822 if (args.length > 1 && args[0].length > 0 && args[1].length > 0) { 823 from = parseInt(args[0]) - 1; 824 lines = parseInt(args[1]); 825 } else if (args.length > 0 && args[0].length > 0) { 826 from = parseInt(args[0]) - 1; 827 } 828 829 if (from < 0) from = 0; 830 if (lines < 0) lines = 10; 831 832 // Request source arround current source location. 833 request.arguments = {}; 834 request.arguments.fromLine = from; 835 request.arguments.toLine = from + lines; 836 837 return request.toJSONProtocol(); 838 }; 839 840 841 // Create a JSON request for the scripts command. 842 DebugRequest.prototype.scriptsCommandToJSONRequest_ = function(args) { 843 // Build a evaluate request from the text command. 844 var request = this.createRequest('scripts'); 845 846 // Process arguments if any. 847 if (args && args.length > 0) { 848 args = args.split(/\s*[ ]+\s*/g); 849 850 if (args.length > 1) { 851 throw new Error('Invalid scripts arguments.'); 852 } 853 854 request.arguments = {}; 855 switch (args[0]) { 856 case 'natives': 857 request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Native); 858 break; 859 860 case 'extensions': 861 request.arguments.types = ScriptTypeFlag(Debug.ScriptType.Extension); 862 break; 863 864 case 'all': 865 request.arguments.types = 866 ScriptTypeFlag(Debug.ScriptType.Normal) | 867 ScriptTypeFlag(Debug.ScriptType.Native) | 868 ScriptTypeFlag(Debug.ScriptType.Extension); 869 break; 870 871 default: 872 // If the arg is not one of the know one aboves, then it must be a 873 // filter used for filtering the results: 874 request.arguments.filter = args[0]; 875 break; 876 } 877 } 878 879 return request.toJSONProtocol(); 880 }; 881 882 883 // Create a JSON request for the break command. 884 DebugRequest.prototype.breakCommandToJSONRequest_ = function(args) { 885 // Build a evaluate request from the text command. 886 // Process arguments if any. 887 if (args && args.length > 0) { 888 var target = args; 889 var type = 'function'; 890 var line; 891 var column; 892 var condition; 893 var pos; 894 895 var request = this.createRequest('setbreakpoint'); 896 897 // Break the args into target spec and condition if appropriate. 898 899 // Check for breakpoint condition. 900 pos = args.indexOf(' '); 901 if (pos > 0) { 902 target = args.substring(0, pos); 903 condition = args.substring(pos + 1, args.length); 904 } 905 906 // Check for script breakpoint (name:line[:column]). If no ':' in break 907 // specification it is considered a function break point. 908 pos = target.indexOf(':'); 909 if (pos > 0) { 910 var tmp = target.substring(pos + 1, target.length); 911 target = target.substring(0, pos); 912 if (target[0] == '/' && target[target.length - 1] == '/') { 913 type = 'scriptRegExp'; 914 target = target.substring(1, target.length - 1); 915 } else { 916 type = 'script'; 917 } 918 919 // Check for both line and column. 920 pos = tmp.indexOf(':'); 921 if (pos > 0) { 922 column = parseInt(tmp.substring(pos + 1, tmp.length)) - 1; 923 line = parseInt(tmp.substring(0, pos)) - 1; 924 } else { 925 line = parseInt(tmp) - 1; 926 } 927 } else if (target[0] == '#' && target[target.length - 1] == '#') { 928 type = 'handle'; 929 target = target.substring(1, target.length - 1); 930 } else { 931 type = 'function'; 932 } 933 934 request.arguments = {}; 935 request.arguments.type = type; 936 request.arguments.target = target; 937 request.arguments.line = line; 938 request.arguments.column = column; 939 request.arguments.condition = condition; 940 } else { 941 var request = this.createRequest('suspend'); 942 } 943 944 return request.toJSONProtocol(); 945 }; 946 947 948 DebugRequest.prototype.breakpointsCommandToJSONRequest_ = function(args) { 949 if (args && args.length > 0) { 950 throw new Error('Unexpected arguments.'); 951 } 952 var request = this.createRequest('listbreakpoints'); 953 return request.toJSONProtocol(); 954 }; 955 956 957 // Create a JSON request for the clear command. 958 DebugRequest.prototype.clearCommandToJSONRequest_ = function(args) { 959 // Build a evaluate request from the text command. 960 var request = this.createRequest('clearbreakpoint'); 961 962 // Process arguments if any. 963 if (args && args.length > 0) { 964 request.arguments = {}; 965 request.arguments.breakpoint = parseInt(args); 966 } else { 967 throw new Error('Invalid break arguments.'); 968 } 969 970 return request.toJSONProtocol(); 971 }; 972 973 974 // Create a JSON request for the change breakpoint command. 975 DebugRequest.prototype.changeBreakpointCommandToJSONRequest_ = 976 function(args, command) { 977 978 var request; 979 980 // Check for exception breaks first: 981 // en[able] exc[eptions] [all|unc[aught]] 982 // en[able] [all|unc[aught]] exc[eptions] 983 // dis[able] exc[eptions] [all|unc[aught]] 984 // dis[able] [all|unc[aught]] exc[eptions] 985 if ((command == 'enable' || command == 'disable') && 986 args && args.length > 1) { 987 var nextPos = args.indexOf(' '); 988 var arg1 = (nextPos > 0) ? args.substring(0, nextPos) : args; 989 var excType = null; 990 991 // Check for: 992 // en[able] exc[eptions] [all|unc[aught]] 993 // dis[able] exc[eptions] [all|unc[aught]] 994 if (arg1 == 'exc' || arg1 == 'exception' || arg1 == 'exceptions') { 995 996 var arg2 = (nextPos > 0) ? 997 args.substring(nextPos + 1, args.length) : 'all'; 998 if (!arg2) { 999 arg2 = 'all'; // if unspecified, set for all. 1000 } else if (arg2 == 'unc') { // check for short cut. 1001 arg2 = 'uncaught'; 1002 } 1003 excType = arg2; 1004 1005 // Check for: 1006 // en[able] [all|unc[aught]] exc[eptions] 1007 // dis[able] [all|unc[aught]] exc[eptions] 1008 } else if (arg1 == 'all' || arg1 == 'unc' || arg1 == 'uncaught') { 1009 1010 var arg2 = (nextPos > 0) ? 1011 args.substring(nextPos + 1, args.length) : null; 1012 if (arg2 == 'exc' || arg1 == 'exception' || arg1 == 'exceptions') { 1013 excType = arg1; 1014 if (excType == 'unc') { 1015 excType = 'uncaught'; 1016 } 1017 } 1018 } 1019 1020 // If we matched one of the command formats, then excType will be non-null: 1021 if (excType) { 1022 // Build a evaluate request from the text command. 1023 request = this.createRequest('setexceptionbreak'); 1024 1025 request.arguments = {}; 1026 request.arguments.type = excType; 1027 request.arguments.enabled = (command == 'enable'); 1028 1029 return request.toJSONProtocol(); 1030 } 1031 } 1032 1033 // Build a evaluate request from the text command. 1034 request = this.createRequest('changebreakpoint'); 1035 1036 // Process arguments if any. 1037 if (args && args.length > 0) { 1038 request.arguments = {}; 1039 var pos = args.indexOf(' '); 1040 var breakpointArg = args; 1041 var otherArgs; 1042 if (pos > 0) { 1043 breakpointArg = args.substring(0, pos); 1044 otherArgs = args.substring(pos + 1, args.length); 1045 } 1046 1047 request.arguments.breakpoint = parseInt(breakpointArg); 1048 1049 switch(command) { 1050 case 'cond': 1051 request.arguments.condition = otherArgs ? otherArgs : null; 1052 break; 1053 case 'enable': 1054 request.arguments.enabled = true; 1055 break; 1056 case 'disable': 1057 request.arguments.enabled = false; 1058 break; 1059 case 'ignore': 1060 request.arguments.ignoreCount = parseInt(otherArgs); 1061 break; 1062 default: 1063 throw new Error('Invalid arguments.'); 1064 } 1065 } else { 1066 throw new Error('Invalid arguments.'); 1067 } 1068 1069 return request.toJSONProtocol(); 1070 }; 1071 1072 1073 // Create a JSON request for the disconnect command. 1074 DebugRequest.prototype.disconnectCommandToJSONRequest_ = function(args) { 1075 var request; 1076 request = this.createRequest('disconnect'); 1077 return request.toJSONProtocol(); 1078 }; 1079 1080 1081 // Create a JSON request for the info command. 1082 DebugRequest.prototype.infoCommandToJSONRequest_ = function(args) { 1083 var request; 1084 if (args && (args == 'break' || args == 'br')) { 1085 // Build a evaluate request from the text command. 1086 request = this.createRequest('listbreakpoints'); 1087 last_cmd = 'info break'; 1088 } else if (args && (args == 'locals' || args == 'lo')) { 1089 // Build a evaluate request from the text command. 1090 request = this.createRequest('frame'); 1091 last_cmd = 'info locals'; 1092 } else if (args && (args == 'args' || args == 'ar')) { 1093 // Build a evaluate request from the text command. 1094 request = this.createRequest('frame'); 1095 last_cmd = 'info args'; 1096 } else { 1097 throw new Error('Invalid info arguments.'); 1098 } 1099 1100 return request.toJSONProtocol(); 1101 }; 1102 1103 1104 DebugRequest.prototype.v8FlagsToJSONRequest_ = function(args) { 1105 var request; 1106 request = this.createRequest('v8flags'); 1107 request.arguments = {}; 1108 request.arguments.flags = args; 1109 return request.toJSONProtocol(); 1110 }; 1111 1112 1113 DebugRequest.prototype.gcToJSONRequest_ = function(args) { 1114 var request; 1115 if (!args) { 1116 args = 'all'; 1117 } 1118 var args = args.split(/\s+/g); 1119 var cmd = args[0]; 1120 1121 switch(cmd) { 1122 case 'all': 1123 case 'quick': 1124 case 'full': 1125 case 'young': 1126 case 'old': 1127 case 'compact': 1128 case 'sweep': 1129 case 'scavenge': { 1130 if (cmd == 'young') { cmd = 'quick'; } 1131 else if (cmd == 'old') { cmd = 'full'; } 1132 1133 request = this.createRequest('gc'); 1134 request.arguments = {}; 1135 request.arguments.type = cmd; 1136 break; 1137 } 1138 // Else fall thru to the default case below to report the error. 1139 default: 1140 throw new Error('Missing arguments after ' + cmd + '.'); 1141 } 1142 return request.toJSONProtocol(); 1143 }; 1144 1145 1146 // Create a JSON request for the threads command. 1147 DebugRequest.prototype.threadsCommandToJSONRequest_ = function(args) { 1148 // Build a threads request from the text command. 1149 var request = this.createRequest('threads'); 1150 return request.toJSONProtocol(); 1151 }; 1152 1153 1154 // Handle the trace command. 1155 DebugRequest.prototype.traceCommand_ = function(args) { 1156 // Process arguments. 1157 if (args && args.length > 0) { 1158 if (args == 'compile') { 1159 trace_compile = !trace_compile; 1160 print('Tracing of compiled scripts ' + (trace_compile ? 'on' : 'off')); 1161 } else if (args === 'debug json' || args === 'json' || args === 'packets') { 1162 trace_debug_json = !trace_debug_json; 1163 print('Tracing of debug json packets ' + 1164 (trace_debug_json ? 'on' : 'off')); 1165 } else { 1166 throw new Error('Invalid trace arguments.'); 1167 } 1168 } else { 1169 throw new Error('Invalid trace arguments.'); 1170 } 1171 }; 1172 1173 // Handle the help command. 1174 DebugRequest.prototype.helpCommand_ = function(args) { 1175 // Help os quite simple. 1176 if (args && args.length > 0) { 1177 print('warning: arguments to \'help\' are ignored'); 1178 } 1179 1180 print('Note: <> denotes symbollic values to be replaced with real values.'); 1181 print('Note: [] denotes optional parts of commands, or optional options / arguments.'); 1182 print(' e.g. d[elete] - you get the same command if you type d or delete.'); 1183 print(''); 1184 print('[break] - break as soon as possible'); 1185 print('b[reak] location [condition]'); 1186 print(' - break on named function: location is a function name'); 1187 print(' - break on function: location is #<id>#'); 1188 print(' - break on script position: location is name:line[:column]'); 1189 print(''); 1190 print('clear <breakpoint #> - deletes the specified user defined breakpoint'); 1191 print('d[elete] <breakpoint #> - deletes the specified user defined breakpoint'); 1192 print('dis[able] <breakpoint #> - disables the specified user defined breakpoint'); 1193 print('dis[able] exc[eptions] [[all] | unc[aught]]'); 1194 print(' - disables breaking on exceptions'); 1195 print('en[able] <breakpoint #> - enables the specified user defined breakpoint'); 1196 print('en[able] exc[eptions] [[all] | unc[aught]]'); 1197 print(' - enables breaking on exceptions'); 1198 print(''); 1199 print('b[ack]t[race] [n] | [-n] | [from to]'); 1200 print(' - prints the stack back trace'); 1201 print('f[rame] - prints info about the current frame context'); 1202 print('f[rame] <frame #> - set context to specified frame #'); 1203 print('scopes'); 1204 print('scope <scope #>'); 1205 print(''); 1206 print('up - set context to caller of current frame'); 1207 print('do[wn] - set context to callee of current frame'); 1208 print('inf[o] br[eak] - prints info about breakpoints in use'); 1209 print('inf[o] ar[gs] - prints info about arguments of the current function'); 1210 print('inf[o] lo[cals] - prints info about locals in the current function'); 1211 print(''); 1212 print('step [in | next | out| min [step count]]'); 1213 print('c[ontinue] - continue executing after a breakpoint'); 1214 print('s[tep] [<N>] - step into the next N callees (default N is 1)'); 1215 print('s[tep]i [<N>] - step into the next N callees (default N is 1)'); 1216 print('n[ext] [<N>] - step over the next N callees (default N is 1)'); 1217 print('fin[ish] [<N>] - step out of N frames (default N is 1)'); 1218 print(''); 1219 print('p[rint] <expression> - prints the result of the specified expression'); 1220 print('dir <expression> - prints the object structure of the result'); 1221 print('set <var> = <expression> - executes the specified statement'); 1222 print(''); 1223 print('l[ist] - list the source code around for the current pc'); 1224 print('l[ist] [- | <start>,<end>] - list the specified range of source code'); 1225 print('source [from line [num lines]]'); 1226 print('scr[ipts] [native|extensions|all]'); 1227 print('scr[ipts] [<filter text>] - list scripts with the specified text in its description'); 1228 print(''); 1229 print('gc - runs the garbage collector'); 1230 print(''); 1231 print('trace compile'); 1232 // hidden command: trace debug json - toggles tracing of debug json packets 1233 print(''); 1234 print('disconnect|exit|quit - disconnects and quits the debugger'); 1235 print('help - prints this help information'); 1236 }; 1237 1238 1239 function formatHandleReference_(value) { 1240 if (value.handle() >= 0) { 1241 return '#' + value.handle() + '#'; 1242 } else { 1243 return '#Transient#'; 1244 } 1245 } 1246 1247 1248 function formatObject_(value, include_properties) { 1249 var result = ''; 1250 result += formatHandleReference_(value); 1251 result += ', type: object'; 1252 result += ', constructor '; 1253 var ctor = value.constructorFunctionValue(); 1254 result += formatHandleReference_(ctor); 1255 result += ', __proto__ '; 1256 var proto = value.protoObjectValue(); 1257 result += formatHandleReference_(proto); 1258 result += ', '; 1259 result += value.propertyCount(); 1260 result += ' properties.'; 1261 if (include_properties) { 1262 result += '\n'; 1263 for (var i = 0; i < value.propertyCount(); i++) { 1264 result += ' '; 1265 result += value.propertyName(i); 1266 result += ': '; 1267 var property_value = value.propertyValue(i); 1268 if (property_value instanceof ProtocolReference) { 1269 result += '<no type>'; 1270 } else { 1271 if (property_value && property_value.type()) { 1272 result += property_value.type(); 1273 } else { 1274 result += '<no type>'; 1275 } 1276 } 1277 result += ' '; 1278 result += formatHandleReference_(property_value); 1279 result += '\n'; 1280 } 1281 } 1282 return result; 1283 } 1284 1285 1286 function formatScope_(scope) { 1287 var result = ''; 1288 var index = scope.index; 1289 result += '#' + (index <= 9 ? '0' : '') + index; 1290 result += ' '; 1291 switch (scope.type) { 1292 case Debug.ScopeType.Global: 1293 result += 'Global, '; 1294 result += '#' + scope.object.ref + '#'; 1295 break; 1296 case Debug.ScopeType.Local: 1297 result += 'Local'; 1298 break; 1299 case Debug.ScopeType.With: 1300 result += 'With, '; 1301 result += '#' + scope.object.ref + '#'; 1302 break; 1303 case Debug.ScopeType.Catch: 1304 result += 'Catch, '; 1305 result += '#' + scope.object.ref + '#'; 1306 break; 1307 case Debug.ScopeType.Closure: 1308 result += 'Closure'; 1309 break; 1310 default: 1311 result += 'UNKNOWN'; 1312 } 1313 return result; 1314 } 1315 1316 1317 function refObjectToString_(protocolPackage, handle) { 1318 var value = protocolPackage.lookup(handle); 1319 var result = ''; 1320 if (value.isString()) { 1321 result = '"' + value.value() + '"'; 1322 } else if (value.isPrimitive()) { 1323 result = value.valueString(); 1324 } else if (value.isObject()) { 1325 result += formatObject_(value, true); 1326 } 1327 return result; 1328 } 1329 1330 1331 // Rounds number 'num' to 'length' decimal places. 1332 function roundNumber(num, length) { 1333 var factor = Math.pow(10, length); 1334 return Math.round(num * factor) / factor; 1335 } 1336 1337 1338 // Convert a JSON response to text for display in a text based debugger. 1339 function DebugResponseDetails(response) { 1340 var details = { text: '', running: false }; 1341 1342 try { 1343 if (!response.success()) { 1344 details.text = response.message(); 1345 return details; 1346 } 1347 1348 // Get the running state. 1349 details.running = response.running(); 1350 1351 var body = response.body(); 1352 var result = ''; 1353 switch (response.command()) { 1354 case 'suspend': 1355 details.text = 'stopped'; 1356 break; 1357 1358 case 'setbreakpoint': 1359 result = 'set breakpoint #'; 1360 result += body.breakpoint; 1361 details.text = result; 1362 break; 1363 1364 case 'clearbreakpoint': 1365 result = 'cleared breakpoint #'; 1366 result += body.breakpoint; 1367 details.text = result; 1368 break; 1369 1370 case 'changebreakpoint': 1371 result = 'successfully changed breakpoint'; 1372 details.text = result; 1373 break; 1374 1375 case 'listbreakpoints': 1376 result = 'breakpoints: (' + body.breakpoints.length + ')'; 1377 for (var i = 0; i < body.breakpoints.length; i++) { 1378 var breakpoint = body.breakpoints[i]; 1379 result += '\n id=' + breakpoint.number; 1380 result += ' type=' + breakpoint.type; 1381 if (breakpoint.script_id) { 1382 result += ' script_id=' + breakpoint.script_id; 1383 } 1384 if (breakpoint.script_name) { 1385 result += ' script_name=' + breakpoint.script_name; 1386 } 1387 if (breakpoint.script_regexp) { 1388 result += ' script_regexp=' + breakpoint.script_regexp; 1389 } 1390 result += ' line=' + (breakpoint.line + 1); 1391 if (breakpoint.column != null) { 1392 result += ' column=' + (breakpoint.column + 1); 1393 } 1394 if (breakpoint.groupId) { 1395 result += ' groupId=' + breakpoint.groupId; 1396 } 1397 if (breakpoint.ignoreCount) { 1398 result += ' ignoreCount=' + breakpoint.ignoreCount; 1399 } 1400 if (breakpoint.active === false) { 1401 result += ' inactive'; 1402 } 1403 if (breakpoint.condition) { 1404 result += ' condition=' + breakpoint.condition; 1405 } 1406 result += ' hit_count=' + breakpoint.hit_count; 1407 } 1408 if (body.breakpoints.length === 0) { 1409 result = "No user defined breakpoints\n"; 1410 } else { 1411 result += '\n'; 1412 } 1413 if (body.breakOnExceptions) { 1414 result += '* breaking on ALL exceptions is enabled\n'; 1415 } else if (body.breakOnUncaughtExceptions) { 1416 result += '* breaking on UNCAUGHT exceptions is enabled\n'; 1417 } else { 1418 result += '* all exception breakpoints are disabled\n'; 1419 } 1420 details.text = result; 1421 break; 1422 1423 case 'setexceptionbreak': 1424 result = 'Break on ' + body.type + ' exceptions: '; 1425 result += body.enabled ? 'enabled' : 'disabled'; 1426 details.text = result; 1427 break; 1428 1429 case 'backtrace': 1430 if (body.totalFrames == 0) { 1431 result = '(empty stack)'; 1432 } else { 1433 var result = 'Frames #' + body.fromFrame + ' to #' + 1434 (body.toFrame - 1) + ' of ' + body.totalFrames + '\n'; 1435 for (i = 0; i < body.frames.length; i++) { 1436 if (i != 0) result += '\n'; 1437 result += body.frames[i].text; 1438 } 1439 } 1440 details.text = result; 1441 break; 1442 1443 case 'frame': 1444 if (last_cmd === 'info locals') { 1445 var locals = body.locals; 1446 if (locals.length === 0) { 1447 result = 'No locals'; 1448 } else { 1449 for (var i = 0; i < locals.length; i++) { 1450 var local = locals[i]; 1451 result += local.name + ' = '; 1452 result += refObjectToString_(response, local.value.ref); 1453 result += '\n'; 1454 } 1455 } 1456 } else if (last_cmd === 'info args') { 1457 var args = body.arguments; 1458 if (args.length === 0) { 1459 result = 'No arguments'; 1460 } else { 1461 for (var i = 0; i < args.length; i++) { 1462 var arg = args[i]; 1463 result += arg.name + ' = '; 1464 result += refObjectToString_(response, arg.value.ref); 1465 result += '\n'; 1466 } 1467 } 1468 } else { 1469 result = SourceUnderline(body.sourceLineText, 1470 body.column); 1471 Debug.State.currentSourceLine = body.line; 1472 Debug.State.currentFrame = body.index; 1473 Debug.State.displaySourceStartLine = -1; 1474 Debug.State.displaySourceEndLine = -1; 1475 } 1476 details.text = result; 1477 break; 1478 1479 case 'scopes': 1480 if (body.totalScopes == 0) { 1481 result = '(no scopes)'; 1482 } else { 1483 result = 'Scopes #' + body.fromScope + ' to #' + 1484 (body.toScope - 1) + ' of ' + body.totalScopes + '\n'; 1485 for (i = 0; i < body.scopes.length; i++) { 1486 if (i != 0) { 1487 result += '\n'; 1488 } 1489 result += formatScope_(body.scopes[i]); 1490 } 1491 } 1492 details.text = result; 1493 break; 1494 1495 case 'scope': 1496 result += formatScope_(body); 1497 result += '\n'; 1498 var scope_object_value = response.lookup(body.object.ref); 1499 result += formatObject_(scope_object_value, true); 1500 details.text = result; 1501 break; 1502 1503 case 'evaluate': 1504 case 'lookup': 1505 case 'getobj': 1506 if (last_cmd == 'p' || last_cmd == 'print') { 1507 result = body.text; 1508 } else { 1509 var value; 1510 if (lookup_handle) { 1511 value = response.bodyValue(lookup_handle); 1512 } else { 1513 value = response.bodyValue(); 1514 } 1515 if (value.isObject()) { 1516 result += formatObject_(value, true); 1517 } else { 1518 result += 'type: '; 1519 result += value.type(); 1520 if (!value.isUndefined() && !value.isNull()) { 1521 result += ', '; 1522 if (value.isString()) { 1523 result += '"'; 1524 } 1525 result += value.value(); 1526 if (value.isString()) { 1527 result += '"'; 1528 } 1529 } 1530 result += '\n'; 1531 } 1532 } 1533 details.text = result; 1534 break; 1535 1536 case 'references': 1537 var count = body.length; 1538 result += 'found ' + count + ' objects'; 1539 result += '\n'; 1540 for (var i = 0; i < count; i++) { 1541 var value = response.bodyValue(i); 1542 result += formatObject_(value, false); 1543 result += '\n'; 1544 } 1545 details.text = result; 1546 break; 1547 1548 case 'source': 1549 // Get the source from the response. 1550 var source = body.source; 1551 var from_line = body.fromLine + 1; 1552 var lines = source.split('\n'); 1553 var maxdigits = 1 + Math.floor(log10(from_line + lines.length)); 1554 if (maxdigits < 3) { 1555 maxdigits = 3; 1556 } 1557 var result = ''; 1558 for (var num = 0; num < lines.length; num++) { 1559 // Check if there's an extra newline at the end. 1560 if (num == (lines.length - 1) && lines[num].length == 0) { 1561 break; 1562 } 1563 1564 var current_line = from_line + num; 1565 var spacer = maxdigits - (1 + Math.floor(log10(current_line))); 1566 if (current_line == Debug.State.currentSourceLine + 1) { 1567 for (var i = 0; i < maxdigits; i++) { 1568 result += '>'; 1569 } 1570 result += ' '; 1571 } else { 1572 for (var i = 0; i < spacer; i++) { 1573 result += ' '; 1574 } 1575 result += current_line + ': '; 1576 } 1577 result += lines[num]; 1578 result += '\n'; 1579 } 1580 details.text = result; 1581 break; 1582 1583 case 'scripts': 1584 var result = ''; 1585 for (i = 0; i < body.length; i++) { 1586 if (i != 0) result += '\n'; 1587 if (body[i].id) { 1588 result += body[i].id; 1589 } else { 1590 result += '[no id]'; 1591 } 1592 result += ', '; 1593 if (body[i].name) { 1594 result += body[i].name; 1595 } else { 1596 if (body[i].compilationType == Debug.ScriptCompilationType.Eval 1597 && body[i].evalFromScript 1598 ) { 1599 result += 'eval from '; 1600 var script_value = response.lookup(body[i].evalFromScript.ref); 1601 result += ' ' + script_value.field('name'); 1602 result += ':' + (body[i].evalFromLocation.line + 1); 1603 result += ':' + body[i].evalFromLocation.column; 1604 } else if (body[i].compilationType == 1605 Debug.ScriptCompilationType.JSON) { 1606 result += 'JSON '; 1607 } else { // body[i].compilation == Debug.ScriptCompilationType.Host 1608 result += '[unnamed] '; 1609 } 1610 } 1611 result += ' (lines: '; 1612 result += body[i].lineCount; 1613 result += ', length: '; 1614 result += body[i].sourceLength; 1615 if (body[i].type == Debug.ScriptType.Native) { 1616 result += ', native'; 1617 } else if (body[i].type == Debug.ScriptType.Extension) { 1618 result += ', extension'; 1619 } 1620 result += '), ['; 1621 var sourceStart = body[i].sourceStart; 1622 if (sourceStart.length > 40) { 1623 sourceStart = sourceStart.substring(0, 37) + '...'; 1624 } 1625 result += sourceStart; 1626 result += ']'; 1627 } 1628 if (body.length == 0) { 1629 result = "no matching scripts found"; 1630 } 1631 details.text = result; 1632 break; 1633 1634 case 'threads': 1635 var result = 'Active V8 threads: ' + body.totalThreads + '\n'; 1636 body.threads.sort(function(a, b) { return a.id - b.id; }); 1637 for (i = 0; i < body.threads.length; i++) { 1638 result += body.threads[i].current ? '*' : ' '; 1639 result += ' '; 1640 result += body.threads[i].id; 1641 result += '\n'; 1642 } 1643 details.text = result; 1644 break; 1645 1646 case 'continue': 1647 details.text = "(running)"; 1648 break; 1649 1650 case 'v8flags': 1651 details.text = "flags set"; 1652 break; 1653 1654 case 'gc': 1655 details.text = "GC " + body.before + " => " + body.after; 1656 if (body.after > (1024*1024)) { 1657 details.text += 1658 " (" + roundNumber(body.before/(1024*1024), 1) + "M => " + 1659 roundNumber(body.after/(1024*1024), 1) + "M)"; 1660 } else if (body.after > 1024) { 1661 details.text += 1662 " (" + roundNumber(body.before/1024, 1) + "K => " + 1663 roundNumber(body.after/1024, 1) + "K)"; 1664 } 1665 break; 1666 1667 default: 1668 details.text = 1669 'Response for unknown command \'' + response.command() + '\'' + 1670 ' (' + response.raw_json() + ')'; 1671 } 1672 } catch (e) { 1673 details.text = 'Error: "' + e + '" formatting response'; 1674 } 1675 1676 return details; 1677 } 1678 1679 1680 /** 1681 * Protocol packages send from the debugger. 1682 * @param {string} json - raw protocol packet as JSON string. 1683 * @constructor 1684 */ 1685 function ProtocolPackage(json) { 1686 this.raw_json_ = json; 1687 this.packet_ = JSON.parse(json); 1688 this.refs_ = []; 1689 if (this.packet_.refs) { 1690 for (var i = 0; i < this.packet_.refs.length; i++) { 1691 this.refs_[this.packet_.refs[i].handle] = this.packet_.refs[i]; 1692 } 1693 } 1694 } 1695 1696 1697 /** 1698 * Get the packet type. 1699 * @return {String} the packet type 1700 */ 1701 ProtocolPackage.prototype.type = function() { 1702 return this.packet_.type; 1703 }; 1704 1705 1706 /** 1707 * Get the packet event. 1708 * @return {Object} the packet event 1709 */ 1710 ProtocolPackage.prototype.event = function() { 1711 return this.packet_.event; 1712 }; 1713 1714 1715 /** 1716 * Get the packet request sequence. 1717 * @return {number} the packet request sequence 1718 */ 1719 ProtocolPackage.prototype.requestSeq = function() { 1720 return this.packet_.request_seq; 1721 }; 1722 1723 1724 /** 1725 * Get the packet request sequence. 1726 * @return {number} the packet request sequence 1727 */ 1728 ProtocolPackage.prototype.running = function() { 1729 return this.packet_.running ? true : false; 1730 }; 1731 1732 1733 ProtocolPackage.prototype.success = function() { 1734 return this.packet_.success ? true : false; 1735 }; 1736 1737 1738 ProtocolPackage.prototype.message = function() { 1739 return this.packet_.message; 1740 }; 1741 1742 1743 ProtocolPackage.prototype.command = function() { 1744 return this.packet_.command; 1745 }; 1746 1747 1748 ProtocolPackage.prototype.body = function() { 1749 return this.packet_.body; 1750 }; 1751 1752 1753 ProtocolPackage.prototype.bodyValue = function(index) { 1754 if (index != null) { 1755 return new ProtocolValue(this.packet_.body[index], this); 1756 } else { 1757 return new ProtocolValue(this.packet_.body, this); 1758 } 1759 }; 1760 1761 1762 ProtocolPackage.prototype.body = function() { 1763 return this.packet_.body; 1764 }; 1765 1766 1767 ProtocolPackage.prototype.lookup = function(handle) { 1768 var value = this.refs_[handle]; 1769 if (value) { 1770 return new ProtocolValue(value, this); 1771 } else { 1772 return new ProtocolReference(handle); 1773 } 1774 }; 1775 1776 1777 ProtocolPackage.prototype.raw_json = function() { 1778 return this.raw_json_; 1779 }; 1780 1781 1782 function ProtocolValue(value, packet) { 1783 this.value_ = value; 1784 this.packet_ = packet; 1785 } 1786 1787 1788 /** 1789 * Get the value type. 1790 * @return {String} the value type 1791 */ 1792 ProtocolValue.prototype.type = function() { 1793 return this.value_.type; 1794 }; 1795 1796 1797 /** 1798 * Get a metadata field from a protocol value. 1799 * @return {Object} the metadata field value 1800 */ 1801 ProtocolValue.prototype.field = function(name) { 1802 return this.value_[name]; 1803 }; 1804 1805 1806 /** 1807 * Check is the value is a primitive value. 1808 * @return {boolean} true if the value is primitive 1809 */ 1810 ProtocolValue.prototype.isPrimitive = function() { 1811 return this.isUndefined() || this.isNull() || this.isBoolean() || 1812 this.isNumber() || this.isString(); 1813 }; 1814 1815 1816 /** 1817 * Get the object handle. 1818 * @return {number} the value handle 1819 */ 1820 ProtocolValue.prototype.handle = function() { 1821 return this.value_.handle; 1822 }; 1823 1824 1825 /** 1826 * Check is the value is undefined. 1827 * @return {boolean} true if the value is undefined 1828 */ 1829 ProtocolValue.prototype.isUndefined = function() { 1830 return this.value_.type == 'undefined'; 1831 }; 1832 1833 1834 /** 1835 * Check is the value is null. 1836 * @return {boolean} true if the value is null 1837 */ 1838 ProtocolValue.prototype.isNull = function() { 1839 return this.value_.type == 'null'; 1840 }; 1841 1842 1843 /** 1844 * Check is the value is a boolean. 1845 * @return {boolean} true if the value is a boolean 1846 */ 1847 ProtocolValue.prototype.isBoolean = function() { 1848 return this.value_.type == 'boolean'; 1849 }; 1850 1851 1852 /** 1853 * Check is the value is a number. 1854 * @return {boolean} true if the value is a number 1855 */ 1856 ProtocolValue.prototype.isNumber = function() { 1857 return this.value_.type == 'number'; 1858 }; 1859 1860 1861 /** 1862 * Check is the value is a string. 1863 * @return {boolean} true if the value is a string 1864 */ 1865 ProtocolValue.prototype.isString = function() { 1866 return this.value_.type == 'string'; 1867 }; 1868 1869 1870 /** 1871 * Check is the value is an object. 1872 * @return {boolean} true if the value is an object 1873 */ 1874 ProtocolValue.prototype.isObject = function() { 1875 return this.value_.type == 'object' || this.value_.type == 'function' || 1876 this.value_.type == 'error' || this.value_.type == 'regexp'; 1877 }; 1878 1879 1880 /** 1881 * Get the constructor function 1882 * @return {ProtocolValue} constructor function 1883 */ 1884 ProtocolValue.prototype.constructorFunctionValue = function() { 1885 var ctor = this.value_.constructorFunction; 1886 return this.packet_.lookup(ctor.ref); 1887 }; 1888 1889 1890 /** 1891 * Get the __proto__ value 1892 * @return {ProtocolValue} __proto__ value 1893 */ 1894 ProtocolValue.prototype.protoObjectValue = function() { 1895 var proto = this.value_.protoObject; 1896 return this.packet_.lookup(proto.ref); 1897 }; 1898 1899 1900 /** 1901 * Get the number og properties. 1902 * @return {number} the number of properties 1903 */ 1904 ProtocolValue.prototype.propertyCount = function() { 1905 return this.value_.properties ? this.value_.properties.length : 0; 1906 }; 1907 1908 1909 /** 1910 * Get the specified property name. 1911 * @return {string} property name 1912 */ 1913 ProtocolValue.prototype.propertyName = function(index) { 1914 var property = this.value_.properties[index]; 1915 return property.name; 1916 }; 1917 1918 1919 /** 1920 * Return index for the property name. 1921 * @param name The property name to look for 1922 * @return {number} index for the property name 1923 */ 1924 ProtocolValue.prototype.propertyIndex = function(name) { 1925 for (var i = 0; i < this.propertyCount(); i++) { 1926 if (this.value_.properties[i].name == name) { 1927 return i; 1928 } 1929 } 1930 return null; 1931 }; 1932 1933 1934 /** 1935 * Get the specified property value. 1936 * @return {ProtocolValue} property value 1937 */ 1938 ProtocolValue.prototype.propertyValue = function(index) { 1939 var property = this.value_.properties[index]; 1940 return this.packet_.lookup(property.ref); 1941 }; 1942 1943 1944 /** 1945 * Check is the value is a string. 1946 * @return {boolean} true if the value is a string 1947 */ 1948 ProtocolValue.prototype.value = function() { 1949 return this.value_.value; 1950 }; 1951 1952 1953 ProtocolValue.prototype.valueString = function() { 1954 return this.value_.text; 1955 }; 1956 1957 1958 function ProtocolReference(handle) { 1959 this.handle_ = handle; 1960 } 1961 1962 1963 ProtocolReference.prototype.handle = function() { 1964 return this.handle_; 1965 }; 1966 1967 1968 // A more universal stringify that supports more types than JSON. 1969 // Used by the d8 shell to output results. 1970 var stringifyDepthLimit = 4; // To avoid crashing on cyclic objects 1971 1972 function Stringify(x, depth) { 1973 if (depth === undefined) 1974 depth = stringifyDepthLimit; 1975 else if (depth === 0) 1976 return "*"; 1977 switch (typeof x) { 1978 case "undefined": 1979 return "undefined"; 1980 case "boolean": 1981 case "number": 1982 case "function": 1983 return x.toString(); 1984 case "string": 1985 return "\"" + x.toString() + "\""; 1986 case "symbol": 1987 return "Symbol(" + (x.name ? Stringify(x.name, depth) : "") + ")" 1988 case "object": 1989 if (IS_NULL(x)) return "null"; 1990 if (x.constructor && x.constructor.name === "Array") { 1991 var elems = []; 1992 for (var i = 0; i < x.length; ++i) { 1993 elems.push( 1994 {}.hasOwnProperty.call(x, i) ? Stringify(x[i], depth - 1) : ""); 1995 } 1996 return "[" + elems.join(", ") + "]"; 1997 } 1998 try { 1999 var string = String(x); 2000 if (string && string !== "[object Object]") return string; 2001 } catch(e) {} 2002 var props = []; 2003 for (var name in x) { 2004 var desc = Object.getOwnPropertyDescriptor(x, name); 2005 if (IS_UNDEFINED(desc)) continue; 2006 if ("value" in desc) { 2007 props.push(name + ": " + Stringify(desc.value, depth - 1)); 2008 } 2009 if ("get" in desc) { 2010 var getter = desc.get.toString(); 2011 props.push("get " + name + getter.slice(getter.indexOf('('))); 2012 } 2013 if ("set" in desc) { 2014 var setter = desc.set.toString(); 2015 props.push("set " + name + setter.slice(setter.indexOf('('))); 2016 } 2017 } 2018 return "{" + props.join(", ") + "}"; 2019 default: 2020 return "[crazy non-standard shit]"; 2021 } 2022 } 2023