Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

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

Open
michaelficarra opened this issue Nov 19, 2024 · 6 comments · May be fixed by #18
Open

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

michaelficarra opened this issue Nov 19, 2024 · 6 comments · May be 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

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

Successfully merging a pull request may close this issue.

4 participants