mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
523 lines
20 KiB
HTML
523 lines
20 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<html>
|
||
<head>
|
||
<title>sourcemap.litcoffee</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 …</a>
|
||
<a class="small" href="javascript:void(0);">+</a>
|
||
<div id="jump_wrapper">
|
||
<div id="jump_page_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="register.html">
|
||
register.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.litcoffee
|
||
</a>
|
||
|
||
</div>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
|
||
<ul class="sections">
|
||
|
||
<li id="title">
|
||
<div class="annotation">
|
||
<h1>sourcemap.litcoffee</h1>
|
||
</div>
|
||
</li>
|
||
|
||
|
||
|
||
<li id="section-1">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-1">¶</a>
|
||
</div>
|
||
<p>Source maps allow JavaScript runtimes to match running JavaScript back to
|
||
the original source code that corresponds to it. This can be minified
|
||
JavaScript, but in our case, we’re concerned with mapping pretty-printed
|
||
JavaScript back to CoffeeScript.</p>
|
||
<p>In order to produce maps, we must keep track of positions (line number, column number)
|
||
that originated every node in the syntax tree, and be able to generate a
|
||
<a href="https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit">map file</a>
|
||
— which is a compact, VLQ-encoded representation of the JSON serialization
|
||
of this information — to write out alongside the generated JavaScript.</p>
|
||
<h2 id="linemap">LineMap</h2>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-2">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-2">¶</a>
|
||
</div>
|
||
<p>A <strong>LineMap</strong> object keeps track of information about original line and column
|
||
positions for a single line of output JavaScript code.
|
||
<strong>SourceMaps</strong> are implemented in terms of <strong>LineMaps</strong>.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LineMap</span></span>
|
||
constructor: <span class="hljs-function"><span class="hljs-params">(@line)</span> -></span>
|
||
@columns = []
|
||
|
||
add: <span class="hljs-function"><span class="hljs-params">(column, [sourceLine, sourceColumn], options={})</span> -></span>
|
||
<span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> @columns[column] <span class="hljs-keyword">and</span> options.noReplace
|
||
@columns[column] = {line: @line, column, sourceLine, sourceColumn}
|
||
|
||
sourceLocation: <span class="hljs-function"><span class="hljs-params">(column)</span> -></span>
|
||
column-- <span class="hljs-keyword">until</span> (mapping = @columns[column]) <span class="hljs-keyword">or</span> (column <= <span class="hljs-number">0</span>)
|
||
mapping <span class="hljs-keyword">and</span> [mapping.sourceLine, mapping.sourceColumn]</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-3">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-3">¶</a>
|
||
</div>
|
||
<h2 id="sourcemap">SourceMap</h2>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-4">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-4">¶</a>
|
||
</div>
|
||
<p>Maps locations in a single generated JavaScript file back to locations in
|
||
the original CoffeeScript source file.</p>
|
||
<p>This is intentionally agnostic towards how a source map might be represented on
|
||
disk. Once the compiler is ready to produce a “v3”-style source map, we can walk
|
||
through the arrays of line and column buffer to produce it.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SourceMap</span></span>
|
||
constructor: <span class="hljs-function">-></span>
|
||
@lines = []</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-5">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-5">¶</a>
|
||
</div>
|
||
<p>Adds a mapping to this SourceMap. <code>sourceLocation</code> and <code>generatedLocation</code>
|
||
are both <code>[line, column]</code> arrays. If <code>options.noReplace</code> is true, then if there
|
||
is already a mapping for the specified <code>line</code> and <code>column</code>, this will have no
|
||
effect.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> add: <span class="hljs-function"><span class="hljs-params">(sourceLocation, generatedLocation, options = {})</span> -></span>
|
||
[line, column] = generatedLocation
|
||
lineMap = (@lines[line] <span class="hljs-keyword">or</span>= <span class="hljs-keyword">new</span> LineMap(line))
|
||
lineMap.add column, sourceLocation, options</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-6">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-6">¶</a>
|
||
</div>
|
||
<p>Look up the original position of a given <code>line</code> and <code>column</code> in the generated
|
||
code.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> sourceLocation: <span class="hljs-function"><span class="hljs-params">([line, column])</span> -></span>
|
||
line-- <span class="hljs-keyword">until</span> (lineMap = @lines[line]) <span class="hljs-keyword">or</span> (line <= <span class="hljs-number">0</span>)
|
||
lineMap <span class="hljs-keyword">and</span> lineMap.sourceLocation column</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-7">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-7">¶</a>
|
||
</div>
|
||
<h2 id="v3-sourcemap-generation">V3 SourceMap Generation</h2>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-8">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-8">¶</a>
|
||
</div>
|
||
<p>Builds up a V3 source map, returning the generated JSON as a string.
|
||
<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 “sources” and “file”, respectively.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> generate: <span class="hljs-function"><span class="hljs-params">(options = {}, code = <span class="hljs-literal">null</span>)</span> -></span>
|
||
writingline = <span class="hljs-number">0</span>
|
||
lastColumn = <span class="hljs-number">0</span>
|
||
lastSourceLine = <span class="hljs-number">0</span>
|
||
lastSourceColumn = <span class="hljs-number">0</span>
|
||
needComma = <span class="hljs-literal">no</span>
|
||
buffer = <span class="hljs-string">""</span>
|
||
|
||
<span class="hljs-keyword">for</span> lineMap, lineNumber <span class="hljs-keyword">in</span> @lines <span class="hljs-keyword">when</span> lineMap
|
||
<span class="hljs-keyword">for</span> mapping <span class="hljs-keyword">in</span> lineMap.columns <span class="hljs-keyword">when</span> mapping
|
||
<span class="hljs-keyword">while</span> writingline < mapping.line
|
||
lastColumn = <span class="hljs-number">0</span>
|
||
needComma = <span class="hljs-literal">no</span>
|
||
buffer += <span class="hljs-string">";"</span>
|
||
writingline++</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-9">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-9">¶</a>
|
||
</div>
|
||
<p>Write a comma if we’ve already written a segment on this line.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> <span class="hljs-keyword">if</span> needComma
|
||
buffer += <span class="hljs-string">","</span>
|
||
needComma = <span class="hljs-literal">no</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-10">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-10">¶</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’t match anything in the source code.</p>
|
||
<p>The starting column in the generated source, relative to any previous recorded
|
||
column for the current line:</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> buffer += @encodeVlq mapping.column - lastColumn
|
||
lastColumn = mapping.column</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-11">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-11">¶</a>
|
||
</div>
|
||
<p>The index into the list of sources:</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> buffer += @encodeVlq <span class="hljs-number">0</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-12">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-12">¶</a>
|
||
</div>
|
||
<p>The starting line in the original source, relative to the previous source line.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> buffer += @encodeVlq mapping.sourceLine - lastSourceLine
|
||
lastSourceLine = mapping.sourceLine</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-13">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-13">¶</a>
|
||
</div>
|
||
<p>The starting column in the original source, relative to the previous column.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> buffer += @encodeVlq mapping.sourceColumn - lastSourceColumn
|
||
lastSourceColumn = mapping.sourceColumn
|
||
needComma = <span class="hljs-literal">yes</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-14">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-14">¶</a>
|
||
</div>
|
||
<p>Produce the canonical JSON object format for a “v3” source map.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> v3 =
|
||
version: <span class="hljs-number">3</span>
|
||
file: options.generatedFile <span class="hljs-keyword">or</span> <span class="hljs-string">''</span>
|
||
sourceRoot: options.sourceRoot <span class="hljs-keyword">or</span> <span class="hljs-string">''</span>
|
||
sources: options.sourceFiles <span class="hljs-keyword">or</span> [<span class="hljs-string">''</span>]
|
||
names: []
|
||
mappings: buffer
|
||
|
||
v3.sourcesContent = [code] <span class="hljs-keyword">if</span> options.inlineMap
|
||
|
||
v3</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-15">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-15">¶</a>
|
||
</div>
|
||
<h2 id="base64-vlq-encoding">Base64 VLQ Encoding</h2>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-16">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-16">¶</a>
|
||
</div>
|
||
<p>Note that SourceMap VLQ encoding is “backwards”. 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">Wikipedia</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="hljs-number">5</span>
|
||
VLQ_CONTINUATION_BIT = <span class="hljs-number">1</span> << VLQ_SHIFT <span class="hljs-comment"># 0010 0000</span>
|
||
VLQ_VALUE_MASK = VLQ_CONTINUATION_BIT - <span class="hljs-number">1</span> <span class="hljs-comment"># 0001 1111</span>
|
||
|
||
encodeVlq: <span class="hljs-function"><span class="hljs-params">(value)</span> -></span>
|
||
answer = <span class="hljs-string">''</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-17">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-17">¶</a>
|
||
</div>
|
||
<p>Least significant bit represents the sign.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> signBit = <span class="hljs-keyword">if</span> value < <span class="hljs-number">0</span> <span class="hljs-keyword">then</span> <span class="hljs-number">1</span> <span class="hljs-keyword">else</span> <span class="hljs-number">0</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-18">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-18">¶</a>
|
||
</div>
|
||
<p>The next bits are the actual value.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> valueToEncode = (Math.abs(value) << <span class="hljs-number">1</span>) + signBit</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-19">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-19">¶</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="hljs-keyword">while</span> valueToEncode <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> answer
|
||
nextChunk = valueToEncode & VLQ_VALUE_MASK
|
||
valueToEncode = valueToEncode >> VLQ_SHIFT
|
||
nextChunk |= VLQ_CONTINUATION_BIT <span class="hljs-keyword">if</span> valueToEncode
|
||
answer += @encodeBase64 nextChunk
|
||
|
||
answer</pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-20">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-20">¶</a>
|
||
</div>
|
||
<h2 id="regular-base64-encoding">Regular Base64 Encoding</h2>
|
||
|
||
</div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-21">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-21">¶</a>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre> BASE64_CHARS = <span class="hljs-string">'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'</span>
|
||
|
||
encodeBase64: <span class="hljs-function"><span class="hljs-params">(value)</span> -></span>
|
||
BASE64_CHARS[value] <span class="hljs-keyword">or</span> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Error <span class="hljs-string">"Cannot Base64 encode value: <span class="hljs-subst">#{value}</span>"</span></pre></div></div>
|
||
|
||
</li>
|
||
|
||
|
||
<li id="section-22">
|
||
<div class="annotation">
|
||
|
||
<div class="pilwrap ">
|
||
<a class="pilcrow" href="#section-22">¶</a>
|
||
</div>
|
||
<p>Our API for source maps is just the <code>SourceMap</code> class.</p>
|
||
|
||
</div>
|
||
|
||
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">module</span>.exports = SourceMap</pre></div></div>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</div>
|
||
</body>
|
||
</html>
|