« Bugzilla Issues Index

#1633 — Error recovery in delegating yield incomplete

Runtime Semantics: Evaluation for "YieldExpression : yield YieldDelegator opt AssignmentExpression", step 4.d.viii. In case of an error, the "throw" handler is called (4.d.viii.1), but then the original error is still propagated, cf. step 4.d.viii.2.

The original delegating yield proposal included error recovery, but this isn't currently spec'ed ( Is this an oversight or an intended change?

test case
function* gen(){ print("start"); yield 1; print("stop") }
gen.prototype.throw = function(e){ print("caught: " + e); return {done: false} }
let g = function*(){ yield*gen(); }();; g.throw("Stop!");;

expected output with error recovery:
caught: Stop!

current output:
caught: Stop!
uncaught exception: Stop!

Yep, I just independently found the same issue.

Maybe the spec text we want is like this:

4. ...
c. Let status be NormalCompletion(undefined).
d. Repeat
i. Let delegateResult be IteratorDelegate(iterator, status).
ii. If delegateResult.[[type]] is return, then
1. Return NormalCompletion(delegateResult.[[value]]).
iii. ReturnIfAbrupt(delegateResult).
iv. Let status be GeneratorYield(delegateResult).

emphasizing that we're just passing Completion values back and forth between the caller and the delegate iterator. The abstract operation IteratorDelegate(iterator, status) can be defined like this:

1. If status.[[type]] is normal, then
a. Let result be IteratorNext(iterator, status.[[value]]).
b. ReturnIfAbrupt(methodCompletion).
2. Else,
a. Assert: status.[[type]] is throw.
b. If HasProperty(iterator, "throw") is true, then
i. Let result = Invoke(iterator, "throw", received).
ii. ReturnIfAbrupt(result).
iii. If Type(result.[[value]]) is not Object, throw a
TypeError exception.
c. Else,
i. Return status.
3. Let done = IteratorComplete(result).
4. ReturnIfAbrupt(done).
5. Let value = IteratorValue(result).
6. ReturnIfAbrupt(value).
7. If done is true, then
a. Return {[[type]]: return, [[value]]: value, [[target]]: empty}.
8. Return NormalCompletion(result).

(In reply to comment #1)
> Yep, I just independently found the same issue.

Me too. Would be great to fix this soon.

fixed in rev21 editor's draft
approximately like Jason's suggestion but with IteratorDelegate inlined.

BTW, you guys should feel free to set the importance field appropriately high on any bugs you need a quick response to.

1. Let exprRef be the result of evaluating AssignmentExpression.
2. Let value be GetValue(exprRef).
3. ReturnIfAbrupt(value).
4. Let iterator be the result of GetIterator(value).
5. ReturnIfAbrupt(iterator).
6. Let received be NormalCompletion(undefined).
7. Repeat
a. If received.[[type]] is normal, then
i. Let innerResult be the result of IteratorNext(iterator, received.[[value]]).
ii. ReturnIfAbrupt(innerResult).
b. Else
i. Asssert: received.[[type]] is throw.
ii. If HasProperty(iterator, "throw") is true, then
1. Let innerResult be the result of Invoke(iterator, "throw", (received.[[value]])).
2. ReturnIfAbrupt(innerResult).
3. If Type(innerResult) is not Object, then throw a TypeError exception.
iii. Else, return received.
c. Let done be IteratorComplete(innerResult).
d. ReturnIfAbrupt(done).
e. If done is true, then
i. Return IteratorValue (innerResult).
f. Let received be the result of GeneratorYield(innerResult).

fixed in rev21 draft