I'm trying to understand the behavior of iterables in Firefox Nightly and V8. Specifically, I am looking at how the runtime is supposed to behave when the iterable's Symbol.iterator
method returns a non-object value. Here are the relevant parts of the specification (as I understand it):
13.6.4.13 Runtime Semantics: ForIn/OfBodyEvaluation
The abstract operation ForIn/OfBodyEvaluation is called with arguments lhs, stmt, iterator, lhsKind, and labelSet. The value of lhsKind is either assignment, varBinding or lexicalBinding.
- Let oldEnv be the running execution context’s LexicalEnvironment.
- Let V = undefined .
- Let destructuring be IsDestructuring of lhs.
- If destructuring is true and if lhsKind is assignment, then
- Assert: lhs is a LeftHandSideExpression.
- Let assignmentPattern be the parse of the source code corresponding to lhs using AssignmentPattern as the goal symbol.
- Repeat
- If lhsKind is either assignment or varBinding, then
- If destructuring is false, then
- Let lhsRef be the result of evaluating lhs ( it may be evaluated repeatedly).
- ReturnIfAbrupt(lhsRef).
- Else
- Assert: lhsKind is lexicalBinding.
- Assert: lhs is a ForDeclaration.
- Let iterationEnv be NewDeclarativeEnvironment(oldEnv).
- Set the running execution context’s LexicalEnvironment to iterationEnv.
- Perform BindingInstantiation for lhs passing iterationEnv as the argument.
- If destructuring is false, then
- Assert: lhs binds a single name.
- Let lhsName be the sole element of BoundNames of lhs.
- Let lhsRef be ResolveBinding(the sole element of lhs).
- Assert: lhsRef is not an abrupt completion.
- Let nextResult be IteratorStep(iterator).
- If nextResult is an abrupt completion, then
- Set the running execution context’s LexicalEnvironment to oldEnv.
- Return nextResult.
- If nextResult is false, then
- Set the running execution context’s LexicalEnvironment to oldEnv.
- Return NormalCompletion(V).
- Let nextValue be IteratorValue(nextResult).
- If nextValue is an abrupt completion, then
- Set the running execution context’s LexicalEnvironment to oldEnv.
- Return nextValue.
- If destructuring is false, then
- If lhsKind is lexicalBinding, then
- Let status be InitializeReferencedBinding(lhsRef, nextValue).
- Else,
- Let status be PutValue(lhsRef, nextValue).
- Else,
- If lhsKind is assignment, then
- Let status be the result of performing DestructuringAssignmentEvaluation of assignmentPattern using nextValue as the argument.
- Else if lhsKind is varBinding, then
- Assert: lhs is a ForBinding.
- Let status be the result of performing BindingInitialization for lhs passing nextValue and undefined as the arguments.
- Else,
- Assert: lhsKind is lexicalBinding.
- Assert: lhs is a ForDeclaration.
- Let status be the result of performing BindingInitialization for lhs passing nextValue and iterationEnv as arguments.
- If status is an abrupt completion, then
- Set the running execution context’s LexicalEnvironment to oldEnv.
- Return IteratorClose(iterator, status).
- Let status be the result of evaluating stmt.
- If status.[[type]] is normal and status.[[value]] is not empty, then
- Let V = status.[[value]].
- Set the running execution context’s LexicalEnvironment to oldEnv.
- If LoopContinues(status,labelSet) is false, then
- Return IteratorClose(iterator, status).
7.4.5 IteratorStep ( iterator )
The abstract operation IteratorStep with argument iterator requests the next value from iterator and returns either false indicating that the iterator has reached its end or the IteratorResult object if a next value is available. IteratorStep performs the following steps:
- Let result be IteratorNext(iterator).
- ReturnIfAbrupt(result).
- Let done be IteratorComplete(result).
- ReturnIfAbrupt(done).
- If done is true, return false.
- Return result.
7.4.2 IteratorNext ( iterator, value )
The abstract operation IteratorNext with argument iterator and optional argument value performs the following steps:
- If value was not passed, then
- Let result be Invoke(iterator,
"next"
, « »).- Else,
- Let result be Invoke(iterator,
"next"
, «value»).- ReturnIfAbrupt(result).
- If Type(result) is not Object, throw a TypeError exception.
- Return result.
As I read it, if the iterable's Symbol.iterator
method returns a non-object value (e.g. the number literal 23
), then I think a TypeError
should be raised according to 7.4.2 (step 4). Both Firefox Nightly and V8 treat this value as though it were an object--value
is interpreted as undefined
and done
is interpreted as false
, so the following test passes:
var iterable = {};
var i, firstIterResult;
iterable[Symbol.iterator] = function() {
var finalIterResult = { value: null, done: true };
var nextIterResult = firstIterResult;
return {
next: function() {
var iterResult = nextIterResult;
nextIterResult = finalIterResult;
return iterResult;
}
};
};
firstIterResult = 23;
i = 0;
for (var x of iterable) {
assert.sameValue(x, undefined);
i++;
}
assert.sameValue(i, 1);
but that seems wrong to me. I think any non-object value returned from an iterable's Symbol.iterator
method should result in a TypeError
during for..of
iteration.