March 12th, 2014
Before diving in, it makes sense to tell you where I am coming from. I
implemented the mozilla/source-map library and the source map support in
the Firefox Developer Tools' debugger. I'm a coauthor of the
document specifying source maps; although the actual design of the
format was done by John Lenz. I mostly documented and polished the parts
that weren't about the format itself: like how to find a script's source map. On
top of that, I've debugged scripts with source maps compiled from browserify,
r.js AMD module optimizer, CoffeeScript, ClojureScript, Emscripten,
UglifyJS, Closure Compiler, and more.
Source maps work pretty well for:
CSS preprocessors and transpilers. From what I can tell, this is the most prevalent use of source maps, and what they've ended up being best at!
types. For example ClojureScript's
Object nor a
Map, and a C++
String. The result
is that the debugger user is inspecting the compiler's implementation of the
data type rather than the source language data type.
A stepping debugger isn't useful without the ability to inspect bindings and values in the environment, and the above issues create humungous hurdles for doing that!
An ideal SourceMap.next format should support the existing use of source maps:
It should support debugging dynamically compiled sources which don't live at a remote URL, like those created in the "Try CoffeeScript" online editor.
The format should be compact and space-saving so that the network isn't a bottleneck when debugging remote targets.
It should optionally provide an
eval capability, for use from a REPL,
watch expression, or conditional breakpoint.
The format should be future-extensible. That is, when SourceMap.next v2 rolls out, any SourceMap.next v1 consumer should still be able to parse and use instances of SourceMap.next v2 (although without the new features, of course). DWARF did a great job solving this problem with its Debugging Information Entries.
The ideas I present here aren't fully baked yet, but I have confidence in the approach.
Annotating the AST enables us to take advantage of the hierarchy present in the tree. If you are searching for a specific annotation and the current AST node does not have it, recurse on the node's parent. This makes it easy to provide very detailed information or a less fine-grained summary. For example, in the following AST, rather than requiring source location annotations for each AssignmentExpression, Identifier, BinaryExpression, and Literal AST nodes, one could simply add the annotation to the AssignmentExpression node. Then, any queries for source location information on any of the other nodes would bubble up to the AssignmentExpression and yield its annotated source location information.
AssignmentExpression operator: "=" left: Identifier name: "answer" right: BinaryExpression operator: "*" left: Literal value: 6 right: literal value: 7
Here are a few debugging annotations that should cover the requirements listed above:
sourceList: An array of sources that were compiled to create this
node. A source would be either a URL pointing to a source file, or the
inlined source text, display name, and content type.
source: The source (as an index into the
sourceList) from which this AST
node was compiled.
line: The line number in the source file that corresponds to this AST
column: The column number in the source file that corresponds to this
all local variables introduced by this AST node. You can walk up the tree to
the root to create the lexical environment chain.
display and inspection in the debugger. For example, a ClojureScript
already know how to provide a reasonable view of
Objects that would work
fine for a ClojureScript
Map even though it wouldn't be the best way to
implement a ClojureScript
arbitrary snippets of the source language. Would accept an optional
parameter that dictated which frame the debugger is paused in, if any.
Naming `eval` Scripts with the `//# sourceURL` Directive on December 5th, 2014
wu.js 2.0 on August 7th, 2014
Come work with me on Firefox Developer Tools on July 8th, 2014
Debugging Web Performance with Firefox DevTools - Velocity 2014 on June 26th, 2014
Beyond Source Maps on March 12th, 2014
Memory Tooling in Firefox Developer Tools in 2014 on March 4th, 2014
Hiding Implementation Details with ECMAScript 6 WeakMaps on January 13th, 2014
Re-evaluate Individual Functions in Firefox Developer Tools' Scratchpad on November 22nd, 2013
Testing Source Maps on October 2nd, 2013
Destructuring Assignment in ECMAScript 6 on August 15th, 2013