From:  Ron Buckton <>
Date:  03 Feb 2015 06:06:42 Hong Kong Time

RE: Source Maps, Debuggers, and Generated Code


> 1. When the compiler needs to embed a runtime with various helper
> functions in the generated JS code. In this case, I think it makes sense to add
> an entry to the sources list and map the appropriate code to this runtime
> source.

Is there a general best-practice for this? Traceur adds an entry to the 'sources' list that is prefixed with "@traceur/generated", as well as adding an entry to 'sourcesContent' containing the same text written to the generated output file. Adding a string to sourceContent for this purpose seems like unnecessary overhead. TypeScript emits null mappings for each line of generated content.

> 2. When a single statement in the source language results in many
> statements in the JS code. In this case, I believe that all the generated
> statements in the JS code should map back to the same location in the source
> language. When implementing source level single stepping, debuggers
> should continue JS object code level single stepping until the source map
> reports a new source location for the current JS code. This is conceptually the
> same as when single stepping C-source level statements in gdb/lldb/etc that
> map to multiple asm instructions: you don't want to pause after executing
> each asm instruction, but once all the asm instructions for that C statement
> have been executed.

While this makes sense for statements that generate a sequential set of steps, it breaks down slightly when you are generating something like the downlevel emit for a generator or async function, where you might end up with something like this:

async function asyncFunc(p: Promise): Promise {
  var i = await p;
  return i;

Which generates (with the current Async Functions prototype, plus additional comments):
var __awaiter = ...; // runtime helper
var __generator = ...; // runtime helper
function asyncFunc(p) {
  // (a)
  return new Promise(function(_resolve) {
    _resolve(__awaiter(__generator(function(_state) {
        // (b)
        switch (_state) {
            // (c)
            case 0: 
                // (d)
                return [4 /*yield*/, p];
            // (e)
            case 1:
                // (f)
                i = _state.sent;
                return [2 /*return*/, i + 1];
  var i;

When the debugger steps through 'asyncFunc', it will execute [a], [b], [c], and [d] immediately before yielding, and when the state machine is resumed it will execute [b], [c], [e], and [f]. As a result, [b] and [c] are executed twice (and for a larger function body with multiple 'case' clauses, it would likely step through each case clause up to the current "label"). Based on my understanding of your response below, we should emit "null" mappings for [a]-[c] and for [e]. When the debugger steps into the function for the first time it should step through until it hits [d], and after it resumes it should step through until it hits [f]. Is this an accurate assumption?

> ‚ÄčI don't think this needs an extension: I believe continuing through null
> mappings is the correct behavior.

I agree that this makes sense. I just want to verify the expected behavior.