-
Notifications
You must be signed in to change notification settings - Fork 1.1k
yield * broken for Firefox < 45 #274
Description
So this is a fun bug.
Try running this code through regenerator on FF 44 or below:
function *inner() { yield 1; }
function *outer() { yield *inner(); }
function runMe() {
var g = outer();
console.log(g.next());
console.log(g.next());
console.log(g.next());
}
You'll note that the generator never reports done as true. Here are the notes from my investigation. I am not 100% of the reason because some of the macro code in gecko is not easy to parse.
- Firefox used to support some alternative form of the iterator.
- It looks like they implemented the ES2015 version in FF 27, but then had some backcompat work to detect when code was trying to use the old protocol.
- Specifically, note that LegacyIteratorNext always returns done: false
- Now note that any delegate relies on
next()working in order to iterate through the delegate
function values(iterable) {
if (iterable) {
var iteratorMethod = iterable[iteratorSymbol];
if (iteratorMethod) {
return iteratorMethod.call(iterable);
}
where iteratorSymbol in this case is Symbol.iterator
5. The scene is almost set. wrap uses the Generator.prototype to create its functions:
function wrap(innerFn, outerFn, self, tryLocsList) {
// If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.
var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;
var generator = Object.create(protoGenerator.prototype);
- And lastly (this is where some educated amount of speculation comes in), it looks like the iterator methods on the Generator prototype in FF < 45 are completely busted.
So, putting it all together, this is what I believe happens:
Due to the bad Generator prototype, firefox decides that the code trying to get the iterator in values() is legacy and shoves it down the legacy iterator codepath. This means that next will ALWAYS return done: false unless a StopIteration exception is thrown.
The current code does not do such a thing and therefore the generator never ends. It just keeps on going on my friends....
So: What's the fix? It looks like you have a few options:
- Don't use the generator prototype for these builds of firefox
- Polyfill
next()and related iterator methods for these builds of firefox (note that is what we chose to do to fix this bug) - Write some code that does browser detection and throws a
StopIterationexception instead - Convince FF some other way to give you the normal codepath. I was able to do this by using Symbol["@@iterator"] instead of Symbol.iterator. However it appears that is fraught with peril.
tl;dr: yield * never worked on at the very least Firefox 44 and probably several builds earlier than that. It's a comedy of browser issues that you now get to work around. Thanks!