1 /* See license.txt for terms of usage */
  2 
  3 FBL.ns(function() { with (FBL)
  4 {
  5     const Cc = Components.classes;
  6     const Ci = Components.interfaces;
  7 
  8     const PCMAP_SOURCETEXT = Ci.jsdIScript.PCMAP_SOURCETEXT;
  9     const PCMAP_PRETTYPRINT = Ci.jsdIScript.PCMAP_PRETTYPRINT;
 10 
 11 
 12 //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 13 /*
 14  * SourceFile one for every compilation unit.
 15  * Unique URL for each. (href)
 16  * Unique outerScript, the statements outside of any function defintion
 17  * sourceCache keyed by href has source for this compilation unit
 18  * Stored by href in context.
 19  * Contains array of jsdIScript for functions (scripts) defined in this unit
 20  * May contain line table (for sources viewed)
 21  */
 22 
 23 Firebug.SourceFile = function (compilation_unit_type)
 24 {
 25     this.compilation_unit_type = compilation_unit_type; /*@explore*/
 26 }
 27 
 28 Firebug.SourceFile.prototype =
 29 {
 30     getBaseLineOffset: function()
 31     {
 32         return 0;
 33     },
 34 
 35     toString: function()
 36     {
 37         var str = (this.compilation_unit_type?this.compilation_unit_type+" ":"")+this.href+" script.tags( ";
 38         if (this.outerScript)
 39             str += (this.outerScript.isValid?this.outerScript.tag:"X") +"| ";
 40         if (this.innerScripts)
 41         {
 42             var numberInvalid = 0;
 43             for (var p in this.innerScripts)
 44             {
 45                 var script = this.innerScripts[p];
 46                 if (script.isValid)
 47                     str += p+" ";
 48                 else
 49                     numberInvalid++;
 50             }
 51         }
 52         str += ")"+(numberInvalid ? "("+numberInvalid+" invalid)" : "");
 53         return str;
 54     },
 55 
 56     forEachScript: function(callback)
 57      {
 58          if (this.outerScript)
 59              callback(this.outerScript);
 60          if (this.innerScripts)
 61          {
 62              for (var p in this.innerScripts)
 63              {
 64                  var script = this.innerScripts[p];
 65                  var rc = callback(script);
 66                  if (rc)
 67                      return rc;
 68              }
 69          }
 70      },
 71 
 72      getLineRanges: function()
 73      {
 74          var str = "";
 75          this.forEachScript(function appendARange(script)
 76          {
 77              var endLineNumber = script.baseLineNumber + script.lineExtent;
 78              str += " "+script.baseLineNumber +"-("+script.tag+")-"+endLineNumber;
 79          });
 80          return str;
 81      },
 82 
 83      getSourceLength: function()
 84      {
 85              return this.sourceLength;
 86      },
 87 
 88      getLine: function(context, lineNo)
 89      {
 90          return context.sourceCache.getLine(this.href, lineNo);
 91      },
 92 
 93      addToLineTable: function(script)
 94      {
 95          if (!script || !script.isValid)
 96          {
 97              if (FBTrace.DBG_ERRORS)
 98                  FBTrace.sysout("addToLineTable got invalid script "+(script?script.tag:"null")+"\n");
 99              return;
100          }
101 
102          // For outer scripts, a better algorithm would loop over PC, use pcToLine to mark the lines.
103          // This assumes there are fewer PCs in an outer script than lines, probably true for large systems.
104          // And now addToLineTable is only used for outerScripts (eval and top-level).
105          // But since we can't know the range of PC values we cannot use that approach.
106 
107          if (!this.outerScriptLineMap)
108              this.outerScriptLineMap = [];
109 
110          var lineCount = script.lineExtent + 1;
111          var offset = this.getBaseLineOffset();
112          if (FBTrace.DBG_LINETABLE)
113          {
114              FBTrace.sysout("lib.SourceFile.addToLineTable script.tag:"+script.tag+" lineExtent="+lineCount+" baseLineNumber="+script.baseLineNumber+" offset="+offset+" for "+this.compilation_unit_type+"\n");
115              var startTime = new Date().getTime();
116          }
117          if (lineCount > 100)
118              lineCount = 100; // isLineExecutable requires about 1ms per line, so it can only be called for toy programs
119 
120          for (var i = 0; i <= lineCount; i++)
121          {
122              var scriptLineNo = i + script.baseLineNumber;  // the max is (i + script.baseLineNumber + script.lineExtent)
123              var mapLineNo = scriptLineNo - offset;
124              try
125              {
126                  if (script.isLineExecutable(scriptLineNo, this.pcmap_type))
127                      this.outerScriptLineMap.push(mapLineNo);
128              }
129              catch (e)
130              {
131                  // I guess not...
132              }
133 
134              if (FBTrace.DBG_LINETABLE)
135              {
136                  var pcFromLine = script.lineToPc(scriptLineNo, this.pcmap_type);
137                  var lineFromPC = script.pcToLine(pcFromLine, this.pcmap_type);
138                  if (this.outerScriptLineMap.indexOf(mapLineNo) != -1)
139                      FBTrace.sysout("lib.SourceFile.addToLineTable ["+mapLineNo+"]="+script.tag+" for scriptLineNo="+scriptLineNo+" vs "+lineFromPC+"=lineFromPC; lineToPc="+pcFromLine+" with map="+(this.pcmap_type==PCMAP_PRETTYPRINT?"PP":"SOURCE")+"\n");
140                  else
141                      FBTrace.sysout("lib.SourceFile.addToLineTable not executable scriptLineNo="+scriptLineNo+" vs "+lineFromPC+"=lineFromPC; lineToPc="+pcFromLine+"\n");
142              }
143          }
144          if (FBTrace.DBG_LINETABLE)
145          {
146              var endTime = new Date().getTime();
147              var delta = endTime - startTime ;
148              if (delta > 0) FBTrace.sysout("SourceFile.addToLineTable processed "+lineCount+" lines in "+delta+" millisecs "+Math.round(lineCount/delta)+" lines per millisecond\n");
149              FBTrace.sysout("SourceFile.addToLineTable: "+this.toString()+"\n");
150          }
151      },
152 
153      addToLineTableByPCLoop: function(script)
154      {
155          // This code is not called; it crashes FF3pre https://bugzilla.mozilla.org/show_bug.cgi?id=430205
156          if (!this.outerScriptLineMap)
157              this.outerScriptLineMap = {};
158 
159          var lineCount = script.lineExtent;
160          var offset = this.getBaseLineOffset();
161          if (FBTrace.DBG_LINETABLE)
162          {
163              FBTrace.sysout("lib.SourceFile.addToLineTableByPCLoop script.tag:"+script.tag+" lineCount="+lineCount+" offset="+offset+" for "+this.compilation_unit_type+"\n");
164              var startTime = new Date().getTime();
165          }
166 
167          for (var i = 0; i <= 10*lineCount; i++)
168          {
169              var lineFromPC = script.pcToLine(i, this.pcmap_type);
170              //FBTrace.sysout("lib.SourceFile.addToLineTableByPCLoop pc="+i+" line: "+lineFromPC+"\n");
171              this.outerScriptLineMap[lineFromPC] = script;
172              if (lineFromPC >= lineCount) break;
173          }
174 
175          if (FBTrace.DBG_LINETABLE)
176          {
177              FBTrace.sysout("SourceFile.addToLineTableByPCLoop: "+this.toString()+"\n");
178              var endTime = new Date().getTime();
179              var delta = endTime - startTime ;
180              if (delta > 0) FBTrace.sysout("SourceFileaddToLineTableByPCLoop processed "+lineCount+" lines in "+delta+" millisecs "+Math.round(lineCount/delta)+" lines per millisecond\n");
181          }
182      },
183 
184      getScriptsAtLineNumber: function(lineNo, mustBeExecutableLine)
185      {
186          var offset = this.getBaseLineOffset();
187 
188          if (!this.innerScripts)
189              return; // eg URLOnly
190 
191          var targetLineNo = lineNo + offset;  // lineNo is user-viewed number, targetLineNo is jsd number
192 
193          var scripts = [];
194          for (var p in this.innerScripts)
195          {
196              var script = this.innerScripts[p];
197              if (mustBeExecutableLine && !script.isValid) continue;
198              this.addScriptAtLineNumber(scripts, script, targetLineNo, mustBeExecutableLine, offset);
199          }
200 
201          if (this.outerScript && !(mustBeExecutableLine && !this.outerScript.isValid) )
202              this.addScriptAtLineNumber(scripts, this.outerScript, targetLineNo, mustBeExecutableLine, offset);
203 
204          if (FBTrace.DBG_LINETABLE)
205          {
206              if (scripts.length < 1)
207              {
208                  FBTrace.sysout("lib.getScriptsAtLineNumber no targetScript at "+lineNo," for sourceFile:"+this.toString());
209                  return false;
210              }
211              else
212              {
213                  FBTrace.sysout("getScriptsAtLineNumber offset "+offset+" for sourcefile: "+this.toString()+"\n");
214              }
215          }
216 
217          return (scripts.length > 0) ? scripts : false;
218      },
219 
220      addScriptAtLineNumber: function(scripts, script, targetLineNo, mustBeExecutableLine, offset)
221      {
222          // script.isValid will be true.
223          if (FBTrace.DBG_LINETABLE)
224              FBTrace.sysout("addScriptAtLineNumber trying "+script.tag+", is "+script.baseLineNumber+" <= "+targetLineNo +" <= "+ (script.baseLineNumber + script.lineExtent)+"? using offset = "+offset+"\n");
225 
226          if (targetLineNo >= script.baseLineNumber)
227          {
228              if ( (script.baseLineNumber + script.lineExtent) >= targetLineNo)
229              {
230                  if (mustBeExecutableLine)
231                  {
232                      try
233                      {
234                          if (!script.isLineExecutable(targetLineNo, this.pcmap_type) )
235                          {
236                              if (FBTrace.DBG_LINETABLE)
237                                  FBTrace.sysout("getScriptsAtLineNumber tried "+script.tag+", not executable at targetLineNo:"+targetLineNo+" pcmap:"+this.pcmap_type+"\n");
238                              return;
239                          }
240                      }
241                      catch (e)
242                      {
243                          // Component returned failure code: 0x80040111 (NS_ERROR_NOT_AVAILABLE) [jsdIScript.isLineExecutable]
244                          return;
245                      }
246                  }
247                  scripts.push(script);
248                  if (FBTrace.DBG_LINETABLE)
249                  {
250                      var checkExecutable = "";
251                      if (mustBeExecutableLine)
252                          var checkExecutable = " isLineExecutable: "+script.isLineExecutable(targetLineNo, this.pcmap_type)+"@pc:"+script.lineToPc(targetLineNo, this.pcmap_type);
253                      FBTrace.sysout("getScriptsAtLineNumber found "+script.tag+", isValid: "+script.isValid+" targetLineNo:"+targetLineNo+checkExecutable+"\n");
254                  }
255              }
256          }
257      },
258 
259      scriptsIfLineCouldBeExecutable: function(lineNo)  // script may not be valid
260      {
261          var scripts = this.getScriptsAtLineNumber(lineNo, true);
262          if (FBTrace.DBG_LINETABLE && !scripts) FBTrace.sysout("lib.scriptsIfLineCouldBeExecutable this.outerScriptLineMap", this.outerScriptLineMap);
263          if (!scripts && this.outerScriptLineMap && (this.outerScriptLineMap.indexOf(lineNo) != -1) )
264              return [this.outerScript];
265          return scripts;
266      },
267 
268      hasScript: function(script)
269      {
270          if (this.outerScript && (this.outerScript.tag == script.tag) )
271              return true;
272          // XXXjjb Don't use indexOf or similar tests that rely on ===, since we are really working with
273          // wrappers around jsdIScript, not script themselves.  I guess.
274 
275         return ( this.innerScripts && this.innerScripts.hasOwnProperty(script.tag) );
276      },
277 
278      // these objects map JSD's values to correct values
279      getScriptAnalyzer: function(script)
280      {
281          if (this.outerScript && (script.tag == this.outerScript.tag) )
282              return this.getOuterScriptAnalyzer();
283          return new Firebug.SourceFile.NestedScriptAnalyzer(this);
284      },
285 
286      // return.path: group/category label, return.name: item label
287      getObjectDescription: function()
288      {
289          return FBL.splitURLBase(this.href);
290      },
291 
292      isEval: function()
293      {
294          return (this.compilation_unit_type == "eval-level") || (this.compilation_unit_type == "newFunction");
295      },
296 
297      isEvent: function()
298      {
299          return (this.compilation_unit_type == "event");
300      },
301 
302      loadScriptLines: function(context)  // array of lines
303      {
304          if (this.source)
305              return this.source;
306          else
307              return context.sourceCache.load(this.href);
308      },
309 
310      getOuterScriptAnalyzer: function()
311      {
312          FBTrace.sysout("getOuterScriptAnalyzer not overridden for "+sourceFile, this);
313      },
314 
315 }
316 
317 Firebug.SourceFile.summarizeSourceLineArray = function(sourceLines, size)
318 {
319     var buf  = "";
320     for (var i = 0; i < sourceLines.length; i++)
321      {
322          var aLine = sourceLines[i].substr(0,240);  // avoid huge lines
323          buf += aLine.replace(/\s/, " ", "g");
324          if (buf.length > size || aLine.length > 240)
325              break;
326      }
327      return buf.substr(0, size);
328 };
329 
330 
331 Firebug.SourceFile.NestedScriptAnalyzer = function(sourceFile)
332 {
333     this.sourceFile = sourceFile;
334 }
335 
336 Firebug.SourceFile.NestedScriptAnalyzer.prototype =
337 {
338     // Adjust JSD line numbers based on origin of script
339     getSourceLineFromFrame: function(context, frame)
340     {
341         if (FBTrace.DBG_SOURCEFILES) FBTrace.sysout("NestedScriptAnalyzer in "+this.sourceFile.compilation_unit_type+": frame.line  - this.sourceFile.getBaseLineOffset()",
342              frame.line +" - "+this.sourceFile.getBaseLineOffset());
343 
344         return frame.line - (this.sourceFile.getBaseLineOffset());
345     },
346     // Interpret frame to give fn(args)
347     getFunctionDescription: function(script, context, frame)
348     {
349         if (frame)
350         {
351             var name = frame.name;
352             var args = FBL.getFunctionArgValues(frame);
353         }
354         else
355         {
356             var name = script.functionName;
357             var args = [];
358         }
359 
360         if (name ==  "anonymous")
361         {
362             name = FBL.guessFunctionName(this.sourceFile.href, this.getBaseLineNumberByScript(script), context);
363         }
364 
365         return {name: name, args: args};
366     },
367 
368     // link to source for this script.
369     getSourceLinkForScript: function (script)
370     {
371         var line = this.getBaseLineNumberByScript(script);
372         return new FBL.SourceLink(this.sourceFile.href, line, "js");
373     },
374 
375     getBaseLineNumberByScript: function(script)
376     {
377         return script.baseLineNumber - (this.sourceFile.getBaseLineOffset() - 1);
378     }
379 }
380 
381 Firebug.SourceFile.addScriptsToSourceFile = function(sourceFile, outerScript, innerScripts)
382 {
383     // Attach the innerScripts for use later
384     if (!sourceFile.innerScripts)
385          sourceFile.innerScripts = {};
386 
387      var total = 0;
388      while (innerScripts.hasMoreElements())
389      {
390          var script = innerScripts.getNext();
391          if (!script || ( (script instanceof Ci.jsdIScript) && !script.tag) )
392          {
393              if (FBTrace.DBG_SOURCEFILES)
394                  FBTrace.sysout("addScriptsToSourceFile innerScripts.getNext FAILS "+sourceFile, script);
395              continue;
396          }
397          sourceFile.innerScripts[script.tag] = script;
398          if (FBTrace.DBG_SOURCEFILES)
399              total++;
400      }
401      if (FBTrace.DBG_SOURCEFILES)
402          FBTrace.sysout("addScriptsToSourceFile "+ total +" scripts, sourcefile="+sourceFile.toString(), sourceFile);
403 }
404 
405 //------------
406 Firebug.EvalLevelSourceFile = function(url, script, eval_expr, source, mapType, innerScriptEnumerator) // ctor
407 {
408     this.href = url.href;
409     this.hrefKind = url.kind;
410      this.outerScript = script;
411      this.containingURL = script.fileName;
412      this.evalExpression = eval_expr;
413      this.sourceLength = source.length;
414      this.source = source;
415      this.pcmap_type = mapType;
416      Firebug.SourceFile.addScriptsToSourceFile(this, script, innerScriptEnumerator);
417 };
418 
419 Firebug.EvalLevelSourceFile.prototype =
420     descend(new Firebug.SourceFile("eval-level"), // shared prototype
421 {
422     getLine: function(context, lineNo)
423     {
424         return this.source[lineNo - 1];
425     },
426 
427     getBaseLineOffset: function()
428     {
429         return this.outerScript.baseLineNumber - 1; // baseLineNumber always valid even after jsdIscript isValid false
430     },
431 
432     getObjectDescription: function()
433     {
434          if (this.hrefKind == "source" || this.hrefKind == "data")
435              return FBL.splitURLBase(this.href);
436 
437          if (!this.summary)
438          {
439              if (this.evalExpression)
440                  this.summary = Firebug.SourceFile.summarizeSourceLineArray(this.evalExpression.substr(0, 240), 120);
441              if (!this.summary)
442                  this.summary = "";
443              if (this.summary.length < 120)
444                  this.summary = "eval("+this.summary + "...)=" + Firebug.SourceFile.summarizeSourceLineArray(this.source, 120 - this.summary.length);
445          }
446          var containingFileDescription = FBL.splitURLBase(this.containingURL);
447          if (FBTrace.DBG_SOURCEFILES)
448              FBTrace.sysout("EvalLevelSourceFile this.evalExpression.substr(0, 240):"+(this.evalExpression?this.evalExpression.substr(0, 240):"null")+" summary", this.summary);
449          return {path: containingFileDescription.path, name: containingFileDescription.name+"/eval: "+this.summary };
450     },
451 
452     getOuterScriptAnalyzer: function()
453     {
454         return new Firebug.EvalLevelSourceFile.OuterScriptAnalyzer(this);
455     },
456 
457 });
458 
459 Firebug.EvalLevelSourceFile.OuterScriptAnalyzer = function(sourceFile)
460 {
461     this.sourceFile = sourceFile;
462 }
463 
464 Firebug.EvalLevelSourceFile.OuterScriptAnalyzer.prototype =
465 {
466     // Adjust JSD line numbers based on origin of script
467     getSourceLineFromFrame: function(context, frame)
468     {
469         return frame.line - this.sourceFile.getBaseLineOffset();
470     },
471     // Interpret frame to give fn(args)
472     getFunctionDescription: function(script, context, frame)
473     {
474         return {name: "eval", args: [this.evalExpression] };
475     },
476     getSourceLinkForScript: function (script)
477     {
478         return new FBL.SourceLink(this.sourceFile.href, 1, "js");
479     }
480 }
481 
482 //------------
483 Firebug.EventSourceFile = function(url, script, title, source, innerScriptEnumerator)
484 {
485      this.href = url;
486      this.outerScript = script;
487      this.containingURL = script.fileName;
488      this.title = title;
489      this.sourceLines = source; // points to the sourceCache lines
490      this.sourceLength = source.length;
491      this.pcmap_type = PCMAP_PRETTYPRINT;
492 
493      Firebug.SourceFile.addScriptsToSourceFile(this, script, innerScriptEnumerator);
494 };
495 
496 Firebug.EventSourceFile.prototype =	descend(new Firebug.SourceFile("event"),  // prototypical inheritance
497 {
498     getLine: function(context, lineNo)
499     {
500         return this.sourceLines[lineNo - 1];
501     },
502 
503     getBaseLineOffset: function()
504     {
505         return 1;
506     },
507 
508     getObjectDescription: function()
509     {
510         if (!this.summary)
511              this.summary = Firebug.SourceFile.summarizeSourceLineArray(this.sourceLines, 120);
512 
513         var containingFileDescription = FBL.splitURLBase(this.containingURL);
514 
515         return {path: containingFileDescription.path, name: containingFileDescription.name+"/event: "+this.summary };
516     },
517 
518     getOuterScriptAnalyzer: function()
519     {
520         return new Firebug.EventSourceFile.OuterScriptAnalyzer(this);
521     },
522 
523 });
524 
525 Firebug.EventSourceFile.OuterScriptAnalyzer = function(sourceFile)
526 {
527     this.sourceFile = sourceFile;
528 }
529 
530 Firebug.EventSourceFile.OuterScriptAnalyzer.prototype =
531 {
532     // Adjust JSD line numbers based on origin of script
533     getSourceLineFromFrame: function(context, frame)
534     {
535         var script = frame.script;
536         var line = script.pcToLine(frame.pc, PCMAP_PRETTYPRINT);
537         return line - 1;
538     },
539     // Interpret frame to give fn(args)
540     getFunctionDescription: function(script, context, frame)
541     {
542         var fn = unwrapIValue(script.functionObject);  //?? should be name of?
543         if (frame)
544             var args = FBL.getFunctionArgValues(frame);
545         else
546             var args = [];
547         return {name: fn, args: args};
548     },
549     getSourceLinkForScript: function (script)
550     {
551         return new FBL.SourceLink(this.sourceFile.href, 1, "js");  // XXXjjb why do we need FBL.??
552     }
553 }
554 
555 //------------
556 Firebug.SourceFile.CommonBase =
557 {
558     getSourceLength: function()
559     {
560         if (!this.sourceLength)
561             this.sourceLength = this.context.sourceCache.load(this.href).length;
562         return this.sourceLength;
563     },
564 
565     getOuterScriptAnalyzer: function()
566     {
567         return Firebug.TopLevelSourceFile.OuterScriptAnalyzer;
568     },
569 
570 }
571 //-----------
572 Firebug.TopLevelSourceFile = function(url, outerScript, sourceLength, innerScriptEnumerator)
573 {
574     this.href = url;
575     this.outerScript = outerScript;  // Beware may not be valid after we return!!
576     this.sourceLength = sourceLength;
577     this.pcmap_type = PCMAP_SOURCETEXT;
578 
579     Firebug.SourceFile.addScriptsToSourceFile(this, outerScript, innerScriptEnumerator);
580 }
581 
582 Firebug.TopLevelSourceFile.prototype = descend(new Firebug.SourceFile("top-level"), Firebug.SourceFile.CommonBase);
583 
584 
585 Firebug.TopLevelSourceFile.OuterScriptAnalyzer = {
586     // Adjust JSD line numbers based on origin of script
587     getSourceLineFromFrame: function(context, frame)
588     {
589         return frame.line;
590     },
591     // Interpret frame to give fn(args)
592     getFunctionDescription: function(script, context, frame)
593     {
594         var file_name = FBL.getFileName(FBL.normalizeURL(script.fileName)); // this is more useful that just "top_level"
595         file_name = file_name ? file_name: "__top_level__";
596         return {name: file_name, args: []};
597     },
598     getSourceLinkForScript: function (script)
599     {
600         return FBL.SourceLink(FBL.normalizeURL(script.fileName), script.baseLineNumber, "js")
601     }
602 }
603 
604 //-------
605 
606 Firebug.EnumeratedSourceFile = function(url) // we don't have the outer script and we delay source load.
607 {
608     this.href = new String(url);  // may not be outerScript file name, eg this could be an enumerated eval
609     this.innerScripts = {};
610     this.pcmap_type = PCMAP_SOURCETEXT;
611 }
612 
613 Firebug.EnumeratedSourceFile.prototype = descend(
614         new Firebug.SourceFile("enumerated"),
615         Firebug.SourceFile.CommonBase);
616 
617 //---------
618 Firebug.NoScriptSourceFile = function(context, url) // Somehow we got the URL, but not the script
619 {
620     this.href = url;  // we know this much
621     this.innerScripts = {};
622 }
623 
624 Firebug.NoScriptSourceFile.prototype = descend(
625         new Firebug.SourceFile("URLOnly"),
626         Firebug.SourceFile.CommonBase);
627 
628 //---------// javascript in a .xul or .xml file, no outerScript
629 Firebug.XULSourceFile = function(url, innerScriptEnumerator)
630 {
631     this.href = url;
632     this.pcmap_type = PCMAP_SOURCETEXT;
633 
634     Firebug.SourceFile.addScriptsToSourceFile(this, null, innerScriptEnumerator);
635 }
636 
637 Firebug.XULSourceFile.prototype = descend(
638         new Firebug.SourceFile("xul"),
639         Firebug.SourceFile.CommonBase);
640 
641 //---------
642 Firebug.ScriptTagSourceFile = function(context, url, scriptTagNumber) // we don't have the outer script and we delay source load
643 {
644     this.context = context;
645     this.href = url;  // we know this is not an eval
646     this.scriptTagNumber = scriptTagNumber;
647     this.innerScripts = {};
648     this.pcmap_type = PCMAP_SOURCETEXT;
649 }
650 
651 Firebug.ScriptTagSourceFile.prototype = descend(
652         new Firebug.SourceFile("scriptTag"),
653         Firebug.SourceFile.CommonBase);
654 
655 //-------------------
656 Firebug.SourceFile.getSourceFileByScript = function(context, script)
657 {
658     if (!context.sourceFileMap)
659          return null;
660 
661     // Other algorithms are possible:
662     //   We could store an index, context.sourceFileByTag
663     //   Or we could build a tree keyed by url, with SpiderMonkey script.fileNames at the top and our urls below
664     var lucky = context.sourceFileMap[script.fileName];  // we won't be lucky for file:/ urls, no normalizeURL applied
665     if (FBTrace.DBG_SOURCEFILES && lucky)
666         FBTrace.sysout("getSourceFileByScript trying to be lucky for "+
667             script.tag + " in "+lucky, script);
668 
669     if (lucky && lucky.hasScript(script))
670         return lucky;
671 
672     if (FBTrace.DBG_SOURCEFILES)
673         FBTrace.sysout("getSourceFileByScript looking for "+script.tag+"@"+script.fileName+" in "+
674             context.getName()+": ", context.sourceFileMap);
675 
676     for (var url in context.sourceFileMap)
677     {
678         var sourceFile = context.sourceFileMap[url];
679         if (sourceFile.hasScript(script))
680             return sourceFile;
681     }
682 };
683 
684 Firebug.SourceFile.getScriptAnalyzer = function(context, script)
685 {
686     var sourceFile = Firebug.SourceFile.getSourceFileByScript(context, script);
687     if (FBTrace.DBG_STACK)
688          FBTrace.sysout("getScriptAnalyzer "+ (sourceFile?"finds sourceFile: ":"FAILS to find sourceFile"), sourceFile);
689      if (sourceFile)
690      {
691          var analyzer = sourceFile.getScriptAnalyzer(script);
692          if (FBTrace.DBG_STACK)
693              FBTrace.sysout("getScriptAnalyzer finds analyzer: ", analyzer);
694 
695          return analyzer;
696      }
697      return undefined;
698 };
699 
700 Firebug.SourceFile.getSourceFileAndLineByScript= function(context, script, frame)
701 {
702     var sourceFile = Firebug.SourceFile.getSourceFileByScript(context, script);
703     if (sourceFile)
704     {
705         var analyzer = sourceFile.getScriptAnalyzer(script);
706         if (analyzer)
707             var line = frame ? analyzer.getSourceLineFromFrame(context, frame) : analyzer.getBaseLineNumberByScript(script);
708         else
709             var line = 0;
710 
711         return { sourceFile: sourceFile, lineNo: line };
712     }
713 };
714 
715 Firebug.SourceFile.guessEnclosingFunctionName = function(url, line, context)
716 {
717     var sourceFile = context.sourceFileMap[url];
718     if (sourceFile)
719     {
720         var scripts = sourceFile.getScriptsAtLineNumber(line);
721         if (scripts)
722         {
723             var script = scripts[0]; // TODO try others?
724             var analyzer = sourceFile.getScriptAnalyzer(script);
725             line = analyzer.getBaseLineNumberByScript(script);
726         }
727     }
728     return FBL.guessFunctionName(url, line-1, context);
729 };
730 
731 }});
732