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.
When do source maps work well?
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!
When do source maps fall short?
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
evalcapability, 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.
A Kernel of a Solution
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:
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 node.
column: The column number in the source file that corresponds to this AST node.
Objects that would work fine for a ClojureScript
Mapeven though it wouldn't be the best way to implement a ClojureScript