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