Files
coffeescript/documentation/docs/sourcemap.html
Jeremy Ashkenas a3e8de338a CoffeeScript 1.6.2
2013-03-18 13:06:33 +08:00

734 lines
25 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>sourcemap.coffee</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
<link rel="stylesheet" media="all" href="docco.css" />
</head>
<body>
<div id="container">
<div id="background"></div>
<ul id="jump_to">
<li>
<a class="large" href="javascript:void(0);">Jump To &hellip;</a>
<a class="small" href="javascript:void(0);">+</a>
<div id="jump_wrapper">
<div id="jump_page">
<a class="source" href="browser.html">
browser.coffee
</a>
<a class="source" href="cake.html">
cake.coffee
</a>
<a class="source" href="coffee-script.html">
coffee-script.coffee
</a>
<a class="source" href="command.html">
command.coffee
</a>
<a class="source" href="grammar.html">
grammar.coffee
</a>
<a class="source" href="helpers.html">
helpers.coffee
</a>
<a class="source" href="index.html">
index.coffee
</a>
<a class="source" href="lexer.html">
lexer.coffee
</a>
<a class="source" href="nodes.html">
nodes.coffee
</a>
<a class="source" href="optparse.html">
optparse.coffee
</a>
<a class="source" href="repl.html">
repl.coffee
</a>
<a class="source" href="rewriter.html">
rewriter.coffee
</a>
<a class="source" href="scope.html">
scope.litcoffee
</a>
<a class="source" href="sourcemap.html">
sourcemap.coffee
</a>
</div>
</li>
</ul>
<ul class="sections">
<li id="title">
<div class="annotation">
<h1>sourcemap.coffee</h1>
</div>
</li>
<li id="section-1">
<div class="annotation">
<div class="pilwrap for-h3">
<a class="pilcrow" href="#section-1">&#182;</a>
</div>
<h3>LineMapping</h3>
<p>Hold data about mappings for one line of generated source code.
</p>
</div>
<div class="content"><div class='highlight'><pre><span class="class"><span class="keyword">class</span> <span class="title">LineMapping</span></span>
constructor: (<span class="property">@generatedLine</span>) -&gt;</pre></div></div>
</li>
<li id="section-2">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-2">&#182;</a>
</div>
<p>columnMap keeps track of which columns we&#39;ve already mapped.
</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="property">@columnMap</span> = {}</pre></div></div>
</li>
<li id="section-3">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-3">&#182;</a>
</div>
<p>columnMappings is an array of all column mappings, sorted by generated-column.
</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="property">@columnMappings</span> = []
addMapping: (generatedColumn, [sourceLine, sourceColumn], options={}) -&gt;
<span class="keyword">if</span> <span class="property">@columnMap</span>[generatedColumn] <span class="keyword">and</span> options.noReplace</pre></div></div>
</li>
<li id="section-4">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-4">&#182;</a>
</div>
<p>We already have a mapping for this column.
</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="keyword">return</span>
<span class="property">@columnMap</span>[generatedColumn] = {
generatedLine: <span class="property">@generatedLine</span>
generatedColumn
sourceLine
sourceColumn
}
<span class="property">@columnMappings</span>.push <span class="property">@columnMap</span>[generatedColumn]
<span class="property">@columnMappings</span>.sort (a,b) -&gt; a.generatedColumn - b.generatedColumn
getSourcePosition: (generatedColumn) -&gt;
answer = <span class="literal">null</span>
lastColumnMapping = <span class="literal">null</span>
<span class="keyword">for</span> columnMapping <span class="keyword">in</span> <span class="property">@columnMappings</span>
<span class="keyword">if</span> columnMapping.generatedColumn &gt; generatedColumn
<span class="keyword">break</span>
<span class="keyword">else</span>
lastColumnMapping = columnMapping
<span class="keyword">if</span> lastColumnMapping
answer = [lastColumnMapping.sourceLine, lastColumnMapping.sourceColumn]</pre></div></div>
</li>
<li id="section-5">
<div class="annotation">
<div class="pilwrap for-h3">
<a class="pilcrow" href="#section-5">&#182;</a>
</div>
<h3>SourceMap</h3>
<p>Maps locations in a generated source file back to locations in the original source file.
</p>
<p>This is intentionally agnostic towards how a source map might be represented on disk. A
SourceMap can be converted to a &quot;v3&quot; style sourcemap with <code>#generateV3SourceMap()</code>, for example
but the SourceMap class itself knows nothing about v3 source maps.
</p>
</div>
<div class="content"><div class='highlight'><pre><span class="class"><span class="keyword">class</span> <span class="title">exports</span>.<span class="title">SourceMap</span></span>
constructor: () -&gt;</pre></div></div>
</li>
<li id="section-6">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-6">&#182;</a>
</div>
<p><code>generatedLines</code> is an array of LineMappings, one per generated line.
</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="property">@generatedLines</span> = []</pre></div></div>
</li>
<li id="section-7">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-7">&#182;</a>
</div>
<p>Adds a mapping to this SourceMap.
</p>
<p><code>sourceLocation</code> and <code>generatedLocation</code> are both [line, column] arrays.
</p>
<p>If <code>options.noReplace</code> is true, then if there is already a mapping for
the specified <code>generatedLine</code> and <code>generatedColumn</code>, this will have no effect.
</p>
</div>
<div class="content"><div class='highlight'><pre> addMapping: (sourceLocation, generatedLocation, options={}) -&gt;
[generatedLine, generatedColumn] = generatedLocation
lineMapping = <span class="property">@generatedLines</span>[generatedLine]
<span class="keyword">if</span> <span class="keyword">not</span> lineMapping
lineMapping = <span class="property">@generatedLines</span>[generatedLine] = <span class="keyword">new</span> LineMapping(generatedLine)
lineMapping.addMapping generatedColumn, sourceLocation, options</pre></div></div>
</li>
<li id="section-8">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-8">&#182;</a>
</div>
<p>Returns [sourceLine, sourceColumn], or null if no mapping could be found.
</p>
</div>
<div class="content"><div class='highlight'><pre> getSourcePosition: ([generatedLine, generatedColumn]) -&gt;
answer = <span class="literal">null</span>
lineMapping = <span class="property">@generatedLines</span>[generatedLine]
<span class="keyword">if</span> <span class="keyword">not</span> lineMapping</pre></div></div>
</li>
<li id="section-9">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-9">&#182;</a>
</div>
<p>TODO: Search backwards for the line?
</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="keyword">else</span>
answer = lineMapping.getSourcePosition generatedColumn
answer</pre></div></div>
</li>
<li id="section-10">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-10">&#182;</a>
</div>
<p><code>fn</code> will be called once for every recorded mapping, in the order in
which they occur in the generated source. <code>fn</code> will be passed an object
with four properties: sourceLine, sourceColumn, generatedLine, and
generatedColumn.
</p>
</div>
<div class="content"><div class='highlight'><pre> forEachMapping: (fn) -&gt;
<span class="keyword">for</span> lineMapping, generatedLineNumber <span class="keyword">in</span> <span class="property">@generatedLines</span>
<span class="keyword">if</span> lineMapping
<span class="keyword">for</span> columnMapping <span class="keyword">in</span> lineMapping.columnMappings
fn(columnMapping)</pre></div></div>
</li>
<li id="section-11">
<div class="annotation">
<div class="pilwrap for-h3">
<a class="pilcrow" href="#section-11">&#182;</a>
</div>
<h3>generateV3SourceMap</h3>
<p>Builds a V3 source map from a SourceMap object.
Returns the generated JSON as a string.
</p>
<p><code>options.sourceRoot</code> may be used to specify the sourceRoot written to the source map. Also,
<code>options.sourceFiles</code> and <code>options.generatedFile</code> may be passed to set &quot;sources&quot; and &quot;file&quot;,
respectively. Note that <code>sourceFiles</code> must be an array.
</p>
</div>
<div class="content"><div class='highlight'><pre>exports.<span class="function"><span class="title">generateV3SourceMap</span></span> = (sourceMap, options={}, code) -&gt;
sourceRoot = options.sourceRoot <span class="keyword">or</span> <span class="string">""</span>
sourceFiles = options.sourceFiles <span class="keyword">or</span> [<span class="string">""</span>]
generatedFile = options.generatedFile <span class="keyword">or</span> <span class="string">""</span>
writingGeneratedLine = <span class="number">0</span>
lastGeneratedColumnWritten = <span class="number">0</span>
lastSourceLineWritten = <span class="number">0</span>
lastSourceColumnWritten = <span class="number">0</span>
needComma = <span class="literal">no</span>
mappings = <span class="string">""</span>
sourceMap.forEachMapping (mapping) -&gt;
<span class="keyword">while</span> writingGeneratedLine &lt; mapping.generatedLine
lastGeneratedColumnWritten = <span class="number">0</span>
needComma = <span class="literal">no</span>
mappings += <span class="string">";"</span>
writingGeneratedLine++</pre></div></div>
</li>
<li id="section-12">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-12">&#182;</a>
</div>
<p>Write a comma if we&#39;ve already written a segment on this line.
</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="keyword">if</span> needComma
mappings += <span class="string">","</span>
needComma = <span class="literal">no</span></pre></div></div>
</li>
<li id="section-13">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-13">&#182;</a>
</div>
<p>Write the next segment.
Segments can be 1, 4, or 5 values. If just one, then it is a generated column which
doesn&#39;t match anything in the source code.
</p>
<p>Fields are all zero-based, and relative to the previous occurence unless otherwise noted:
<em> starting-column in generated source, relative to previous occurence for the current line.
</em> index into the &quot;sources&quot; list
<em> starting line in the original source
</em> starting column in the original source
* index into the &quot;names&quot; list associated with this segment.
</p>
<p>Add the generated start-column
</p>
</div>
<div class="content"><div class='highlight'><pre> mappings += exports.vlqEncodeValue(mapping.generatedColumn - lastGeneratedColumnWritten)
lastGeneratedColumnWritten = mapping.generatedColumn</pre></div></div>
</li>
<li id="section-14">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-14">&#182;</a>
</div>
<p>Add the index into the sources list
</p>
</div>
<div class="content"><div class='highlight'><pre> mappings += exports.vlqEncodeValue(<span class="number">0</span>)</pre></div></div>
</li>
<li id="section-15">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-15">&#182;</a>
</div>
<p>Add the source start-line
</p>
</div>
<div class="content"><div class='highlight'><pre> mappings += exports.vlqEncodeValue(mapping.sourceLine - lastSourceLineWritten)
lastSourceLineWritten = mapping.sourceLine</pre></div></div>
</li>
<li id="section-16">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-16">&#182;</a>
</div>
<p>Add the source start-column
</p>
</div>
<div class="content"><div class='highlight'><pre> mappings += exports.vlqEncodeValue(mapping.sourceColumn - lastSourceColumnWritten)
lastSourceColumnWritten = mapping.sourceColumn</pre></div></div>
</li>
<li id="section-17">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-17">&#182;</a>
</div>
<p>TODO: Do we care about symbol names for CoffeeScript? Probably not.
</p>
</div>
<div class="content"><div class='highlight'><pre> needComma = <span class="literal">yes</span>
answer = {
version: <span class="number">3</span>
file: generatedFile
sourceRoot
sources: sourceFiles
names: []
mappings
}
answer.sourcesContent = [code] <span class="keyword">if</span> options.inline
<span class="keyword">return</span> JSON.stringify answer, <span class="literal">null</span>, <span class="number">2</span></pre></div></div>
</li>
<li id="section-18">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-18">&#182;</a>
</div>
<p>Load a SourceMap from a JSON string. Returns the SourceMap object.
</p>
</div>
<div class="content"><div class='highlight'><pre>exports.<span class="function"><span class="title">loadV3SourceMap</span></span> = (sourceMap) -&gt;
todo()</pre></div></div>
</li>
<li id="section-19">
<div class="annotation">
<div class="pilwrap for-h3">
<a class="pilcrow" href="#section-19">&#182;</a>
</div>
<h3>Base64 encoding helpers</h3>
</div>
<div class="content"><div class='highlight'><pre>BASE64_CHARS = <span class="string">'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'</span>
MAX_BASE64_VALUE = BASE64_CHARS.length - <span class="number">1</span>
<span class="function"><span class="title">encodeBase64Char</span></span> = (value) -&gt;
<span class="keyword">if</span> value &gt; MAX_BASE64_VALUE
<span class="keyword">throw</span> <span class="keyword">new</span> Error <span class="string">"Cannot encode value <span class="subst">#{value}</span> &gt; <span class="subst">#{MAX_BASE64_VALUE}</span>"</span>
<span class="keyword">else</span> <span class="keyword">if</span> value &lt; <span class="number">0</span>
<span class="keyword">throw</span> <span class="keyword">new</span> Error <span class="string">"Cannot encode value <span class="subst">#{value}</span> &lt; 0"</span>
BASE64_CHARS[value]
<span class="function"><span class="title">decodeBase64Char</span></span> = (char) -&gt;
value = BASE64_CHARS.indexOf char
<span class="keyword">if</span> value == -<span class="number">1</span>
<span class="keyword">throw</span> <span class="keyword">new</span> Error <span class="string">"Invalid Base 64 character: <span class="subst">#{char}</span>"</span>
value</pre></div></div>
</li>
<li id="section-20">
<div class="annotation">
<div class="pilwrap for-h3">
<a class="pilcrow" href="#section-20">&#182;</a>
</div>
<h3>Base 64 VLQ encoding/decoding helpers</h3>
<p>Note that SourceMap VLQ encoding is &quot;backwards&quot;. MIDI style VLQ encoding puts the
most-significant-bit (MSB) from the original value into the MSB of the VLQ encoded value
(see <a href="http://en.wikipedia.org/wiki/File:Uintvar_coding.svg">http://en.wikipedia.org/wiki/File:Uintvar_coding.svg</a>). SourceMap VLQ does things
the other way around, with the least significat four bits of the original value encoded
into the first byte of the VLQ encoded value.
</p>
</div>
<div class="content"><div class='highlight'><pre>VLQ_SHIFT = <span class="number">5</span>
VLQ_CONTINUATION_BIT = <span class="number">1</span> &lt;&lt; VLQ_SHIFT <span class="comment"># 0010 0000</span>
VLQ_VALUE_MASK = VLQ_CONTINUATION_BIT - <span class="number">1</span> <span class="comment"># 0001 1111</span></pre></div></div>
</li>
<li id="section-21">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-21">&#182;</a>
</div>
<p>Encode a value as Base 64 VLQ.
</p>
</div>
<div class="content"><div class='highlight'><pre>exports.<span class="function"><span class="title">vlqEncodeValue</span></span> = (value) -&gt;</pre></div></div>
</li>
<li id="section-22">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-22">&#182;</a>
</div>
<p>Least significant bit represents the sign.
</p>
</div>
<div class="content"><div class='highlight'><pre> signBit = <span class="keyword">if</span> value &lt; <span class="number">0</span> <span class="keyword">then</span> <span class="number">1</span> <span class="keyword">else</span> <span class="number">0</span></pre></div></div>
</li>
<li id="section-23">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-23">&#182;</a>
</div>
<p>Next bits are the actual value
</p>
</div>
<div class="content"><div class='highlight'><pre> valueToEncode = (Math.abs(value) &lt;&lt; <span class="number">1</span>) + signBit
answer = <span class="string">""</span></pre></div></div>
</li>
<li id="section-24">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-24">&#182;</a>
</div>
<p>Make sure we encode at least one character, even if valueToEncode is 0.
</p>
</div>
<div class="content"><div class='highlight'><pre> <span class="keyword">while</span> valueToEncode || !answer
nextVlqChunk = valueToEncode &amp; VLQ_VALUE_MASK
valueToEncode = valueToEncode &gt;&gt; VLQ_SHIFT
<span class="keyword">if</span> valueToEncode
nextVlqChunk |= VLQ_CONTINUATION_BIT
answer += encodeBase64Char(nextVlqChunk)
<span class="keyword">return</span> answer</pre></div></div>
</li>
<li id="section-25">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-25">&#182;</a>
</div>
<p>Decode a Base 64 VLQ value.
</p>
<p>Returns <code>[value, consumed]</code> where <code>value</code> is the decoded value, and <code>consumed</code> is the number
of characters consumed from <code>str</code>.
</p>
</div>
<div class="content"><div class='highlight'><pre>exports.<span class="function"><span class="title">vlqDecodeValue</span></span> = (str, offset=<span class="number">0</span>) -&gt;
position = offset
done = <span class="literal">false</span>
value = <span class="number">0</span>
continuationShift = <span class="number">0</span>
<span class="keyword">while</span> !done
nextVlqChunk = decodeBase64Char(str[position])
position += <span class="number">1</span>
nextChunkValue = nextVlqChunk &amp; VLQ_VALUE_MASK
value += (nextChunkValue &lt;&lt; continuationShift)
<span class="keyword">if</span> !(nextVlqChunk &amp; VLQ_CONTINUATION_BIT)</pre></div></div>
</li>
<li id="section-26">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-26">&#182;</a>
</div>
<p>We&#39;ll be done after this character.
</p>
</div>
<div class="content"><div class='highlight'><pre> done = <span class="literal">true</span></pre></div></div>
</li>
<li id="section-27">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-27">&#182;</a>
</div>
<p>Bits are encoded least-significant first (opposite of MIDI VLQ). Increase the
continuationShift, so the next byte will end up where it should in the value.
</p>
</div>
<div class="content"><div class='highlight'><pre> continuationShift += VLQ_SHIFT
consumed = position - offset</pre></div></div>
</li>
<li id="section-28">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-28">&#182;</a>
</div>
<p>Least significant bit represents the sign.
</p>
</div>
<div class="content"><div class='highlight'><pre> signBit = value &amp; <span class="number">1</span>
value = value &gt;&gt; <span class="number">1</span>
<span class="keyword">if</span> signBit <span class="keyword">then</span> value = -value
<span class="keyword">return</span> [value, consumed]</pre></div></div>
</li>
</ul>
</div>
</body>
</html>