Skip to content
This repository was archived by the owner on Feb 1, 2025. It is now read-only.
This repository was archived by the owner on Feb 1, 2025. It is now read-only.

yield * broken for Firefox < 45 #274

@intentionally-left-nil

Description

@intentionally-left-nil

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.

  1. Firefox used to support some alternative form of the iterator.
  2. 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.
  3. Specifically, note that LegacyIteratorNext always returns done: false
  4. 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);
  1. 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!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions