1 /* See license.txt for terms of usage */ 2 3 define([ 4 "firebug/lib/object", 5 "firebug/firebug", 6 "firebug/lib/url", 7 "firebug/js/sourceLink", 8 "firebug/js/stackFrame", 9 ], 10 function(Obj, Firebug, Url, SourceLink, StackFrame) { 11 12 // ********************************************************************************************* // 13 // Constants 14 15 const Cc = Components.classes; 16 const Ci = Components.interfaces; 17 18 const PCMAP_SOURCETEXT = Ci.jsdIScript.PCMAP_SOURCETEXT; 19 const PCMAP_PRETTYPRINT = Ci.jsdIScript.PCMAP_PRETTYPRINT; 20 21 var jsd = Cc["@mozilla.org/js/jsd/debugger-service;1"].getService(Ci.jsdIDebuggerService); 22 23 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 24 25 /** 26 * SourceFile one for every compilation unit. 27 * Unique URL for each. (href) 28 * Unique outerScript, the statements outside of any function defintion 29 * sourceCache keyed by href has source for this compilation unit 30 * Stored by href in context. 31 * Contains array of jsdIScript for functions (scripts) defined in this unit 32 * May contain line table (for sources viewed) 33 */ 34 Firebug.SourceFile = function (compilation_unit_type) 35 { 36 this.compilation_unit_type = compilation_unit_type; 37 } 38 39 Firebug.SourceFile.prototype = 40 { 41 getBaseLineOffset: function() 42 { 43 return 0; 44 }, 45 46 getURL: function() 47 { 48 return this.href; 49 }, 50 51 toString: function() 52 { 53 var str = (this.compilation_unit_type?this.compilation_unit_type + " " : "") + 54 this.href + " script.tags( "; 55 56 if (this.outerScript) 57 str += (this.outerScript.isValid?this.outerScript.tag:"X") +"| "; 58 59 if (this.innerScripts) 60 { 61 var numberInvalid = 0; 62 for (var p in this.innerScripts) 63 { 64 var script = this.innerScripts[p]; 65 if (script.isValid) 66 str += p+" "; 67 else 68 numberInvalid++; 69 } 70 } 71 72 str += ")" + (numberInvalid ? "(" + numberInvalid + " invalid)" : ""); 73 return str; 74 }, 75 76 forEachScript: function(callback) 77 { 78 if (this.outerScript) 79 callback(this.outerScript); 80 81 if (this.innerScripts) 82 { 83 for (var p in this.innerScripts) 84 { 85 var script = this.innerScripts[p]; 86 var rc = callback(script); 87 if (rc) 88 return rc; 89 } 90 } 91 }, 92 93 getLineRanges: function() 94 { 95 var str = ""; 96 this.forEachScript(function appendARange(script) 97 { 98 var endLineNumber = script.baseLineNumber + script.lineExtent; 99 str += " "+script.baseLineNumber +"-("+script.tag+")-"+endLineNumber; 100 }); 101 102 return str; 103 }, 104 105 getSourceLength: function() 106 { 107 return this.sourceLength; 108 }, 109 110 getLine: function(context, lineNo) 111 { 112 return context.sourceCache.getLine(this.href, lineNo); 113 }, 114 115 addToLineTable: function(script) 116 { 117 if (!script || !script.isValid) 118 { 119 if (FBTrace.DBG_ERRORS) 120 FBTrace.sysout("addToLineTable got invalid script " + 121 (script ? script.tag : "null")); 122 return; 123 } 124 125 // For outer scripts, a better algorithm would loop over PC, use pcToLine to mark the lines. 126 // This assumes there are fewer PCs in an outer script than lines, probably true for large 127 // systems. 128 // And now addToLineTable is only used for outerScripts (eval and top-level). 129 // But since we can't know the range of PC values we cannot use that approach. 130 131 if (!this.outerScriptLineMap) 132 this.outerScriptLineMap = []; 133 134 var lineCount = script.lineExtent + 1; 135 var offset = this.getBaseLineOffset(); 136 137 if (FBTrace.DBG_LINETABLE) 138 { 139 FBTrace.sysout("lib.SourceFile.addToLineTable script.tag:" + script.tag + 140 " lineExtent=" + lineCount + " baseLineNumber=" + script.baseLineNumber + 141 " offset=" + offset + " for " + this.compilation_unit_type); 142 var startTime = new Date().getTime(); 143 } 144 145 // isLineExecutable requires about 1ms per line, so it can only be called for toy programs 146 if (lineCount > 100) 147 lineCount = 100; 148 149 for (var i = 0; i <= lineCount; i++) 150 { 151 // the max is (i + script.baseLineNumber + script.lineExtent) 152 var scriptLineNo = i + script.baseLineNumber; 153 var mapLineNo = scriptLineNo - offset; 154 try 155 { 156 if (script.isLineExecutable(scriptLineNo, this.pcmap_type)) 157 this.outerScriptLineMap.push(mapLineNo); 158 } 159 catch (e) 160 { 161 // I guess not... 162 } 163 164 if (FBTrace.DBG_LINETABLE) 165 { 166 var pcFromLine = script.lineToPc(scriptLineNo, this.pcmap_type); 167 var lineFromPC = script.pcToLine(pcFromLine, this.pcmap_type); 168 169 if (this.outerScriptLineMap.indexOf(mapLineNo) != -1) 170 { 171 FBTrace.sysout("lib.SourceFile.addToLineTable ["+mapLineNo+"]="+script.tag+ 172 " for scriptLineNo="+scriptLineNo+" vs "+lineFromPC+ 173 "=lineFromPC; lineToPc="+pcFromLine+" with map="+ 174 (this.pcmap_type==PCMAP_PRETTYPRINT?"PP":"SOURCE")); 175 } 176 else 177 { 178 FBTrace.sysout("lib.SourceFile.addToLineTable not executable scriptLineNo="+ 179 scriptLineNo+" vs "+lineFromPC+"=lineFromPC; lineToPc="+pcFromLine); 180 } 181 } 182 } 183 184 if (FBTrace.DBG_LINETABLE) 185 { 186 var endTime = new Date().getTime(); 187 var delta = endTime - startTime ; 188 if (delta > 0) 189 { 190 FBTrace.sysout("SourceFile.addToLineTable processed "+lineCount+" lines in "+ 191 delta+" millisecs "+Math.round(lineCount/delta)+" lines per millisecond"); 192 } 193 194 FBTrace.sysout("SourceFile.addToLineTable: "+this.toString()); 195 } 196 }, 197 198 addToLineTableByPCLoop: function(script) 199 { 200 // This code is not called; it crashes FF3pre 201 // https://bugzilla.mozilla.org/show_bug.cgi?id=430205 202 if (!this.outerScriptLineMap) 203 this.outerScriptLineMap = {}; 204 205 var lineCount = script.lineExtent; 206 var offset = this.getBaseLineOffset(); 207 if (FBTrace.DBG_LINETABLE) 208 { 209 FBTrace.sysout("lib.SourceFile.addToLineTableByPCLoop script.tag:"+script.tag+ 210 " lineCount="+lineCount+" offset="+offset+" for "+this.compilation_unit_type); 211 var startTime = new Date().getTime(); 212 } 213 214 for (var i = 0; i <= 10*lineCount; i++) 215 { 216 var lineFromPC = script.pcToLine(i, this.pcmap_type); 217 //FBTrace.sysout("lib.SourceFile.addToLineTableByPCLoop pc="+i+" line: "+lineFromPC+"\n"); 218 this.outerScriptLineMap[lineFromPC] = script; 219 if (lineFromPC >= lineCount) 220 break; 221 } 222 223 if (FBTrace.DBG_LINETABLE) 224 { 225 FBTrace.sysout("SourceFile.addToLineTableByPCLoop: "+this.toString()+"\n"); 226 var endTime = new Date().getTime(); 227 var delta = endTime - startTime ; 228 229 if (delta > 0) 230 { 231 FBTrace.sysout("SourceFileaddToLineTableByPCLoop processed "+lineCount+ 232 " lines in "+delta+" millisecs "+Math.round(lineCount/delta)+ 233 " lines per millisecond\n"); 234 } 235 } 236 }, 237 238 hasScriptAtLineNumber: function(lineNo, mustBeExecutableLine) 239 { 240 var offset = this.getBaseLineOffset(); 241 242 if (!this.innerScripts) 243 return; // eg URLOnly 244 245 // lineNo is user-viewed number, targetLineNo is jsd number 246 var targetLineNo = lineNo + offset; 247 248 var scripts = []; 249 for (var p in this.innerScripts) 250 { 251 var script = this.innerScripts[p]; 252 if (mustBeExecutableLine && !script.isValid) 253 continue; 254 255 this.addScriptAtLineNumber(scripts, script, targetLineNo, 256 mustBeExecutableLine, offset); 257 258 if (scripts.length) 259 return true; 260 } 261 262 if (this.outerScript && !(mustBeExecutableLine && !this.outerScript.isValid)) 263 { 264 this.addScriptAtLineNumber(scripts, this.outerScript, targetLineNo, 265 mustBeExecutableLine, offset); 266 } 267 268 return (scripts.length > 0); 269 }, 270 271 getScriptsAtLineNumber: function(lineNo, mustBeExecutableLine) 272 { 273 var offset = this.getBaseLineOffset(); 274 275 if (!this.innerScripts) 276 return; // eg URLOnly 277 278 // lineNo is user-viewed number, targetLineNo is jsd number 279 var targetLineNo = lineNo + offset; 280 281 var scripts = []; 282 for (var p in this.innerScripts) 283 { 284 var script = this.innerScripts[p]; 285 if (mustBeExecutableLine && !script.isValid) 286 continue; 287 288 this.addScriptAtLineNumber(scripts, script, targetLineNo, 289 mustBeExecutableLine, offset); 290 } 291 292 if (this.outerScript && !(mustBeExecutableLine && !this.outerScript.isValid)) 293 { 294 this.addScriptAtLineNumber(scripts, this.outerScript, targetLineNo, 295 mustBeExecutableLine, offset); 296 } 297 298 if (FBTrace.DBG_LINETABLE) 299 { 300 if (scripts.length < 1) 301 { 302 FBTrace.sysout("lib.getScriptsAtLineNumber no targetScript at "+lineNo, 303 " for sourceFile:"+this.toString()); 304 return false; 305 } 306 else 307 { 308 FBTrace.sysout("getScriptsAtLineNumber offset "+offset+" for sourcefile: "+ 309 this.toString()); 310 } 311 } 312 313 return (scripts.length > 0) ? scripts : false; 314 }, 315 316 addScriptAtLineNumber: function(scripts, script, targetLineNo, mustBeExecutableLine, offset) 317 { 318 // script.isValid will be true. 319 if (FBTrace.DBG_LINETABLE) 320 FBTrace.sysout("addScriptAtLineNumber trying "+script.tag+", is "+ 321 script.baseLineNumber+" <= "+targetLineNo +" <= "+ (script.baseLineNumber + 322 script.lineExtent)+"? using offset = "+offset+"\n"); 323 324 if (targetLineNo >= script.baseLineNumber) 325 { 326 if ((script.baseLineNumber + script.lineExtent) >= targetLineNo) 327 { 328 if (mustBeExecutableLine) 329 { 330 try 331 { 332 if (!script.isLineExecutable(targetLineNo, this.pcmap_type) ) 333 { 334 if (FBTrace.DBG_LINETABLE) 335 FBTrace.sysout("getScriptsAtLineNumber tried "+script.tag+ 336 ", not executable at targetLineNo:"+targetLineNo+" pcmap:"+ 337 this.pcmap_type); 338 return; 339 } 340 } 341 catch (e) 342 { 343 // Component returned failure code: 0x80040111 (NS_ERROR_NOT_AVAILABLE) 344 // [jsdIScript.isLineExecutable] 345 return; 346 } 347 } 348 349 scripts.push(script); 350 351 if (FBTrace.DBG_LINETABLE) 352 { 353 var checkExecutable = ""; 354 if (mustBeExecutableLine) 355 checkExecutable = " isLineExecutable: "+ 356 script.isLineExecutable(targetLineNo, this.pcmap_type)+"@pc:"+ 357 script.lineToPc(targetLineNo, this.pcmap_type); 358 359 FBTrace.sysout("getScriptsAtLineNumber found "+script.tag+", isValid: "+ 360 script.isValid+" targetLineNo:"+targetLineNo+checkExecutable); 361 } 362 } 363 } 364 }, 365 366 scriptsIfLineCouldBeExecutable: function(lineNo) // script may not be valid 367 { 368 var scripts = this.getScriptsAtLineNumber(lineNo, true); 369 370 if (FBTrace.DBG_LINETABLE && !scripts) 371 FBTrace.sysout("lib.scriptsIfLineCouldBeExecutable this.outerScriptLineMap", 372 this.outerScriptLineMap); 373 374 if (!scripts && this.outerScriptLineMap && (this.outerScriptLineMap.indexOf(lineNo) != -1)) 375 return [this.outerScript]; 376 377 return scripts; 378 }, 379 380 isExecutableLine: function(lineNo) // script may not be valid 381 { 382 if (this.hasScriptAtLineNumber(lineNo, true)) 383 return true; 384 385 if (this.outerScriptLineMap && (this.outerScriptLineMap.indexOf(lineNo) != -1)) 386 return true; 387 388 return false; 389 }, 390 391 hasScript: function(script) 392 { 393 if (this.outerScript && (this.outerScript.tag == script.tag) ) 394 return true; 395 396 // XXXjjb Don't use indexOf or similar tests that rely on ===, since we are really 397 // working with wrappers around jsdIScript, not script themselves. I guess. 398 399 return (this.innerScripts && this.innerScripts.hasOwnProperty(script.tag)); 400 }, 401 402 // these objects map JSD's values to correct values 403 getScriptAnalyzer: function(script) 404 { 405 if (script && this.outerScript && (script.tag == this.outerScript.tag) ) 406 return this.getOuterScriptAnalyzer(); 407 408 return new Firebug.SourceFile.NestedScriptAnalyzer(this); 409 }, 410 411 // return.path: group/category label, return.name: item label 412 getObjectDescription: function() 413 { 414 return Url.splitURLBase(this.href); 415 }, 416 417 isEval: function() 418 { 419 return (this.compilation_unit_type == "eval-level") || 420 (this.compilation_unit_type == "newFunction"); 421 }, 422 423 isEvent: function() 424 { 425 return (this.compilation_unit_type == "event"); 426 }, 427 428 loadScriptLines: function(context) // array of lines 429 { 430 if (this.source) 431 return this.source; 432 else if (context.sourceCache) 433 return context.sourceCache.load(this.href); 434 else if (FBTrace.DBG_ERRORS) 435 FBTrace.sysout("sourceFile.loadScriptLines FAILS no sourceCache "+ 436 context.getName(), context); 437 }, 438 439 getOuterScriptAnalyzer: function() 440 { 441 FBTrace.sysout("getOuterScriptAnalyzer not overridden for "+sourceFile, this); 442 }, 443 } 444 445 Firebug.SourceFile.summarizeSourceLineArray = function(sourceLines, size) 446 { 447 var buf = ""; 448 for (var i = 0; i < sourceLines.length; i++) 449 { 450 var aLine = sourceLines[i].substr(0,240); // avoid huge lines 451 buf += aLine.replace(/\s/, " ", "g"); 452 if (buf.length > size || aLine.length > 240) 453 break; 454 } 455 return buf.substr(0, size); 456 }; 457 458 459 Firebug.SourceFile.NestedScriptAnalyzer = function(sourceFile) 460 { 461 this.sourceFile = sourceFile; 462 } 463 464 Firebug.SourceFile.NestedScriptAnalyzer.prototype = 465 { 466 // Adjust JSD line numbers based on origin of script 467 getSourceLineFromFrame: function(context, frame) 468 { 469 if (FBTrace.DBG_SOURCEFILES) 470 FBTrace.sysout("NestedScriptAnalyzer in "+this.sourceFile.compilation_unit_type+ 471 ": frame.line - this.sourceFile.getBaseLineOffset() "+ 472 frame.line +" - "+this.sourceFile.getBaseLineOffset()); 473 474 return frame.line - (this.sourceFile.getBaseLineOffset()); 475 }, 476 477 // Interpret frame to give fn(args) 478 getFunctionDescription: function(script, context, frame) 479 { 480 if (frame) 481 { 482 var name = frame.name; 483 var args = StackFrame.getFunctionArgValues(frame); 484 } 485 else 486 { 487 var name = script.functionName; 488 var args = []; 489 } 490 491 if (name == "anonymous") 492 { 493 var name = StackFrame.guessFunctionName(this.sourceFile.href, 494 this.getBaseLineNumberByScript(script), context); 495 } 496 497 return {name: name, args: args}; 498 }, 499 500 // link to source for this script. 501 getSourceLinkForScript: function (script) 502 { 503 var line = this.getBaseLineNumberByScript(script); 504 return new SourceLink.SourceLink(this.sourceFile.href, line, "js"); 505 }, 506 507 getBaseLineNumberByScript: function(script) 508 { 509 return script.baseLineNumber - (this.sourceFile.getBaseLineOffset() - 1); 510 } 511 } 512 513 Firebug.SourceFile.addScriptsToSourceFile = function(sourceFile, outerScript, innerScripts) 514 { 515 // Attach the innerScripts for use later 516 if (!sourceFile.innerScripts) 517 sourceFile.innerScripts = {}; 518 519 var total = 0; 520 while (innerScripts.hasMoreElements()) 521 { 522 var script = innerScripts.getNext(); 523 if (!script || ((script instanceof Ci.jsdIScript) && !script.tag)) 524 { 525 if (FBTrace.DBG_SOURCEFILES) 526 FBTrace.sysout("addScriptsToSourceFile innerScripts.getNext FAILS "+ 527 sourceFile, script); 528 continue; 529 } 530 531 sourceFile.innerScripts[script.tag] = script; 532 533 if (FBTrace.DBG_SOURCEFILES) 534 total++; 535 } 536 537 if (FBTrace.DBG_SOURCEFILES) 538 FBTrace.sysout("addScriptsToSourceFile "+ total +" scripts, sourcefile="+ 539 sourceFile.toString(), sourceFile); 540 } 541 542 // ********************************************************************************************* // 543 544 Firebug.EvalLevelSourceFile = function(url, script, eval_expr, source, mapType, 545 innerScriptEnumerator) 546 { 547 this.href = url.href; 548 this.hrefKind = url.kind; 549 this.outerScript = script; 550 this.containingURL = script.fileName; 551 this.evalExpression = eval_expr; 552 this.sourceLength = source.length; 553 this.source = source; 554 this.pcmap_type = mapType; 555 Firebug.SourceFile.addScriptsToSourceFile(this, script, innerScriptEnumerator); 556 }; 557 558 Firebug.EvalLevelSourceFile.prototype = 559 Obj.descend(new Firebug.SourceFile("eval-level"), // shared prototype 560 { 561 getLine: function(context, lineNo) 562 { 563 return this.source[lineNo - 1]; 564 }, 565 566 getBaseLineOffset: function() 567 { 568 // baseLineNumber always valid even after jsdIscript isValid false 569 return this.outerScript.baseLineNumber - 1; 570 }, 571 572 getObjectDescription: function() 573 { 574 if (this.hrefKind == "source" || this.hrefKind == "data") 575 return Url.splitURLBase(this.href); 576 577 if (!this.summary) 578 { 579 if (this.evalExpression) 580 this.summary = Firebug.SourceFile.summarizeSourceLineArray( 581 this.evalExpression.substr(0, 240), 120); 582 583 if (!this.summary) 584 this.summary = ""; 585 586 if (this.summary.length < 120) 587 this.summary = "eval("+this.summary + "...)=" + 588 Firebug.SourceFile.summarizeSourceLineArray(this.source, 589 120 - this.summary.length); 590 } 591 592 var containingFileDescription = Url.splitURLBase(this.containingURL); 593 594 if (FBTrace.DBG_SOURCEFILES) 595 FBTrace.sysout("EvalLevelSourceFile this.evalExpression.substr(0, 240):"+ 596 (this.evalExpression?this.evalExpression.substr(0, 240):"null")+" summary", 597 this.summary); 598 599 return { 600 path: containingFileDescription.path, 601 name: containingFileDescription.name+"/eval: "+this.summary 602 }; 603 }, 604 605 getOuterScriptAnalyzer: function() 606 { 607 return new Firebug.EvalLevelSourceFile.OuterScriptAnalyzer(this); 608 }, 609 }); 610 611 Firebug.EvalLevelSourceFile.OuterScriptAnalyzer = function(sourceFile) 612 { 613 this.sourceFile = sourceFile; 614 } 615 616 Firebug.EvalLevelSourceFile.OuterScriptAnalyzer.prototype = 617 { 618 // Adjust JSD line numbers based on origin of script 619 getSourceLineFromFrame: function(context, frame) 620 { 621 return frame.line - this.sourceFile.getBaseLineOffset(); 622 }, 623 624 // Interpret frame to give fn(args) 625 getFunctionDescription: function(script, context, frame) 626 { 627 return {name: "eval", args: [this.evalExpression] }; 628 }, 629 630 getSourceLinkForScript: function (script) 631 { 632 return new SourceLink.SourceLink(this.sourceFile.href, 1, "js"); 633 } 634 } 635 636 // ********************************************************************************************* // 637 638 Firebug.EventSourceFile = function(url, script, title, source, innerScriptEnumerator) 639 { 640 this.href = url; 641 this.outerScript = script; 642 this.containingURL = script.fileName; 643 this.title = title; 644 this.source = source; // points to the sourceCache lines 645 this.sourceLength = source.length; 646 this.pcmap_type = PCMAP_PRETTYPRINT; 647 648 Firebug.SourceFile.addScriptsToSourceFile(this, script, innerScriptEnumerator); 649 }; 650 651 Firebug.EventSourceFile.prototype = Obj.descend(new Firebug.SourceFile("event"), 652 { 653 getLine: function(context, lineNo) 654 { 655 return this.source[lineNo - 1]; 656 }, 657 658 getBaseLineOffset: function() 659 { 660 return 1; 661 }, 662 663 getObjectDescription: function() 664 { 665 if (!this.summary) 666 this.summary = Firebug.SourceFile.summarizeSourceLineArray(this.source, 120); 667 668 var containingFileDescription = Url.splitURLBase(this.containingURL); 669 670 return { 671 path: containingFileDescription.path, 672 name: containingFileDescription.name+"/event: "+this.summary 673 }; 674 }, 675 676 getOuterScriptAnalyzer: function() 677 { 678 return new Firebug.EventSourceFile.OuterScriptAnalyzer(this); 679 }, 680 }); 681 682 Firebug.EventSourceFile.OuterScriptAnalyzer = function(sourceFile) 683 { 684 this.sourceFile = sourceFile; 685 } 686 687 Firebug.EventSourceFile.OuterScriptAnalyzer.prototype = 688 { 689 // Adjust JSD line numbers based on origin of script 690 getSourceLineFromFrame: function(context, frame) 691 { 692 var script = frame.script; 693 var line = script.pcToLine(frame.pc, PCMAP_PRETTYPRINT); 694 return line - 1; 695 }, 696 697 // Interpret frame to give fn(args) 698 getFunctionDescription: function(script, context, frame) 699 { 700 var name = script.functionName; 701 if (!name) 702 name = "jsdbug_NoScriptFunctionName"; 703 704 if (frame) 705 { 706 var args = StackFrame.getFunctionArgValues(frame); 707 } 708 else 709 { 710 var args = [] 711 } 712 return {name: name, args: args}; 713 }, 714 715 getSourceLinkForScript: function (script) 716 { 717 return new SourceLink.SourceLink(this.sourceFile.href, 1, "js"); 718 } 719 } 720 721 // ********************************************************************************************* // 722 723 Firebug.SourceFile.CommonBase = 724 { 725 getSourceLength: function() 726 { 727 if (!this.sourceLength) 728 this.sourceLength = this.context.sourceCache.load(this.href).length; 729 return this.sourceLength; 730 }, 731 732 getOuterScriptAnalyzer: function() 733 { 734 return Firebug.TopLevelSourceFile.OuterScriptAnalyzer; 735 }, 736 } 737 738 // ********************************************************************************************* // 739 740 Firebug.TopLevelSourceFile = function(url, outerScript, sourceLength, innerScriptEnumerator) 741 { 742 this.href = url; 743 this.outerScript = outerScript; // Beware may not be valid after we return!! 744 this.sourceLength = sourceLength; 745 this.pcmap_type = PCMAP_SOURCETEXT; 746 747 Firebug.SourceFile.addScriptsToSourceFile(this, outerScript, innerScriptEnumerator); 748 } 749 750 Firebug.TopLevelSourceFile.prototype = Obj.descend(new Firebug.SourceFile("top-level"), 751 Firebug.SourceFile.CommonBase); 752 753 Firebug.TopLevelSourceFile.OuterScriptAnalyzer = 754 { 755 // Adjust JSD line numbers based on origin of script 756 getSourceLineFromFrame: function(context, frame) 757 { 758 return frame.line; 759 }, 760 // Interpret frame to give fn(args) 761 getFunctionDescription: function(script, context, frame) 762 { 763 // this is more useful that just "top_level" 764 var file_name = Url.getFileName(Url.normalizeURL(script.fileName)); 765 file_name = file_name ? file_name: "__top_level__"; 766 return {name: file_name, args: []}; 767 }, 768 getSourceLinkForScript: function (script) 769 { 770 return SourceLink.SourceLink(Url.normalizeURL(script.fileName), 771 script.baseLineNumber, "js") 772 } 773 } 774 775 // ********************************************************************************************* // 776 777 // we don't have the outer script and we delay source load. 778 Firebug.EnumeratedSourceFile = function(url) 779 { 780 // may not be outerScript file name, eg this could be an enumerated eval 781 this.href = new String(url); 782 this.innerScripts = {}; 783 this.pcmap_type = PCMAP_SOURCETEXT; 784 } 785 786 Firebug.EnumeratedSourceFile.prototype = Obj.descend( 787 new Firebug.SourceFile("enumerated"), 788 Firebug.SourceFile.CommonBase); 789 790 // ********************************************************************************************* // 791 792 Firebug.NoScriptSourceFile = function(context, url) // Somehow we got the Url, but not the script 793 { 794 this.href = url; // we know this much 795 this.innerScripts = {}; 796 } 797 798 Firebug.NoScriptSourceFile.prototype = Obj.descend( 799 new Firebug.SourceFile("URLOnly"), 800 Firebug.SourceFile.CommonBase); 801 802 // ********************************************************************************************* // 803 // javascript in a .xul or .xml file, no outerScript 804 805 Firebug.XULSourceFile = function(url, outerScript, innerScriptEnumerator) 806 { 807 this.href = url; 808 this.pcmap_type = PCMAP_SOURCETEXT; 809 this.outerScript = outerScript; // Beware may not be valid after we return!! 810 811 Firebug.SourceFile.addScriptsToSourceFile(this, outerScript, innerScriptEnumerator); 812 } 813 814 Firebug.XULSourceFile.prototype = Obj.descend( 815 new Firebug.SourceFile("xul"), 816 Firebug.SourceFile.CommonBase); 817 818 // ********************************************************************************************* // 819 820 // element.appendChild(scriptTag) 821 Firebug.ScriptTagAppendSourceFile = function(url, outerScript, sourceLength, innerScriptEnumerator) 822 { 823 this.href = url; 824 this.outerScript = outerScript; // Beware may not be valid after we return!! 825 this.sourceLength = sourceLength; 826 this.pcmap_type = PCMAP_SOURCETEXT; 827 828 Firebug.SourceFile.addScriptsToSourceFile(this, outerScript, innerScriptEnumerator); 829 } 830 831 Firebug.ScriptTagAppendSourceFile.prototype = Obj.descend( 832 new Firebug.SourceFile("scriptTagAppend"), 833 Firebug.SourceFile.CommonBase); 834 835 // ********************************************************************************************* // 836 837 // we don't have the outer script and we delay source load 838 Firebug.ScriptTagSourceFile = function(context, url, scriptTagNumber) 839 { 840 this.context = context; 841 this.href = url; // we know this is not an eval 842 this.scriptTagNumber = scriptTagNumber; 843 this.innerScripts = {}; 844 this.pcmap_type = PCMAP_SOURCETEXT; 845 } 846 847 Firebug.ScriptTagSourceFile.prototype = Obj.descend( 848 new Firebug.SourceFile("scriptTag"), 849 Firebug.SourceFile.CommonBase); 850 851 // ********************************************************************************************* // 852 853 Firebug.SourceFile.getSourceFileByScript = function(context, script) 854 { 855 if (!context.sourceFileMap) 856 return null; 857 858 // Other algorithms are possible: 859 // We could store an index, context.sourceFileByTag 860 // Or we could build a tree keyed by url, with SpiderMonkey script.fileNames at the top 861 // and our urls below 862 863 // we won't be lucky for file:/ urls, no normalizeURL applied 864 var lucky = context.sourceFileMap[script.fileName]; 865 if (FBTrace.DBG_SOURCEFILES && lucky) 866 FBTrace.sysout("getSourceFileByScript trying to be lucky for "+ 867 script.tag + " in "+lucky, script); 868 869 if (lucky && lucky.hasScript(script)) 870 return lucky; 871 872 if (FBTrace.DBG_SOURCEFILES) 873 FBTrace.sysout("getSourceFileByScript looking for "+script.tag+"@"+script.fileName+" in "+ 874 context.getName()+": ", context.sourceFileMap); 875 876 for (var url in context.sourceFileMap) 877 { 878 var sourceFile = context.sourceFileMap[url]; 879 if (sourceFile.hasScript(script)) 880 return sourceFile; 881 } 882 }; 883 884 Firebug.SourceFile.getScriptAnalyzer = function(context, script) 885 { 886 var sourceFile = Firebug.SourceFile.getSourceFileByScript(context, script); 887 if (FBTrace.DBG_STACK) 888 FBTrace.sysout("getScriptAnalyzer "+ (sourceFile?"finds sourceFile: ": 889 "FAILS to find sourceFile"), sourceFile); 890 891 if (sourceFile) 892 { 893 var analyzer = sourceFile.getScriptAnalyzer(script); 894 if (FBTrace.DBG_STACK) 895 FBTrace.sysout("getScriptAnalyzer finds analyzer: ", analyzer); 896 897 return analyzer; 898 } 899 900 return undefined; 901 }; 902 903 Firebug.SourceFile.getSourceFileAndLineByScript= function(context, script, frame) 904 { 905 var sourceFile = Firebug.SourceFile.getSourceFileByScript(context, script); 906 if (sourceFile) 907 { 908 if (sourceFile.pcmap_type) 909 var line = script.pcToLine(1, sourceFile.pcmap_type); 910 else 911 var line = 1; 912 913 return { sourceFile: sourceFile, lineNo: line }; 914 } 915 }; 916 917 Firebug.SourceFile.guessEnclosingFunctionName = function(url, line, context) 918 { 919 var sourceFile = context.sourceFileMap[url]; 920 if (sourceFile) 921 { 922 var scripts = sourceFile.getScriptsAtLineNumber(line); 923 if (scripts) 924 { 925 // TODO try others? 926 var script = scripts[0]; 927 var analyzer = sourceFile.getScriptAnalyzer(script); 928 929 // Some analyzers don't implement this method. 930 if (analyzer.getBaseLineNumberByScript) 931 line = analyzer.getBaseLineNumberByScript(script); 932 } 933 } 934 935 return StackFrame.guessFunctionName(url, line-1, context); 936 }; 937 938 // ********************************************************************************************* // 939 // Functions 940 941 Firebug.SourceFile.findScripts = function(context, url, line) 942 { 943 var sourceFile = context.sourceFileMap[url]; 944 if (sourceFile) 945 { 946 var scripts = sourceFile.scriptsIfLineCouldBeExecutable(line); 947 } 948 else 949 { 950 if (FBTrace.DBG_STACK) 951 FBTrace.sysout("lib.findScript, no sourceFile in context for url=", url); 952 } 953 return scripts; 954 }; 955 956 Firebug.SourceFile.findScriptForFunctionInContext = function(context, fn) 957 { 958 var found = null; 959 960 if (!fn || typeof(fn) !== 'function') 961 return found; 962 963 try 964 { 965 var wrapped = jsd.wrapValue(fn); 966 found = wrapped.script; 967 if (!found) 968 found = wrapped.jsParent.script; 969 970 if (!found && FBTrace.DBG_ERRORS) 971 { 972 FBTrace.sysout("findScriptForFunctionInContext ", 973 {fn: fn, wrapValue: jsd.wrapValue(fn), found: found}); 974 } 975 } 976 catch (err) 977 { 978 if (FBTrace.DBG_ERRORS) 979 FBTrace.sysout("sourceFile.findScriptForFunctionInContext; EXCEPTION " + err, err); 980 } 981 982 if (FBTrace.DBG_FUNCTION_NAMES) 983 FBTrace.sysout("findScriptForFunctionInContext found " + (found ? found.tag : "none")); 984 985 return found; 986 } 987 988 Firebug.SourceFile.findSourceForFunction = function(fn, context) 989 { 990 var script = Firebug.SourceFile.findScriptForFunctionInContext(context, fn); 991 return script ? Firebug.SourceFile.getSourceLinkForScript(script, context) : null; 992 }; 993 994 Firebug.SourceFile.getSourceLinkForScript = function(script, context) 995 { 996 var sourceFile = Firebug.SourceFile.getSourceFileByScript(context, script); 997 if (sourceFile) 998 { 999 var scriptAnalyzer = sourceFile.getScriptAnalyzer(script); 1000 if (scriptAnalyzer) 1001 return scriptAnalyzer.getSourceLinkForScript(script); 1002 else 1003 { 1004 // no-op for detrace 1005 if (FBTrace.DBG_ERRORS) 1006 FBTrace.sysout("getSourceLineForScript FAILS no scriptAnalyser for sourceFile "+ 1007 sourceFile); 1008 } 1009 } 1010 }; 1011 1012 // ********************************************************************************************* // 1013 // Source Files 1014 1015 Firebug.SourceFile.getSourceFileByHref = function(url, context) 1016 { 1017 return context.sourceFileMap[url]; 1018 }; 1019 1020 Firebug.SourceFile.sourceURLsAsArray = function(context) 1021 { 1022 var urls = []; 1023 var sourceFileMap = context.sourceFileMap; 1024 for (var url in sourceFileMap) 1025 urls.push(url); 1026 1027 if (FBTrace.DBG_SOURCEFILES) 1028 FBTrace.sysout("sourceURLsAsArray urls="+urls.length+" in context "+context.getName()); 1029 1030 return urls; 1031 }; 1032 1033 // deprecated, use mapAsArray 1034 Firebug.SourceFile.sourceFilesAsArray = function(sourceFileMap) 1035 { 1036 var sourceFiles = []; 1037 for (var url in sourceFileMap) 1038 sourceFiles.push(sourceFileMap[url]); 1039 1040 if (FBTrace.DBG_SOURCEFILES) 1041 FBTrace.sysout("sourceFilesAsArray sourcefiles="+sourceFiles.length, sourceFiles); 1042 1043 return sourceFiles; 1044 }; 1045 1046 Firebug.SourceFile.mapAsArray = function(map) 1047 { 1048 var entries = []; 1049 for (var url in map) 1050 entries.push(map[url]); 1051 1052 return entries; 1053 }; 1054 1055 // ********************************************************************************************* // 1056 1057 return Firebug.SourceFile; 1058 1059 // ********************************************************************************************* // 1060 }); 1061