yield * broken for Firefox < 45
See original GitHub issueSo 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
StopIteration
exception 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!
Issue Analytics
- State:
- Created 7 years ago
- Reactions:1
- Comments:5 (2 by maintainers)
I’m also able to reproduce this in Firefox 42 with the above code, but not in the sandbox page, which is a little odd to me. There’s nothing else loading in the test page other than the regenerator runtime. (I’m also running into this in a big way in one of my projects). Stepping through the code, I’ve noticed the following happening:
info
object that is returned when invoking the “next” method of the delegate/inner generator comes back with a value oftryCatch
function, it’s somehow mutated to become<return>
value at this point in the code: the value looks correct (i.e. not nested). But by the time we make it here (which at least in the debugger is just the parent in the call stack) thearg
value has become nested as show above:If I hack the
tryCatch
function like sothen things work as expected, but this is obviously just masking the problem, not getting to the root of it. Hope this is somehow helpful. I’d be glad to help further to get to the bottom of this however I can.
A bit more info on this – try running the browser tests (from
test/index.html
) in FF < 45, and things start to fail (they’re fine in more recent Firefox and in Chome):I should have a PR coming shortly with what appears to be a fix using specific (mis)feature-detection. I say “appears” because it makes the browser tests pass in FF 42, at least 😃