Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

re-use (done: false) IteratorResult objects yielded by underlying iterator #17

Closed
michaelficarra opened this issue Nov 19, 2024 · 6 comments · Fixed by #18
Closed

re-use (done: false) IteratorResult objects yielded by underlying iterator #17

michaelficarra opened this issue Nov 19, 2024 · 6 comments · Fixed by #18

Comments

@michaelficarra
Copy link
Member

Taken from the tests PR: https://github.com/tc39/test262/pull/4326/files#diff-667834d670268d2d741d9c1331df51d617cb4281fd9a46f785af4f43cc1ff256R49.

let oldIterResult = {
  done: false,
  value: 123,
};

let testIterator = {
  next() {
    return oldIterResult;
  }
};

let iterable = {
  [Symbol.iterator]() {
    return testIterator;
  }
};

let iterator = Iterator.concat(iterable);

let iterResult = iterator.next();

assert.sameValue(iterResult.done, false);
assert.sameValue(iterResult.value, 123);

assert.notSameValue(iterResult, oldIterResult);

This seems really wasteful. Would there be any harm in re-using these IteratorResult objects from the underlying iterator instead of constructing them anew?

@bakkot
Copy link
Collaborator

bakkot commented Nov 19, 2024

It's kind of weird to do this given that we don't do it for yield*, which is explicitly the "forward the result" operator.

I'm not totally opposed but it isn't obvious that this would be a perf improvement anyway, because now engines can't assume that the result of Iterator.concat is itself a well-behaved iterator result object. I have a doc about how engines could skip the allocation of these objects for built-in iterators in the common case of consuming the result as an iterable, but you wouldn't be able to do that optimization for Iterator.concat if we made this change.

@zloirock
Copy link

Iterator.prototype.{ take, drop, filter }

@michaelficarra
Copy link
Member Author

Assuming we do this, we should also do this (as a follow-up) for yield* and Iterator.prototype.{take,drop,filter}. We shouldn't blindly follow their precedent here.

@anba
Copy link

anba commented Nov 20, 2024

It's kind of weird to do this given that we don't do it for yield*, which is explicitly the "forward the result" operator.

yield* forwards the iterator result object. https://tc39.es/ecma262/#sec-generator-function-definitions-runtime-semantics-evaluation:

i. Let innerResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « received.[[Value]] »).
...
v. If done is true, then
...
vi. If generatorKind is async, set received to Completion(AsyncGeneratorYield(? IteratorValue(innerResult))).
vii. Else, set received to Completion(GeneratorYield(innerResult)).

It's maybe possible to change yield* to not forward the iterator result object, because JSC already has this behaviour: https://bugs.webkit.org/show_bug.cgi?id=174756.

@bakkot
Copy link
Collaborator

bakkot commented Nov 20, 2024

Ah jeeze, that's what I get for testing in a browser instead of actually reading the spec.

@michaelficarra
Copy link
Member Author

I opened a needs-consensus PR for 262: tc39/ecma262#3489

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants