Yield Ahead: Regenerator in Depth – Next, traverse and modify the syntax tree: – loc() and this.mark(loc)



Yield Ahead: Regenerator in Depth – Next, traverse and modify the syntax tree: – loc() and this.mark(loc)

0 0


jsconf-2014

Slides for my talk entitled "Yield Ahead: Regenerator in Depth" at JSConf 2014

On Github benjamn / jsconf-2014

Yield Ahead: Regenerator in Depth

Ben Newman (Facebook)JSConf 2014

{ github,
  twitter,
  instagram,
  facebook
}.com/benjamn

facebook.github.io/regenerator

“We should have been doing it this way all along!”

Congratulations! You're not done.

Not even if you're right.

Not even if everyone agrees that you're right.

GitHub is strewn with better ways of doing things that never quite caught on.

Code just left there without warranty of any kind, either expressed or implied, in the vague hope that other people face needs that are similar to the ones that motivated the author.

Often “better” just means “better tailored to my particular use case.”

In which case there's no mystery why the evangelical message went nowhere.

There has to be a way forward.

And it can't involve throwing away all of your code and starting over from scratch, as tempting as that might be.

As the evangelist, you have a responsibility to yourself, to your ideas, and to the people you want to help, to devise and execute an incremental migration plan.

Other people won't be willing to buy in unless you justify the effort and short-term increase in complexity.

Programming languages are notoriously difficult to “fix forward.”

Why isn't every Python project using Python 3 yet?

It has that 2to3 script.

The maintainers have realistic expectations.

Biggest barrier: changes in unicode semantics.

Attempting to solve a problem once and for all typically requires adapting many different, somewhat-functional past solutions.

How can ECMAScript 6avoid the Python 3 trap?

Crazy idea: ease into the new language by simulating its most useful features in the current version of JavaScript (ECMAScript 5).

The great virtue of this idea is that it makes sense at every stage.

Even if it never ultimately connects with the next version of the language, or takes a long time getting there, we still benefit.

“Linguistic time travel”

Example: => function syntax

Input (ES6):
[3, 1, 10, 28].sort((a, b) => a - b)
 
 
Output (ES5):
[3, 1, 10, 28].sort(function(a, b) {
  return a - b;
}.bind(this))
 
 

First, import some utilities and parse the code:

var recast = require("recast");
var types = recast.types;
var traverse = types.traverse;
var n = types.namedTypes;
var b = types.builders;
 
var ast = recast.parse(
  "[3, 1, 10, 28].sort((a, b) => a - b)"
);
 
 

Next, traverse and modify the syntax tree:

traverse(ast, function(node) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
});
 
 

Next, traverse and modify the syntax tree:

traverse(ast, function(node) {
  if (n.ArrowFunctionExpression.check(node)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  }
});
 
 

Next, traverse and modify the syntax tree:

traverse(ast, function(node) {
  if (n.ArrowFunctionExpression.check(node)) {
    var body = node.body;
 
    if (node.expression) {
      node.expression = false;
      body = b.blockStatement([b.returnStatement(body)]);
    }
 
 
 
 
 
 
 
 
 
 
 
 
  }
});
 
 

Next, traverse and modify the syntax tree:

traverse(ast, function(node) {
  if (n.ArrowFunctionExpression.check(node)) {
    var body = node.body;
 
    if (node.expression) {
      node.expression = false;
      body = b.blockStatement([b.returnStatement(body)]);
    }
 
    var funExp = b.functionExpression(
      node.id, node.params, body,
      node.generator, node.expression
    );
 
 
 
 
 
 
 
  }
});
 
 

Next, traverse and modify the syntax tree:

traverse(ast, function(node) {
  if (n.ArrowFunctionExpression.check(node)) {
    var body = node.body;
 
    if (node.expression) {
      node.expression = false;
      body = b.blockStatement([b.returnStatement(body)]);
    }
 
    var funExp = b.functionExpression(
      node.id, node.params, body,
      node.generator, node.expression
    );
 
    var bindExp = b.callExpression(
      b.memberExpression(funExp, b.identifier("bind"), false),
      [b.thisExpression()]
    );
 
 
  }
});
 
 

Next, traverse and modify the syntax tree:

traverse(ast, function(node) {
  if (n.ArrowFunctionExpression.check(node)) {
    var body = node.body;
 
    if (node.expression) {
      node.expression = false;
      body = b.blockStatement([b.returnStatement(body)]);
    }
 
    var funExp = b.functionExpression(
      node.id, node.params, body,
      node.generator, node.expression
    );
 
    var bindExp = b.callExpression(
      b.memberExpression(funExp, b.identifier("bind"), false),
      [b.thisExpression()]
    );
 
    this.replace(bindExp);
  }
});
 
 

Finally, reprint the code:

console.log(recast.print(ast).code);
 
// Which prints:
[3, 1, 10, 28].sort(function(a, b) {
  return a - b;
}.bind(this))
 
 

If you already have a build step for static resources, you can be cooking with arrow functions in a matter of minutes!

recast, v.

to give (a metal object) a different form by melting it down and reshaping it. to form, fashion, or arrange again. to remodel or reconstruct (a literary work, document, sentence, etc.). to supply (a theater or opera work) with a new cast.

Recast recap:

var recast = require("recast");
var ast = recast.parse(source);
transform(ast); // Anything goes.
console.log(recast.print(ast).code);
 
 

“Non-destructive partial source transformation” – Ariya Hidayat

Instead of simply pretty-printing the whole tree, recast.print tries to recyle the original source code wherever possible.

Moral hazard:

  • be the laziest, most productive programmer you know
  • be the the change you want to see in the world

Confession:

I gave into temptation for a time.

All told:

1647 files changed
76555 insertions(+)
78260 deletions(-)
1658 classes converted
3582 classes today
That's 1924 classes that have been written by other people in the ES6 style in the last six months.
find ~/www/html/js/lib | \
  grep "\.js$" | \
  time parallel ~/www/scripts/bin/classify --update
 
228.03s user 12.25s system 1229% cpu 19.548 total
 
 

In case you're curious about the details, I gave a whole talk about this project two months ago: slides, video.

What about something trickier?

function *permutations(list) {
  if (list.length < 2) {
    yield list;
  } else {
    var first = list.slice(0, 1);
    var ps = permutations(list.slice(1));
    for (var p of ps) {
      for (var i = 0; i < list.length; ++i) {
        yield Array.prototype.concat.call(
          p.slice(0, i), // prefix
          first,         // middle
          p.slice(i)     // suffix
        );
      }
    }
  }
}
 
 
var g = permutations([1, 3, 2]);
 
g.next().value; // [1, 3, 2]
g.next().value; // [3, 1, 2]
g.next().value; // [3, 2, 1]
g.next().value; // [1, 2, 3]
g.next().value; // [2, 1, 3]
g.next().value; // [2, 3, 1]
g.next().done; // true
 
 
How hard could it be to translate this function into one that no longer contains function* or yield?

What about something trickier?

function *permutations(list) {
  if (list.length < 2) {
    yield list;
  } else {
    var first = list.slice(0, 1);
    var ps = permutations(list.slice(1));
    for (var p of ps) {
      for (var i = 0; i < list.length; ++i) {
        yield Array.prototype.concat.call(
          p.slice(0, i), // prefix
          first,         // middle
          p.slice(i)     // suffix
        );
      }
    }
  }
}
 
 
var g = permutations([1, 3, 2]);
var count = 0;
 
for (var p of g) {
  console.log(p);
  ++count;
}
 
console.log(count); // 6
 
 
How hard could it be to translate this function into one that no longer contains function* or yield?

I've been jokingly referring to this side project as "my life's work" for so long that I'm terrified it might actually be finished soon.

— Ben Newman (@benjamn) May 8, 2013

It's the trickiest code I've ever written.

function permutations(list) {
  if (list.length < 2) {
    yield list;
  } else {
    var first = list.slice(0, 1);
    var ps = permutations(list.slice(1));
    for (var p of ps) {
      for (var i = 0; i < list.length; ++i) {
        yield Array.prototype.concat.call(
          p.slice(0, i), // prefix
          first,         // middle
          p.slice(i)     // suffix
        );
      }
    }
  }
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
function permutations(list) {
  var first, ps, p, i;
 
  if (list.length < 2) {
    yield list;
  } else {
    first = list.slice(0, 1);
    ps = permutations(list.slice(1));
    for (p of ps) {
      for (i = 0; i < list.length; ++i) {
        yield Array.prototype.concat.call(
          p.slice(0, i), // prefix
          first,         // middle
          p.slice(i)     // suffix
        );
      }
    }
  }
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
function permutations(list) {
  var first, ps, p, t$0, t$1, i;
 
  if (list.length < 2) {
    yield list;
  } else {
    first = list.slice(0, 1);
    ps = permutations(list.slice(1));
    for (p of ps) {
      for (i = 0; i < list.length; ++i) {
        yield Array.prototype.concat.call(
          p.slice(0, i), // prefix
          first,         // middle
          p.slice(i)     // suffix
        );
      }
    }
  }
}
 
 
 
 
 
            
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
function permutations(list) {
  var first, ps, p, t$0, t$1, i;
 
  if (list.length < 2) {
    yield list;
  } else {
    first = list.slice(0, 1);
    ps = permutations(list.slice(1));
    t$0 = wrapGenerator.values(ps);
    while (!(t$1 = t$0.next()).done) {
      p = t$1.value;
      for (i = 0; i < list.length; ++i) {
        yield Array.prototype.concat.call(
          p.slice(0, i), // prefix
          first,         // middle
          p.slice(i)     // suffix
        );
      }
    }
  }
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
function permutations(list) {
  var first, ps, p, t$0, t$1, i;
 
  return function*() {
    if (list.length < 2) {
      yield list;
    } else {
      first = list.slice(0, 1);
      ps = permutations(list.slice(1));
      t$0 = wrapGenerator.values(ps);
      while (!(t$1 = t$0.next()).done) {
        p = t$1.value;
        for (i = 0; i < list.length; ++i) {
          yield Array.prototype.concat.call(
            p.slice(0, i), // prefix
            first,         // middle
            p.slice(i)     // suffix
          );
        }
      }
    }
  };
}
 
            
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
function permutations(list) {
  var first, ps, p, t$0, t$1, i;
 
  return function(context) {
    while (1) switch (context.next) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
            
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
    }
  };
}
 
 
function permutations(list) {
  var first, ps, p, t$0, t$1, i;
 
  return function(context) {
    while (1) switch (context.next) {
    case 0:
      if (!(list.length < 2)) {
        context.next = 5;
        break;
      }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
            
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
    }
  };
}
 
 
function permutations(list) {
  var first, ps, p, t$0, t$1, i;
 
  return function(context) {
    while (1) switch (context.next) {
    case 0:
      if (!(list.length < 2)) {
        context.next = 5;
        break;
      }
      context.next = 3;
      return list;
 
 
 
 
 
 
 
 
 
 
 
 
            
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
    }
  };
}
 
 
function permutations(list) {
  var first, ps, p, t$0, t$1, i;
 
  return function(context) {
    while (1) switch (context.next) {
    case 0:
      if (!(list.length < 2)) {
        context.next = 5;
        break;
      }
      context.next = 3;
      return list;
    case 3:
      context.next = 19;
      break;
 
 
 
 
 
 
 
 
 
            
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
    }
  };
}
 
 
function permutations(list) {
  var first, ps, p, t$0, t$1, i;
 
  return function(context) {
    while (1) switch (context.next) {
    case 0:
      if (!(list.length < 2)) {
        context.next = 5;
        break;
      }
      context.next = 3;
      return list;
    case 3:
      context.next = 19;
      break;
 
 
 
 
 
 
 
 
 
            
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
    case 19:
    case "end":
      return context.stop();
    }
  };
}
 
 
function permutations(list) {
  var first, ps, p, t$0, t$1, i;
 
  return function(context) {
    while (1) switch (context.next) {
    case 0:
      if (!(list.length < 2)) {
        context.next = 5;
        break;
      }
      context.next = 3;
      return list;
    case 3:
      context.next = 19;
      break;
    case 5:
      first = list.slice(0, 1);
      ps = permutations(list.slice(1));
      t$0 = wrapGenerator.values(ps);
 
 
 
 
 
            
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
    case 19:
    case "end":
      return context.stop();
    }
  };
}
 
 
function permutations(list) {
  var first, ps, p, t$0, t$1, i;
 
  return function(context) {
    while (1) switch (context.next) {
    case 0:
      if (!(list.length < 2)) {
        context.next = 5;
        break;
      }
      context.next = 3;
      return list;
    case 3:
      context.next = 19;
      break;
    case 5:
      first = list.slice(0, 1);
      ps = permutations(list.slice(1));
      t$0 = wrapGenerator.values(ps);
    case 8:
      if ((t$1 = t$0.next()).done) {
        context.next = 19;
        break;
      }
      p = t$1.value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
    case 19:
    case "end":
      return context.stop();
    }
  };
}
 
 
function permutations(list) {
  var first, ps, p, t$0, t$1, i;
 
  return function(context) {
    while (1) switch (context.next) {
    case 0:
      if (!(list.length < 2)) {
        context.next = 5;
        break;
      }
      context.next = 3;
      return list;
    case 3:
      context.next = 19;
      break;
    case 5:
      first = list.slice(0, 1);
      ps = permutations(list.slice(1));
      t$0 = wrapGenerator.values(ps);
    case 8:
      if ((t$1 = t$0.next()).done) {
        context.next = 19;
        break;
      }
      p = t$1.value;
 
 
      i = 0;
    case 11:
      if (!(i < list.length)) {
        context.next = 17;
        break;
      }
 
 
 
 
 
 
 
 
 
 
 
 
 
    case 19:
    case "end":
      return context.stop();
    }
  };
}
 
 
function permutations(list) {
  var first, ps, p, t$0, t$1, i;
 
  return function(context) {
    while (1) switch (context.next) {
    case 0:
      if (!(list.length < 2)) {
        context.next = 5;
        break;
      }
      context.next = 3;
      return list;
    case 3:
      context.next = 19;
      break;
    case 5:
      first = list.slice(0, 1);
      ps = permutations(list.slice(1));
      t$0 = wrapGenerator.values(ps);
    case 8:
      if ((t$1 = t$0.next()).done) {
        context.next = 19;
        break;
      }
      p = t$1.value;
 
 
      i = 0;
    case 11:
      if (!(i < list.length)) {
        context.next = 17;
        break;
      }
      context.next = 14;
      return Array.prototype.concat.call(
        p.slice(0, i), // prefix
        first,         // middle
        p.slice(i)     // suffix
      );
 
 
 
 
 
 
 
    case 19:
    case "end":
      return context.stop();
    }
  };
}
 
 
function permutations(list) {
  var first, ps, p, t$0, t$1, i;
 
  return function(context) {
    while (1) switch (context.next) {
    case 0:
      if (!(list.length < 2)) {
        context.next = 5;
        break;
      }
      context.next = 3;
      return list;
    case 3:
      context.next = 19;
      break;
    case 5:
      first = list.slice(0, 1);
      ps = permutations(list.slice(1));
      t$0 = wrapGenerator.values(ps);
    case 8:
      if ((t$1 = t$0.next()).done) {
        context.next = 19;
        break;
      }
      p = t$1.value;
 
 
      i = 0;
    case 11:
      if (!(i < list.length)) {
        context.next = 17;
        break;
      }
      context.next = 14;
      return Array.prototype.concat.call(
        p.slice(0, i), // prefix
        first,         // middle
        p.slice(i)     // suffix
      );
    case 14:
      ++i;
      context.next = 11;
      break;
 
 
 
    case 19:
    case "end":
      return context.stop();
    }
  };
}
 
 
function permutations(list) {
  var first, ps, p, t$0, t$1, i;
 
  return function(context) {
    while (1) switch (context.next) {
    case 0:
      if (!(list.length < 2)) {
        context.next = 5;
        break;
      }
      context.next = 3;
      return list;
    case 3:
      context.next = 19;
      break;
    case 5:
      first = list.slice(0, 1);
      ps = permutations(list.slice(1));
      t$0 = wrapGenerator.values(ps);
    case 8:
      if ((t$1 = t$0.next()).done) {
        context.next = 19;
        break;
      }
      p = t$1.value;
 
 
      i = 0;
    case 11:
      if (!(i < list.length)) {
        context.next = 17;
        break;
      }
      context.next = 14;
      return Array.prototype.concat.call(
        p.slice(0, i), // prefix
        first,         // middle
        p.slice(i)     // suffix
      );
    case 14:
      ++i;
      context.next = 11;
      break;
    case 17:
      context.next = 8;
      break;
    case 19:
    case "end":
      return context.stop();
    }
  };
}
 
 
function permutations(list) {
  var first, ps, p, t$0, t$1, i;
 
  return wrapGenerator(function(context) {
    while (1) switch (context.next) {
    case 0:
      if (!(list.length < 2)) {
        context.next = 5;
        break;
      }
      context.next = 3;
      return list;
    case 3:
      context.next = 19;
      break;
    case 5:
      first = list.slice(0, 1);
      ps = permutations(list.slice(1));
      t$0 = wrapGenerator.values(ps);
    case 8:
      if ((t$1 = t$0.next()).done) {
        context.next = 19;
        break;
      }
      p = t$1.value;
 
 
      i = 0;
    case 11:
      if (!(i < list.length)) {
        context.next = 17;
        break;
      }
      context.next = 14;
      return Array.prototype.concat.call(
        p.slice(0, i), // prefix
        first,         // middle
        p.slice(i)     // suffix
      );
    case 14:
      ++i;
      context.next = 11;
      break;
    case 17:
      context.next = 8;
      break;
    case 19:
    case "end":
      return context.stop();
    }
  }, permutations, this);
}
 
 

ForStatement code generation

  case "ForStatement":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  case "ForStatement":
    var head = loc();
    var update = loc();
    var after = loc();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  case "ForStatement":
    var head = loc();
    var update = loc();
    var after = loc();
 
    if (stmt.init) {
      self.explode(path.get("init"), true);
    }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  case "ForStatement":
    var head = loc();
    var update = loc();
    var after = loc();
 
    if (stmt.init) {
      self.explode(path.get("init"), true);
    }
 
    self.mark(head);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  case "ForStatement":
    var head = loc();
    var update = loc();
    var after = loc();
 
    if (stmt.init) {
      self.explode(path.get("init"), true);
    }
 
    self.mark(head);
 
    if (stmt.test) {
      self.jumpIfNot(self.explodeExpression(path.get("test")), after);
    }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  case "ForStatement":
    var head = loc();
    var update = loc();
    var after = loc();
 
    if (stmt.init) {
      self.explode(path.get("init"), true);
    }
 
    self.mark(head);
 
    if (stmt.test) {
      self.jumpIfNot(self.explodeExpression(path.get("test")), after);
    }
 
    self.leapManager.withEntry(
      new leap.LoopEntry(after, update, labelId),
      function() { self.explodeStatement(path.get("body")); }
    );
 
 
 
 
 
 
 
 
 
 
 
 
  case "ForStatement":
    var head = loc();
    var update = loc();
    var after = loc();
 
    if (stmt.init) {
      self.explode(path.get("init"), true);
    }
 
    self.mark(head);
 
    if (stmt.test) {
      self.jumpIfNot(self.explodeExpression(path.get("test")), after);
    }
 
    self.leapManager.withEntry(
      new leap.LoopEntry(after, update, labelId),
      function() { self.explodeStatement(path.get("body")); }
    );
 
    self.mark(update);
 
 
 
 
 
 
 
 
 
 
  case "ForStatement":
    var head = loc();
    var update = loc();
    var after = loc();
 
    if (stmt.init) {
      self.explode(path.get("init"), true);
    }
 
    self.mark(head);
 
    if (stmt.test) {
      self.jumpIfNot(self.explodeExpression(path.get("test")), after);
    }
 
    self.leapManager.withEntry(
      new leap.LoopEntry(after, update, labelId),
      function() { self.explodeStatement(path.get("body")); }
    );
 
    self.mark(update);
 
    if (stmt.update) {
      self.explode(path.get("update"), true);
    }
 
 
 
 
 
 
  case "ForStatement":
    var head = loc();
    var update = loc();
    var after = loc();
 
    if (stmt.init) {
      self.explode(path.get("init"), true);
    }
 
    self.mark(head);
 
    if (stmt.test) {
      self.jumpIfNot(self.explodeExpression(path.get("test")), after);
    }
 
    self.leapManager.withEntry(
      new leap.LoopEntry(after, update, labelId),
      function() { self.explodeStatement(path.get("body")); }
    );
 
    self.mark(update);
 
    if (stmt.update) {
      self.explode(path.get("update"), true);
    }
 
    self.jump(head);
 
 
 
 
  case "ForStatement":
    var head = loc();
    var update = loc();
    var after = loc();
 
    if (stmt.init) {
      self.explode(path.get("init"), true);
    }
 
    self.mark(head);
 
    if (stmt.test) {
      self.jumpIfNot(self.explodeExpression(path.get("test")), after);
    }
 
    self.leapManager.withEntry(
      new leap.LoopEntry(after, update, labelId),
      function() { self.explodeStatement(path.get("body")); }
    );
 
    self.mark(update);
 
    if (stmt.update) {
      self.explode(path.get("update"), true);
    }
 
    self.jump(head);
    self.mark(after);
 
 
 
  case "ForStatement":
    var head = loc();
    var update = loc();
    var after = loc();
 
    if (stmt.init) {
      self.explode(path.get("init"), true);
    }
 
    self.mark(head);
 
    if (stmt.test) {
      self.jumpIfNot(self.explodeExpression(path.get("test")), after);
    }
 
    self.leapManager.withEntry(
      new leap.LoopEntry(after, update, labelId),
      function() { self.explodeStatement(path.get("body")); }
    );
 
    self.mark(update);
 
    if (stmt.update) {
      self.explode(path.get("update"), true);
    }
 
    self.jump(head);
    self.mark(after);
    break;
 
 

loc() and this.mark(loc)

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

loc() and this.mark(loc)

// Offsets into this.listing that could be used as targets for branches or
// jumps are represented as numeric Literal nodes.
function loc() {
  return { type: "Literal", value: -1 };
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

loc() and this.mark(loc)

// Offsets into this.listing that could be used as targets for branches or
// jumps are represented as numeric Literal nodes.
function loc() {
  return require("recast").types.builders.literal(-1);
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

loc() and this.mark(loc)

// Offsets into this.listing that could be used as targets for branches or
// jumps are represented as numeric Literal nodes.
function loc() {
  return b.literal(-1);
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

loc() and this.mark(loc)

// Offsets into this.listing that could be used as targets for branches or
// jumps are represented as numeric Literal nodes.
function loc() {
  return b.literal(-1);
}
 
// Sets the exact value of the given location to the offset of the next
// Statement emitted.
Emitter.prototype.mark = function(loc) {
  assert.strictEqual(loc.type, "Literal");
  var index = this.listing.length;
  if (loc.value === -1) {
    loc.value = index;
  } else {
    // Locations can be marked redundantly, but their values cannot change
    // once set the first time.
    assert.strictEqual(loc.value, index);
  }
  this.marked[index] = true;
  return loc;
};
 
 

loc() and this.mark(loc)

// Offsets into this.listing that could be used as targets for branches or
// jumps are represented as numeric Literal nodes.
function loc() {
  return b.literal(-1);
}
 
// Sets the exact value of the given location to the offset of the next
// Statement emitted.
Emitter.prototype.mark = function(loc) {
  require("recast").types.namedTypes.Literal.assert(loc);
  var index = this.listing.length;
  if (loc.value === -1) {
    loc.value = index;
  } else {
    // Locations can be marked redundantly, but their values cannot change
    // once set the first time.
    assert.strictEqual(loc.value, index);
  }
  this.marked[index] = true;
  return loc;
};
 
 

loc() and this.mark(loc)

// Offsets into this.listing that could be used as targets for branches or
// jumps are represented as numeric Literal nodes.
function loc() {
  return b.literal(-1);
}
 
// Sets the exact value of the given location to the offset of the next
// Statement emitted.
Emitter.prototype.mark = function(loc) {
  n.Literal.assert(loc);
  var index = this.listing.length;
  if (loc.value === -1) {
    loc.value = index;
  } else {
    // Locations can be marked redundantly, but their values cannot change
    // once set the first time.
    assert.strictEqual(loc.value, in