Bug 907077: Change for-of to conform to latest ES6 specification. r=jwalden

--HG--
extra : rebase_source : 93c5b9d6a28a42468d9ca9ac7da3b4e1b6d92820
This commit is contained in:
Andy Wingo 2013-10-01 06:25:00 +02:00
parent b26ec8cdeb
commit 28a193c95f
102 changed files with 1066 additions and 546 deletions

View File

@ -52,7 +52,7 @@ is(names[8], "z", "Entry 9")
is(names[9], "something", "Entry 10")
is(names[10], "item", "Entry 11")
is(names[11], "namedItem", "Entry 12")
is(names[12], "iterator", "Entry 13")
is(names[12], "@@iterator", "Entry 13")
is(names[13], "length", "Entry 14")
</script>
</pre>

View File

@ -40,7 +40,7 @@ is(names[7], "w", "Entry 8")
is(names[8], "something", "Entry 9")
is(names[9], "item", "Entry 10")
is(names[10], "namedItem", "Entry 11")
is(names[11], "iterator", "Entry 12")
is(names[11], "@@iterator", "Entry 12")
is(names[12], "length", "Entry 13")
</script>
</pre>

View File

@ -52,7 +52,7 @@ is(names[9], "w", "Entry 10")
is(names[10], "something", "Entry 11")
is(names[11], "item", "Entry 12")
is(names[12], "namedItem", "Entry 13")
is(names[13], "iterator", "Entry 14")
is(names[13], "@@iterator", "Entry 14")
is(names[14], "length", "Entry 15")
</script>
</pre>

View File

@ -1514,9 +1514,9 @@ class MethodDefiner(PropertyDefiner):
# FIXME Check for an existing iterator on the interface first.
if any(m.isGetter() and m.isIndexed() for m in methods):
self.regular.append({"name": 'iterator',
self.regular.append({"name": "@@iterator",
"methodInfo": False,
"nativeName": "JS_ArrayIterator",
"selfHostedName": "ArrayIterator",
"length": 0,
"flags": "JSPROP_ENUMERATE",
"condition": MemberCondition(None, None) })

View File

@ -320,7 +320,7 @@ class IDLUnresolvedIdentifier(IDLObject):
[location])
if name[0] == '_' and not allowDoubleUnderscore:
name = name[1:]
if (name in ["constructor", "iterator", "toString", "toJSON"] and
if (name in ["constructor", "toString", "toJSON"] and
not allowForbidden):
raise WebIDLError("Cannot use reserved identifier '%s'" % (name),
[location])

View File

@ -50,20 +50,12 @@ function runTestsForDocument(document, msgSuffix) {
}
is(log, "egg0;egg1;egg2;egg3;", "'for (x of div.childNodes)' should see elements added during iteration");
var iter1 = basket.childNodes.iterator();
var iter2 = basket.childNodes.iterator();
isnot(iter1, iter2, "nodelist.iterator() returns a new iterator each time");
log = '';
basket.appendChild(document.createTextNode("some text"));
for (var x of basket.children)
log += x.id + ";";
is(log, "egg0;egg1;egg2;egg3;", "'for (x of div.children)' should iterate over child elements");
var iter1 = basket.children.iterator();
var iter2 = basket.children.iterator();
isnot(iter1, iter2, ".iterator() returns a new iterator each time");
var count = 0;
for (var x of document.getElementsByClassName("hazardous-materials"))
count++;

View File

@ -648,6 +648,7 @@ selfhosting_srcs := \
$(srcdir)/builtin/Date.js \
$(srcdir)/builtin/Intl.js \
$(srcdir)/builtin/IntlData.js \
$(srcdir)/builtin/Iterator.js \
$(srcdir)/builtin/Number.js \
$(srcdir)/builtin/ParallelArray.js \
$(srcdir)/builtin/String.js \

View File

@ -0,0 +1,97 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function IteratorIdentity() {
return this;
}
var LegacyIteratorWrapperMap = new std_WeakMap();
function IteratorResult(value, done) {
var result = std_Object_create(null);
result.value = value;
result.done = done;
return result;
}
function LegacyIteratorNext(arg) {
var iter = callFunction(std_WeakMap_get, LegacyIteratorWrapperMap, this);
try {
return IteratorResult(iter.next(arg), false);
} catch (e) {
if (e instanceof std_StopIteration)
return IteratorResult(undefined, true);
throw e;
}
}
function LegacyIteratorThrow(exn) {
var iter = callFunction(std_WeakMap_get, LegacyIteratorWrapperMap, this);
try {
return IteratorResult(iter.throw(exn), false);
} catch (e) {
if (e instanceof std_StopIteration)
return IteratorResult(undefined, true);
throw e;
}
}
function LegacyIterator(iter) {
callFunction(std_WeakMap_set, LegacyIteratorWrapperMap, this, iter);
}
function LegacyGeneratorIterator(iter) {
callFunction(std_WeakMap_set, LegacyIteratorWrapperMap, this, iter);
}
var LegacyIteratorsInitialized = std_Object_create(null);
function InitLegacyIterators() {
var props = std_Object_create(null);
props.next = std_Object_create(null);
props.next.value = LegacyIteratorNext;
props.next.enumerable = false;
props.next.configurable = true;
props.next.writable = true;
props[std_iterator] = std_Object_create(null);
props[std_iterator].value = IteratorIdentity;
props[std_iterator].enumerable = false;
props[std_iterator].configurable = true;
props[std_iterator].writable = true;
var LegacyIteratorProto = std_Object_create(GetIteratorPrototype(), props);
MakeConstructible(LegacyIterator, LegacyIteratorProto);
props.throw = std_Object_create(null);
props.throw.value = LegacyIteratorThrow;
props.throw.enumerable = false;
props.throw.configurable = true;
props.throw.writable = true;
var LegacyGeneratorIteratorProto = std_Object_create(GetIteratorPrototype(), props);
MakeConstructible(LegacyGeneratorIterator, LegacyGeneratorIteratorProto);
LegacyIteratorsInitialized.initialized = true;
}
function NewLegacyIterator(iter, wrapper) {
if (!LegacyIteratorsInitialized.initialized)
InitLegacyIterators();
return new wrapper(iter);
}
function LegacyIteratorShim() {
return NewLegacyIterator(ToObject(this), LegacyIterator);
}
function LegacyGeneratorIteratorShim() {
return NewLegacyIterator(ToObject(this), LegacyGeneratorIterator);
}
function ArrayIterator() {
return NewLegacyIterator(callFunction(std_Array_iterator, this), LegacyIterator);
}

View File

@ -24,13 +24,10 @@ function MapForEach(callbackfn, thisArg = undefined) {
/* Step 6-8. */
var entries = std_Map_iterator.call(M);
while (true) {
try {
var entry = std_Map_iterator_next.call(entries);
} catch (err) {
if (err instanceof StopIteration)
break;
throw err;
}
var result = std_Map_iterator_next.call(entries);
if (result.done)
break;
var entry = result.value;
callFunction(callbackfn, thisArg, entry[1], entry[0], M);
}
}

View File

@ -25,6 +25,7 @@ using mozilla::DoubleIsInt32;
using mozilla::IsNaN;
using mozilla::OldMove;
using mozilla::MoveRef;
using mozilla::ArrayLength;
using JS::DoubleNaNValue;
@ -889,6 +890,7 @@ const Class MapIteratorObject::class_ = {
};
const JSFunctionSpec MapIteratorObject::methods[] = {
JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0),
JS_FN("next", next, 0, 0),
JS_FS_END
};
@ -965,35 +967,44 @@ MapIteratorObject::next_impl(JSContext *cx, CallArgs args)
{
MapIteratorObject &thisobj = args.thisv().toObject().as<MapIteratorObject>();
ValueMap::Range *range = thisobj.range();
if (!range)
return js_ThrowStopIteration(cx);
if (range->empty()) {
RootedValue value(cx);
bool done;
if (!range || range->empty()) {
js_delete(range);
thisobj.setReservedSlot(RangeSlot, PrivateValue(nullptr));
return js_ThrowStopIteration(cx);
value.setUndefined();
done = true;
} else {
switch (thisobj.kind()) {
case MapObject::Keys:
value = range->front().key.get();
break;
case MapObject::Values:
value = range->front().value;
break;
case MapObject::Entries: {
Value pair[2] = { range->front().key.get(), range->front().value };
AutoValueArray root(cx, pair, ArrayLength(pair));
JSObject *pairobj = NewDenseCopiedArray(cx, ArrayLength(pair), pair);
if (!pairobj)
return false;
value.setObject(*pairobj);
break;
}
}
range->popFront();
done = false;
}
switch (thisobj.kind()) {
case MapObject::Keys:
args.rval().set(range->front().key.get());
break;
RootedObject result(cx, CreateItrResultObject(cx, value, done));
if (!result)
return false;
args.rval().setObject(*result);
case MapObject::Values:
args.rval().set(range->front().value);
break;
case MapObject::Entries: {
Value pair[2] = { range->front().key.get(), range->front().value };
AutoValueArray root(cx, pair, 2);
JSObject *pairobj = NewDenseCopiedArray(cx, 2, pair);
if (!pairobj)
return false;
args.rval().setObject(*pairobj);
break;
}
}
range->popFront();
return true;
}
@ -1077,7 +1088,7 @@ MapObject::initClass(JSContext *cx, JSObject *obj)
// Define its alias.
RootedValue funval(cx, ObjectValue(*fun));
if (!JS_DefineProperty(cx, proto, "iterator", funval, nullptr, nullptr, 0))
if (!JS_DefineProperty(cx, proto, js_std_iterator_str, funval, nullptr, nullptr, 0))
return nullptr;
}
return proto;
@ -1173,14 +1184,25 @@ MapObject::construct(JSContext *cx, unsigned argc, Value *vp)
CallArgs args = CallArgsFromVp(argc, vp);
if (args.hasDefined(0)) {
ForOfIterator iter(cx, args[0]);
while (iter.next()) {
RootedObject pairobj(cx, js_ValueToNonNullObject(cx, iter.value()));
if (!pairobj)
ForOfIterator iter(cx);
if (!iter.init(args[0]))
return false;
RootedValue pairVal(cx);
RootedObject pairObj(cx);
while (true) {
bool done;
if (!iter.next(&pairVal, &done))
return false;
if (done)
break;
// FIXME: We're supposed to throw if pairVal isn't an object. Bug
// 918341.
pairObj = js_ValueToNonNullObject(cx, pairVal);
if (!pairObj)
return false;
RootedValue key(cx);
if (!JSObject::getElement(cx, pairobj, pairobj, 0, &key))
if (!JSObject::getElement(cx, pairObj, pairObj, 0, &key))
return false;
AutoHashableValueRooter hkey(cx);
@ -1188,7 +1210,7 @@ MapObject::construct(JSContext *cx, unsigned argc, Value *vp)
return false;
RootedValue val(cx);
if (!JSObject::getElement(cx, pairobj, pairobj, 1, &val))
if (!JSObject::getElement(cx, pairObj, pairObj, 1, &val))
return false;
RelocatableValue rval(val);
@ -1198,8 +1220,6 @@ MapObject::construct(JSContext *cx, unsigned argc, Value *vp)
}
WriteBarrierPost(cx->runtime(), map, hkey);
}
if (!iter.close())
return false;
}
args.rval().setObject(*obj);
@ -1456,6 +1476,7 @@ const Class SetIteratorObject::class_ = {
};
const JSFunctionSpec SetIteratorObject::methods[] = {
JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0),
JS_FN("next", next, 0, 0),
JS_FS_END
};
@ -1531,32 +1552,40 @@ SetIteratorObject::next_impl(JSContext *cx, CallArgs args)
{
SetIteratorObject &thisobj = args.thisv().toObject().as<SetIteratorObject>();
ValueSet::Range *range = thisobj.range();
if (!range)
return js_ThrowStopIteration(cx);
if (range->empty()) {
RootedValue value(cx);
bool done;
if (!range || range->empty()) {
js_delete(range);
thisobj.setReservedSlot(RangeSlot, PrivateValue(nullptr));
return js_ThrowStopIteration(cx);
value.setUndefined();
done = true;
} else {
switch (thisobj.kind()) {
case SetObject::Values:
value = range->front().get();
break;
case SetObject::Entries: {
Value pair[2] = { range->front().get(), range->front().get() };
AutoValueArray root(cx, pair, 2);
JSObject *pairObj = NewDenseCopiedArray(cx, 2, pair);
if (!pairObj)
return false;
value.setObject(*pairObj);
break;
}
}
range->popFront();
done = false;
}
switch (thisobj.kind()) {
case SetObject::Values:
args.rval().set(range->front().get());
break;
RootedObject result(cx, CreateItrResultObject(cx, value, done));
if (!result)
return false;
args.rval().setObject(*result);
case SetObject::Entries: {
Value pair[2] = { range->front().get(), range->front().get() };
AutoValueArray root(cx, pair, 2);
JSObject *pairobj = NewDenseCopiedArray(cx, 2, pair);
if (!pairobj)
return false;
args.rval().setObject(*pairobj);
break;
}
}
range->popFront();
return true;
}
@ -1620,7 +1649,7 @@ SetObject::initClass(JSContext *cx, JSObject *obj)
RootedValue funval(cx, ObjectValue(*fun));
if (!JS_DefineProperty(cx, proto, "keys", funval, nullptr, nullptr, 0))
return nullptr;
if (!JS_DefineProperty(cx, proto, "iterator", funval, nullptr, nullptr, 0))
if (!JS_DefineProperty(cx, proto, js_std_iterator_str, funval, nullptr, nullptr, 0))
return nullptr;
}
return proto;
@ -1662,10 +1691,18 @@ SetObject::construct(JSContext *cx, unsigned argc, Value *vp)
CallArgs args = CallArgsFromVp(argc, vp);
if (args.hasDefined(0)) {
ForOfIterator iter(cx, args[0]);
while (iter.next()) {
AutoHashableValueRooter key(cx);
if (!key.setValue(cx, iter.value()))
RootedValue keyVal(cx);
ForOfIterator iter(cx);
if (!iter.init(args[0]))
return false;
AutoHashableValueRooter key(cx);
while (true) {
bool done;
if (!iter.next(&keyVal, &done))
return false;
if (done)
break;
if (!key.setValue(cx, keyVal))
return false;
if (!set->put(key)) {
js_ReportOutOfMemory(cx);
@ -1673,8 +1710,6 @@ SetObject::construct(JSContext *cx, unsigned argc, Value *vp)
}
WriteBarrierPost(cx->runtime(), set, key);
}
if (!iter.close())
return false;
}
args.rval().setObject(*obj);

View File

@ -24,13 +24,10 @@ function SetForEach(callbackfn, thisArg = undefined) {
/* Step 7-8. */
var values = std_Set_iterator.call(S);
while (true) {
try {
var entry = std_Set_iterator_next.call(values);
} catch (err) {
if (err instanceof StopIteration)
break;
throw err;
}
callFunction(callbackfn, thisArg, entry, entry, S);
var result = std_Set_iterator_next.call(values);
if (result.done)
break;
var value = result.value;
callFunction(callbackfn, thisArg, value, value, S);
}
}

View File

@ -39,6 +39,7 @@
var std_isFinite = isFinite;
var std_isNaN = isNaN;
var std_Array_indexOf = ArrayIndexOf;
var std_Array_iterator = Array.prototype.iterator;
var std_Array_join = Array.prototype.join;
var std_Array_push = Array.prototype.push;
var std_Array_shift = Array.prototype.shift;
@ -71,15 +72,18 @@ var std_String_startsWith = String.prototype.startsWith;
var std_String_substring = String.prototype.substring;
var std_String_toLowerCase = String.prototype.toLowerCase;
var std_String_toUpperCase = String.prototype.toUpperCase;
var std_WeakMap = WeakMap;
var std_WeakMap_get = WeakMap.prototype.get;
var std_WeakMap_has = WeakMap.prototype.has;
var std_WeakMap_set = WeakMap.prototype.set;
var std_Map_has = Map.prototype.has;
var std_Set_has = Set.prototype.has;
var std_Map_iterator = Map().iterator;
var std_Set_iterator = Set().iterator;
var std_Map_iterator_next = Object.getPrototypeOf(Map().iterator()).next;
var std_Set_iterator_next = Object.getPrototypeOf(Set().iterator()).next;
var std_iterator = '@@iterator'; // FIXME: Change to be a symbol.
var std_StopIteration = StopIteration;
var std_Map_iterator = Map.prototype[std_iterator];
var std_Set_iterator = Set.prototype[std_iterator];
var std_Map_iterator_next = Object.getPrototypeOf(Map()[std_iterator]()).next;
var std_Set_iterator_next = Object.getPrototypeOf(Set()[std_iterator]()).next;
/********** List specification type **********/

View File

@ -177,6 +177,8 @@ UpdateDepth(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t target)
nuses = ndefs = CurrentBlock(bce->topStmt).slotCount();
} else if (op == JSOP_ENTERLET1) {
nuses = ndefs = CurrentBlock(bce->topStmt).slotCount() + 1;
} else if (op == JSOP_ENTERLET2) {
nuses = ndefs = CurrentBlock(bce->topStmt).slotCount() + 2;
} else {
nuses = StackUses(nullptr, pc);
ndefs = StackDefs(nullptr, pc);
@ -298,6 +300,7 @@ static const char * const statementName[] = {
"do loop", /* DO_LOOP */
"for loop", /* FOR_LOOP */
"for/in loop", /* FOR_IN_LOOP */
"for/of loop", /* FOR_OF_LOOP */
"while loop", /* WHILE_LOOP */
};
@ -555,6 +558,10 @@ EmitNonLocalJumpFixup(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *t
return false;
break;
case STMT_FOR_OF_LOOP:
npops += 2;
break;
case STMT_FOR_IN_LOOP:
FLUSH_POPS();
if (!PopIterator(cx, bce))
@ -577,11 +584,11 @@ EmitNonLocalJumpFixup(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *t
unsigned blockObjCount = stmt->blockObj->slotCount();
if (stmt->isForLetBlock) {
/*
* For a for-let-in statement, pushing/popping the block is
* For a for-let-in/of statement, pushing/popping the block is
* interleaved with JSOP_(END)ITER. Just handle both together
* here and skip over the enclosing STMT_FOR_IN_LOOP.
*/
JS_ASSERT(stmt->down->type == STMT_FOR_IN_LOOP);
unsigned popCount = blockObjCount;
stmt = stmt->down;
if (stmt == toStmt)
break;
@ -589,11 +596,16 @@ EmitNonLocalJumpFixup(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *t
return false;
if (Emit1(cx, bce, JSOP_LEAVEFORLETIN) < 0)
return false;
if (!PopIterator(cx, bce))
return false;
if (stmt->type == STMT_FOR_OF_LOOP) {
popCount += 2;
} else {
JS_ASSERT(stmt->type == STMT_FOR_IN_LOOP);
if (!PopIterator(cx, bce))
return false;
}
if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
return false;
EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount);
EMIT_UINT16_IMM_OP(JSOP_POPN, popCount);
} else {
/* There is a Block object with locals on the stack to pop. */
if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
@ -1052,8 +1064,12 @@ EmitEnterBlock(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp o
Rooted<StaticBlockObject*> blockObj(cx, &pn->pn_objbox->object->as<StaticBlockObject>());
int depth = bce->stackDepth -
(blockObj->slotCount() + ((op == JSOP_ENTERLET1) ? 1 : 0));
int extraSlots = (op == JSOP_ENTERLET1)
? 1
: (op == JSOP_ENTERLET2)
? 2
: 0;
int depth = bce->stackDepth - (blockObj->slotCount() + extraSlots);
JS_ASSERT(depth >= 0);
blockObj->setStackDepth(depth);
@ -3518,10 +3534,11 @@ EmitAssignment(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp
return false;
} else {
/*
* The value to assign is the next enumeration value in a for-in loop.
* That value is produced by a JSOP_ITERNEXT op, previously emitted.
* If offset == 1, that slot is already at the top of the
* stack. Otherwise, rearrange the stack to put that value on top.
* The value to assign is the next enumeration value in a for-in or
* for-of loop. That value has already been emitted: by JSOP_ITERNEXT
* in the for-in case, or via a GETPROP "value" on the result object in
* the for-of case. If offset == 1, that slot is already at the top of
* the stack. Otherwise, rearrange the stack to put that value on top.
*/
if (offset != 1 && Emit2(cx, bce, JSOP_PICK, offset - 1) < 0)
return false;
@ -4234,6 +4251,170 @@ EmitWith(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
return PopStatementBCE(cx, bce);
}
static bool
EmitForOf(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
{
StmtInfoBCE stmtInfo(cx);
PushStatementBCE(bce, &stmtInfo, STMT_FOR_OF_LOOP, top);
ParseNode *forHead = pn->pn_left;
ParseNode *forBody = pn->pn_right;
ParseNode *pn1 = forHead->pn_kid1;
bool letDecl = pn1 && pn1->isKind(PNK_LEXICALSCOPE);
JS_ASSERT_IF(letDecl, pn1->isLet());
Rooted<StaticBlockObject*>
blockObj(cx, letDecl ? &pn1->pn_objbox->object->as<StaticBlockObject>() : nullptr);
uint32_t blockObjCount = blockObj ? blockObj->slotCount() : 0;
// For-of loops run with two values on the stack: the iterator and the
// current result object. If the loop also has a lexical block, those
// lexicals are deeper on the stack than the iterator.
for (uint32_t i = 0; i < blockObjCount; ++i) {
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
return false;
}
// If the left part is 'var x', emit code to define x if necessary using a
// prolog opcode, but do not emit a pop.
if (pn1) {
ParseNode *decl = letDecl ? pn1->pn_expr : pn1;
JS_ASSERT(decl->isKind(PNK_VAR) || decl->isKind(PNK_LET));
bce->emittingForInit = true;
if (!EmitVariables(cx, bce, decl, DefineVars))
return false;
bce->emittingForInit = false;
}
// Compile the object expression to the right of 'of'.
if (!EmitTree(cx, bce, forHead->pn_kid3))
return false;
// Convert iterable to iterator.
if (Emit1(cx, bce, JSOP_DUP) < 0) // OBJ OBJ
return false;
if (!EmitAtomOp(cx, cx->names().std_iterator, JSOP_CALLPROP, bce)) // OBJ @@ITERATOR
return false;
if (Emit1(cx, bce, JSOP_SWAP) < 0) // @@ITERATOR OBJ
return false;
if (Emit1(cx, bce, JSOP_NOTEARG) < 0)
return false;
if (EmitCall(cx, bce, JSOP_CALL, 0) < 0) // ITER
return false;
CheckTypeSet(cx, bce, JSOP_CALL);
// Push a dummy result so that we properly enter iteration midstream.
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) // ITER RESULT
return false;
// Enter the block before the loop body, after evaluating the obj.
StmtInfoBCE letStmt(cx);
if (letDecl) {
PushBlockScopeBCE(bce, &letStmt, *blockObj, bce->offset());
letStmt.isForLetBlock = true;
if (!EmitEnterBlock(cx, bce, pn1, JSOP_ENTERLET2))
return false;
}
// Jump down to the loop condition to minimize overhead assuming at least
// one iteration, as the other loop forms do. Annotate so IonMonkey can
// find the loop-closing jump.
int noteIndex = NewSrcNote(cx, bce, SRC_FOR_OF);
if (noteIndex < 0)
return false;
ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
if (jmp < 0)
return false;
top = bce->offset();
SET_STATEMENT_TOP(&stmtInfo, top);
if (EmitLoopHead(cx, bce, nullptr) < 0)
return false;
#ifdef DEBUG
int loopDepth = bce->stackDepth;
#endif
// Emit code to assign result.value to the iteration variable.
if (Emit1(cx, bce, JSOP_DUP) < 0) // ITER RESULT RESULT
return false;
if (!EmitAtomOp(cx, cx->names().value, JSOP_GETPROP, bce)) // ITER RESULT VALUE
return false;
if (!EmitAssignment(cx, bce, forHead->pn_kid2, JSOP_NOP, nullptr)) // ITER RESULT VALUE
return false;
if (Emit1(cx, bce, JSOP_POP) < 0) // ITER RESULT
return false;
// The stack should be balanced around the assignment opcode sequence.
JS_ASSERT(bce->stackDepth == loopDepth);
// Emit code for the loop body.
if (!EmitTree(cx, bce, forBody))
return false;
// Set loop and enclosing "update" offsets, for continue.
StmtInfoBCE *stmt = &stmtInfo;
do {
stmt->update = bce->offset();
} while ((stmt = stmt->down) != nullptr && stmt->type == STMT_LABEL);
// COME FROM the beginning of the loop to here.
SetJumpOffsetAt(bce, jmp);
if (!EmitLoopEntry(cx, bce, nullptr))
return false;
if (Emit1(cx, bce, JSOP_POP) < 0) // ITER
return false;
if (Emit1(cx, bce, JSOP_DUP) < 0) // ITER ITER
return false;
if (Emit1(cx, bce, JSOP_DUP) < 0) // ITER ITER ITER
return false;
if (!EmitAtomOp(cx, cx->names().next, JSOP_CALLPROP, bce)) // ITER ITER NEXT
return false;
if (Emit1(cx, bce, JSOP_SWAP) < 0) // ITER NEXT ITER
return false;
if (Emit1(cx, bce, JSOP_NOTEARG) < 0)
return false;
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) // ITER NEXT ITER UNDEFINED
return false;
if (Emit1(cx, bce, JSOP_NOTEARG) < 0)
return false;
if (EmitCall(cx, bce, JSOP_CALL, 1) < 0) // ITER RESULT
return false;
CheckTypeSet(cx, bce, JSOP_CALL);
if (Emit1(cx, bce, JSOP_DUP) < 0) // ITER RESULT RESULT
return false;
if (!EmitAtomOp(cx, cx->names().done, JSOP_GETPROP, bce)) // ITER RESULT DONE?
return false;
ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, top - bce->offset()); // ITER RESULT
if (beq < 0)
return false;
JS_ASSERT(bce->stackDepth == loopDepth);
// Let Ion know where the closing jump of this loop is.
if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, beq - jmp))
return false;
// Fixup breaks and continues.
if (!PopStatementBCE(cx, bce))
return false;
if (letDecl) {
if (!PopStatementBCE(cx, bce))
return false;
if (Emit1(cx, bce, JSOP_LEAVEFORLETIN) < 0)
return false;
}
// Pop result, iter, and slots from the lexical block (if any).
EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount + 2);
return true;
}
static bool
EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
{
@ -4553,10 +4734,14 @@ EmitNormalFor(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff
static inline bool
EmitFor(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
{
JS_ASSERT(pn->pn_left->isKind(PNK_FORIN) || pn->pn_left->isKind(PNK_FORHEAD));
return pn->pn_left->isKind(PNK_FORIN)
? EmitForIn(cx, bce, pn, top)
: EmitNormalFor(cx, bce, pn, top);
if (pn->pn_left->isKind(PNK_FORIN)) {
// FIXME: Give for-of loops their own PNK. Bug 922066.
if (pn->pn_iflags == JSITER_FOR_OF)
return EmitForOf(cx, bce, pn, top);
return EmitForIn(cx, bce, pn, top);
}
JS_ASSERT(pn->pn_left->isKind(PNK_FORHEAD));
return EmitNormalFor(cx, bce, pn, top);
}
static JS_NEVER_INLINE bool
@ -4992,6 +5177,7 @@ EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter)
return false;
if (EmitCall(cx, bce, JSOP_CALL, 1) < 0) // ITER RESULT
return false;
CheckTypeSet(cx, bce, JSOP_CALL);
JS_ASSERT(bce->stackDepth == depth + 1);
ptrdiff_t checkResult = -1;
if (EmitBackPatchOp(cx, bce, &checkResult) < 0) // goto checkResult
@ -5032,6 +5218,7 @@ EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter)
return false;
if (EmitCall(cx, bce, JSOP_CALL, 1) < 0) // ITER RESULT
return false;
CheckTypeSet(cx, bce, JSOP_CALL);
JS_ASSERT(bce->stackDepth == depth + 1);
if (!BackPatch(cx, bce, checkResult, bce->code().end(), JSOP_GOTO)) // checkResult:
@ -6672,6 +6859,8 @@ CGConstList::finish(ConstArray *array)
/*
* We should try to get rid of offsetBias (always 0 or 1, where 1 is
* JSOP_{NOP,POP}_LENGTH), which is used only by SRC_FOR.
*
* FIXME: Generate this using a higher-order macro. Bug 922070.
*/
const JSSrcNoteSpec js_SrcNoteSpec[] = {
/* 0 */ {"null", 0},
@ -6684,29 +6873,29 @@ const JSSrcNoteSpec js_SrcNoteSpec[] = {
/* 5 */ {"while", 1},
/* 6 */ {"for-in", 1},
/* 7 */ {"continue", 0},
/* 8 */ {"break", 0},
/* 9 */ {"break2label", 0},
/* 10 */ {"switchbreak", 0},
/* 7 */ {"for-of", 1},
/* 8 */ {"continue", 0},
/* 9 */ {"break", 0},
/* 10 */ {"break2label", 0},
/* 11 */ {"switchbreak", 0},
/* 11 */ {"tableswitch", 1},
/* 12 */ {"condswitch", 2},
/* 12 */ {"tableswitch", 1},
/* 13 */ {"condswitch", 2},
/* 13 */ {"nextcase", 1},
/* 14 */ {"nextcase", 1},
/* 14 */ {"assignop", 0},
/* 15 */ {"assignop", 0},
/* 15 */ {"hidden", 0},
/* 16 */ {"hidden", 0},
/* 16 */ {"catch", 0},
/* 17 */ {"catch", 0},
/* 17 */ {"try", 1},
/* 18 */ {"try", 1},
/* 18 */ {"colspan", 1},
/* 19 */ {"newline", 0},
/* 20 */ {"setline", 1},
/* 19 */ {"colspan", 1},
/* 20 */ {"newline", 0},
/* 21 */ {"setline", 1},
/* 21 */ {"unused21", 0},
/* 22 */ {"unused22", 0},
/* 23 */ {"unused23", 0},

View File

@ -3993,7 +3993,7 @@ Parser<FullParseHandler>::forStatement()
* that receives the enumeration value each iteration, and pn3 is the
* rhs of 'in'.
*/
forStmt.type = STMT_FOR_IN_LOOP;
forStmt.type = isForOf ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
/* Set iflags and rule out invalid combinations. */
if (isForOf && isForEach) {
@ -4275,7 +4275,7 @@ Parser<SyntaxParseHandler>::forStatement()
bool isForOf;
if (lhsNode && matchInOrOf(&isForOf)) {
/* Parse the rest of the for/in or for/of head. */
forStmt.type = STMT_FOR_IN_LOOP;
forStmt.type = isForOf ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
/* Check that the left side of the 'in' or 'of' is valid. */
if (!isForDecl &&

View File

@ -363,6 +363,7 @@ enum StmtType {
STMT_DO_LOOP, /* do/while loop statement */
STMT_FOR_LOOP, /* for loop statement */
STMT_FOR_IN_LOOP, /* for/in loop statement */
STMT_FOR_OF_LOOP, /* for/of loop statement */
STMT_WHILE_LOOP, /* while loop statement */
STMT_LIMIT
};

View File

@ -36,6 +36,9 @@ namespace js {
*
* Don't forget to update XDR_BYTECODE_VERSION in vm/Xdr.h for all such
* incompatible source note or other bytecode changes.
*
* FIXME: Use higher-order macro to force this to be in sync with
* js_SrcNoteSpec. Bug 922070.
*/
enum SrcNoteType {
SRC_NULL = 0, /* terminates a note vector */
@ -51,36 +54,37 @@ enum SrcNoteType {
do-while loop */
SRC_FOR_IN = 6, /* JSOP_GOTO to for-in loop condition from
before loop */
SRC_CONTINUE = 7, /* JSOP_GOTO is a continue */
SRC_BREAK = 8, /* JSOP_GOTO is a break */
SRC_BREAK2LABEL = 9, /* JSOP_GOTO for 'break label' */
SRC_SWITCHBREAK = 10, /* JSOP_GOTO is a break in a switch */
SRC_FOR_OF = 7, /* JSOP_GOTO to for-of loop condition from
before loop */
SRC_CONTINUE = 8, /* JSOP_GOTO is a continue */
SRC_BREAK = 9, /* JSOP_GOTO is a break */
SRC_BREAK2LABEL = 10, /* JSOP_GOTO for 'break label' */
SRC_SWITCHBREAK = 11, /* JSOP_GOTO is a break in a switch */
SRC_TABLESWITCH = 11, /* JSOP_TABLESWITCH, offset points to end of
SRC_TABLESWITCH = 12, /* JSOP_TABLESWITCH, offset points to end of
switch */
SRC_CONDSWITCH = 12, /* JSOP_CONDSWITCH, 1st offset points to end of
SRC_CONDSWITCH = 13, /* JSOP_CONDSWITCH, 1st offset points to end of
switch, 2nd points to first JSOP_CASE */
SRC_NEXTCASE = 13, /* distance forward from one CASE in a
SRC_NEXTCASE = 14, /* distance forward from one CASE in a
CONDSWITCH to the next */
SRC_ASSIGNOP = 14, /* += or another assign-op follows */
SRC_ASSIGNOP = 15, /* += or another assign-op follows */
SRC_HIDDEN = 15, /* opcode shouldn't be decompiled */
SRC_HIDDEN = 16, /* opcode shouldn't be decompiled */
SRC_CATCH = 16, /* catch block has guard */
SRC_CATCH = 17, /* catch block has guard */
SRC_TRY = 17, /* JSOP_TRY, offset points to goto at the
SRC_TRY = 18, /* JSOP_TRY, offset points to goto at the
end of the try block. */
/* All notes below here are "gettable". See SN_IS_GETTABLE below. */
SRC_LAST_GETTABLE = SRC_TRY,
SRC_COLSPAN = 18, /* number of columns this opcode spans */
SRC_NEWLINE = 19, /* bytecode follows a source newline */
SRC_SETLINE = 20, /* a file-absolute source line number note */
SRC_COLSPAN = 19, /* number of columns this opcode spans */
SRC_NEWLINE = 20, /* bytecode follows a source newline */
SRC_SETLINE = 21, /* a file-absolute source line number note */
SRC_UNUSED21 = 21,
SRC_UNUSED22 = 22,
SRC_UNUSED23 = 23,

View File

@ -5,9 +5,16 @@
load(libdir + "asserts.js");
// FIXME: Import from std::iteration.
const std_iterator = '@@iterator';
if (typeof assertIteratorResult === 'undefined') {
var assertIteratorResult = function assertIteratorResult(result, value, done) {
assertEq(typeof result, "object");
var expectedProps = ['done', 'value'];
var actualProps = Object.getOwnPropertyNames(result);
actualProps.sort(), expectedProps.sort();
assertDeepEq(actualProps, expectedProps);
assertDeepEq(result.value, value);
assertDeepEq(result.done, done);
}

View File

@ -1,5 +1,7 @@
// |jit-test| error: TypeError
load(libdir + "iteration.js");
eval("var x; typeof x")
Array.prototype.iterator = function () { for(y in x); };
Array.prototype[std_iterator] = function () { for(y in x); };
for (var v of ['a', 'b', 'c', 'd'])
s = v;

View File

@ -1,4 +1,5 @@
load(libdir + "asserts.js");
load(libdir + "iteration.js");
load(libdir + "eqArrayHelper.js");
assertEqArray([...[1, 2, 3]], [1, 2, 3]);
@ -19,27 +20,24 @@ assertEqArray([..."abc"], ["a", "b", "c"]);
assertEqArray([...[1, 2, 3].iterator()], [1, 2, 3]);
assertEqArray([...Set([1, 2, 3])], [1, 2, 3]);
assertEqArray([...Map([["a", "A"], ["b", "B"], ["c", "C"]])].map(([k, v]) => k + v), ["aA", "bB", "cC"]);
let itr = {
iterator: function() {
let itr = {};
itr[std_iterator] = function () {
return {
i: 1,
next: function() {
if (this.i < 4)
return this.i++;
else
throw StopIteration;
}
i: 1,
next: function() {
if (this.i < 4)
return { value: this.i++, done: false };
else
return { value: undefined, done: true };
}
};
}
};
}
assertEqArray([...itr], [1, 2, 3]);
let gen = {
iterator: function() {
function* gen() {
for (let i = 1; i < 4; i ++)
yield i;
}
};
assertEqArray([...gen], [1, 2, 3]);
yield i;
}
assertEqArray([...gen()], [1, 2, 3]);
let a, b = [1, 2, 3];
assertEqArray([...a=b], [1, 2, 3]);

View File

@ -1,4 +1,5 @@
load(libdir + "asserts.js");
load(libdir + "iteration.js");
assertEq(eval(...[]), undefined);
assertEq(eval(...["1 + 2"]), 3);
@ -25,27 +26,24 @@ try { // line0 + 1
// other iterable objects
assertEq(eval(...["a + b"].iterator()), 11);
assertEq(eval(...Set(["a + b"])), 11);
let itr = {
iterator: function() {
let itr = {};
itr[std_iterator] = function() {
return {
i: 0,
next: function() {
this.i++;
if (this.i == 1)
return "a + b";
else
throw StopIteration;
}
i: 0,
next: function() {
this.i++;
if (this.i == 1)
return { value: "a + b", done: false };
else
return { value: undefined, done: true };
}
};
}
};
assertEq(eval(...itr), 11);
let gen = {
iterator: function() {
function* gen() {
yield "a + b";
}
};
assertEq(eval(...gen), 11);
}
assertEq(eval(...gen()), 11);
let c = ["C"], d = "D";
assertEq(eval(...c=["c[0] + d"]), "c[0] + dD");

View File

@ -1,5 +1,6 @@
load(libdir + "asserts.js");
load(libdir + "eqArrayHelper.js");
load(libdir + "iteration.js");
function checkCommon(f) {
assertEqArray(f.apply(null, ...[[1, 2, 3]]), [1, 2, 3]);
@ -10,30 +11,27 @@ function checkCommon(f) {
// other iterable objects
assertEqArray(f.apply(...Set([null, [1, 2, 3]])), [1, 2, 3]);
assertEqArray(f.apply(...[null, [1, 2, 3]].iterator()), [1, 2, 3]);
let itr = {
iterator: function() {
let itr = {};
itr[std_iterator] = function() {
return {
i: 0,
next: function() {
this.i++;
if (this.i == 1)
return null;
else if (this.i == 2)
return [1, 2, 3];
else
throw StopIteration;
}
i: 0,
next: function() {
this.i++;
if (this.i == 1)
return { value: null, done: false };
else if (this.i == 2)
return { value: [1, 2, 3], done: false };
else
return { value: undefined, done: true };
}
};
}
};
assertEqArray(f.apply(...itr), [1, 2, 3]);
let gen = {
iterator: function() {
function* gen() {
yield null;
yield [1, 2, 3];
}
};
assertEqArray(f.apply(...gen), [1, 2, 3]);
}
assertEqArray(f.apply(...gen()), [1, 2, 3]);
let a;
assertEqArray(f.apply(null, ...a=[[1, 2, 3]]), [1, 2, 3]);

View File

@ -1,3 +1,5 @@
load(libdir + 'iteration.js');
let makeCall = farg => Function("f", "arg", "return f(" + farg + ");");
let makeFunCall = farg => Function("f", "arg", "return f.call(null, " + farg + ");");
let makeNew = farg => Function("f", "arg", "return new f(" + farg + ").length;");
@ -22,27 +24,24 @@ function checkLength(f, makeFn) {
assertEq(makeFn("...arg")(f, [1, 2, 3].iterator()), 3);
assertEq(makeFn("...arg")(f, Set([1, 2, 3])), 3);
assertEq(makeFn("...arg")(f, Map([["a", "A"], ["b", "B"], ["c", "C"]])), 3);
let itr = {
iterator: function() {
let itr = {};
itr[std_iterator] = function() {
return {
i: 1,
next: function() {
if (this.i < 4)
return this.i++;
else
throw StopIteration;
}
i: 1,
next: function() {
if (this.i < 4)
return { value: this.i++, done: false };
else
return { value: undefined, done: true };
}
};
}
};
}
assertEq(makeFn("...arg")(f, itr), 3);
let gen = {
iterator: function() {
function* gen() {
for (let i = 1; i < 4; i ++)
yield i;
}
};
assertEq(makeFn("...arg")(f, gen), 3);
yield i;
}
assertEq(makeFn("...arg")(f, gen()), 3);
}
checkLength(function(x) arguments.length, makeCall);

View File

@ -1,4 +1,5 @@
load(libdir + "asserts.js");
load(libdir + "iteration.js");
assertThrowsInstanceOf(() => Math.sin(...true), TypeError);
assertThrowsInstanceOf(() => Math.sin(...false), TypeError);
@ -8,9 +9,20 @@ assertThrowsInstanceOf(() => Math.sin(...function () {}), TypeError);
assertThrowsInstanceOf(() => Math.sin(...(x => x)), TypeError);
assertThrowsInstanceOf(() => Math.sin(...1), TypeError);
assertThrowsInstanceOf(() => Math.sin(...{}), TypeError);
assertThrowsInstanceOf(() => Math.sin(...{ iterator: 10 }), TypeError);
assertThrowsInstanceOf(() => Math.sin(...{ iterator: function() undefined }), TypeError);
assertThrowsInstanceOf(() => Math.sin(...{ iterator: function() this }), TypeError);
assertThrowsValue(() => Math.sin(...{ iterator: function() this, next: function() { throw 10; } }), 10);
var foo = {}
foo[std_iterator] = 10;
assertThrowsInstanceOf(() => Math.sin(...foo), TypeError);
foo[std_iterator] = function() undefined;
assertThrowsInstanceOf(() => Math.sin(...foo), TypeError);
foo[std_iterator] = function() this;
assertThrowsInstanceOf(() => Math.sin(...foo), TypeError);
foo[std_iterator] = function() this;
foo.next = function() { throw 10; };
assertThrowsValue(() => Math.sin(...foo), 10);
assertThrowsInstanceOf(() => Math.sin(.../a/), TypeError);
assertThrowsInstanceOf(() => Math.sin(...new Error()), TypeError);

View File

@ -1,5 +1,6 @@
load(libdir + "asserts.js");
load(libdir + "eqArrayHelper.js");
load(libdir + "iteration.js");
let makeCall = farg => Function("f", "arg", "return f(" + farg + ");");
let makeFunCall = farg => Function("f", "arg", "return f.call(null, " + farg + ");");
@ -18,27 +19,24 @@ function checkCommon(f, makeFn) {
assertEqArray(makeFn("...arg")(f, [1, 2, 3].iterator()), [1, 2, 3]);
assertEqArray(makeFn("...arg")(f, Set([1, 2, 3])), [1, 2, 3]);
assertEqArray(makeFn("...arg")(f, Map([["a", "A"], ["b", "B"], ["c", "C"]])).map(([k, v]) => k + v), ["aA", "bB", "cC"]);
let itr = {
iterator: function() {
let itr = {};
itr[std_iterator] = function() {
return {
i: 1,
next: function() {
if (this.i < 4)
return this.i++;
else
throw StopIteration;
}
i: 1,
next: function() {
if (this.i < 4)
return { value: this.i++, done: false };
else
return { value: undefined, done: true };
}
};
}
};
assertEqArray(makeFn("...arg")(f, itr), [1, 2, 3]);
let gen = {
iterator: function() {
function gen() {
for (let i = 1; i < 4; i ++)
yield i;
}
};
assertEqArray(makeFn("...arg")(f, gen), [1, 2, 3]);
yield i;
}
assertEqArray(makeFn("...arg")(f, gen()), [1, 2, 3]);
assertEqArray(makeFn("...arg=[1, 2, 3]")(f), [1, 2, 3]);

View File

@ -1,17 +1,17 @@
// A Map iterator does not visit entries removed by clear().
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var m = Map();
var it = m.iterator();
var it = m[std_iterator]();
m.clear();
assertThrowsValue(it.next.bind(it), StopIteration);
assertIteratorResult(it.next(), undefined, true);
m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
it = m.iterator();
assertEq(it.next()[0], "a");
it = m[std_iterator]();
assertIteratorResult(it.next(), ["a", 1], false);
m.clear();
assertThrowsValue(it.next.bind(it), StopIteration);
assertIteratorResult(it.next(), undefined, true);
var log = "";
m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);

View File

@ -1,15 +1,12 @@
// A Map iterator continues to visit entries added after a clear().
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var m = Map([["a", 1]]);
var it = m.iterator();
assertEq(it.next()[0], "a");
var it = m[std_iterator]();
assertIteratorResult(it.next(), ["a", 1], false);
m.clear();
m.set("b", 2);
var pair = it.next()
assertEq(pair[0], "b");
assertEq(pair[1], 2);
assertThrowsValue(it.next.bind(it), StopIteration);
assertIteratorResult(it.next(), ["b", 2], false);
assertIteratorResult(it.next(), undefined, true);

View File

@ -1,6 +1,7 @@
/* test Map.prototype.forEach */
load(libdir + 'asserts.js');
load(libdir + 'iteration.js');
// testing success conditions of Map.prototype.forEach
@ -16,12 +17,12 @@ var initialMap = new Map([['a', 1], ['b', 2.3], [false, undefined]]);
initialMap.forEach(callback);
// test that both the Maps are equal and are in same order
var iterator = initialMap.iterator();
var iterator = initialMap[std_iterator]();
var count = 0;
for (var [k, v] of testMap) {
assertEq(initialMap.has(k), true);
assertEq(initialMap.get(k), testMap.get(k));
assertEq(iterator.next()[1], testMap.get(k));
assertIteratorResult(iterator.next(), [k, testMap.get(k)], false);
count++;
}
@ -52,7 +53,7 @@ assertThrowsInstanceOf(function() {
// StopIteration exception is thrown
var m = new Map([["one", 1]]);
Object.getPrototypeOf(m.iterator()).next = function () { throw "FAIL"; };
Object.getPrototypeOf(m[std_iterator]()).next = function () { throw "FAIL"; };
assertThrowsInstanceOf(function () {
m.forEach(function () { throw StopIteration; });
}, StopIteration, "Map.prototype.forEach should use intrinsic next method.");

View File

@ -1,11 +1,10 @@
// A Map iterator does not iterate over new entries added after it throws StopIteration.
load(libdir + "asserts.js");
load(libdir + "eqArrayHelper.js");
load(libdir + "iteration.js");
var map = Map();
var iter0 = map.iterator(), iter1 = map.iterator();
assertThrowsValue(function () { iter0.next(); }, StopIteration); // closes iter0
var iter0 = map[std_iterator](), iter1 = map[std_iterator]();
assertIteratorResult(iter0.next(), undefined, true); // closes iter0
map.set(1, 2);
assertThrowsValue(function () { iter0.next(); }, StopIteration); // already closed
assertEqArray(iter1.next(), [1, 2]); // was not yet closed
assertIteratorResult(iter0.next(), undefined, true); // already closed
assertIteratorResult(iter1.next(), [1, 2], false); // was not yet closed

View File

@ -1,8 +1,10 @@
// mapiter.next() returns an actual array.
load(libdir + "iteration.js");
var key = {};
var map = Map([[key, 'value']]);
var entry = map.iterator().next();
var entry = map[std_iterator]().next().value;
assertEq(Array.isArray(entry), true);
assertEq(Object.getPrototypeOf(entry), Array.prototype);
assertEq(Object.isExtensible(entry), true);

View File

@ -1,10 +1,13 @@
// mapiter.next() returns a fresh array each time.
load(libdir + "iteration.js");
var map = Map([['a', 1], ['b', 2]]);
var iter = map.iterator();
var iter = map[std_iterator]();
var a = iter.next(), b = iter.next();
assertEq(a !== b, true);
assertEq(a[0], 'a');
assertEq(b[0], 'b');
var a1 = map.iterator().next();
assertEq(a !== a1, true);
assertIteratorResult(a, ['a', 1], false);
assertIteratorResult(b, ['b', 2], false);
assertEq(a.value !== b.value, true);
var a1 = map[std_iterator]().next();
assertIteratorResult(a1, ['a', 1], false);
assertEq(a.value !== a1.value, true);

View File

@ -1,12 +1,13 @@
// Modifying an array returned by mapiter.next() does not modify the Map.
load(libdir + "iteration.js");
var map = Map([['a', 1]]);
var pair = map.iterator().next();
assertEq(pair[0], 'a');
pair[0] = 'b';
pair[1] = 2;
assertEq(pair[0], 'b');
assertEq(pair[1], 2);
var res = map[std_iterator]().next();
assertIteratorResult(res, ['a', 1], false);
res.value[0] = 'b';
res.value[1] = 2;
assertIteratorResult(res, ['b', 2], false);
assertEq(map.get('a'), 1);
assertEq(map.has('b'), false);
assertEq(map.size, 1);

View File

@ -2,18 +2,20 @@
load(libdir + "asserts.js");
load(libdir + "eqArrayHelper.js");
load(libdir + "iteration.js");
var g = newGlobal();
var iterator_fn = Map.prototype.iterator;
var iterator_fn = Map.prototype[std_iterator];
assertThrowsInstanceOf(function () { iterator_fn.call({}); }, TypeError);
assertThrowsInstanceOf(function () { iterator_fn.call(Set()); }, TypeError);
var mapw = g.eval("Map([['x', 1], ['y', 2]])");
assertEqArray(iterator_fn.call(mapw).next(), ["x", 1]);
assertEqArray(iterator_fn.call(mapw).next().value, ["x", 1]);
var next_fn = Map().iterator().next;
var next_fn = Map()[std_iterator]().next;
assertThrowsInstanceOf(function () { next_fn.call({}); }, TypeError);
assertThrowsInstanceOf(function () { next_fn.call(Set().iterator()); }, TypeError);
var iterw = mapw.iterator();
assertEqArray(next_fn.call(iterw), ["x", 1]);
assertEqArray(next_fn.call(iterw), ["y", 2]);
assertThrowsValue(function () { next_fn.call(iterw); }, g.StopIteration);
assertThrowsInstanceOf(function () { next_fn.call(Set()[std_iterator]()); }, TypeError);
var iterw = mapw[std_iterator]();
assertEqArray(next_fn.call(iterw).value, ["x", 1]);
assertEqArray(next_fn.call(iterw).value, ["y", 2]);
assertEq(next_fn.call(iterw).done, true);

View File

@ -1,7 +1,9 @@
// A map iterator can cope with removing the next entry.
load(libdir + "iteration.js");
var map = Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]);
var iter = map.iterator();
var iter = map[std_iterator]();
var log = '';
for (let [k, v] of iter) {
log += k + v;

View File

@ -1,12 +1,13 @@
// A map iterator can cope with removing the next entry, then the current entry.
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var map = Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]);
var iter = map.iterator();
assertEq(iter.next()[0], 'a');
assertEq(iter.next()[0], 'b');
var iter = map[std_iterator]();
assertIteratorResult(iter.next(), ['a', 0], false);
assertIteratorResult(iter.next(), ['b', 1], false);
map.delete('c');
map.delete('b');
assertEq(iter.next()[0], 'd');
assertThrowsValue(function () { iter.next(); }, StopIteration);
assertIteratorResult(iter.next(), ['d', 3], false);
assertIteratorResult(iter.next(), undefined, true);

View File

@ -1,7 +1,6 @@
// Multiple live iterators on the same Map can cope with removing entries.
load(libdir + "eqArrayHelper.js");
load(libdir + "asserts.js");
load(libdir + "iteration.js");
// Make a map.
var map = Map();
@ -13,9 +12,9 @@ for (var j = 0; j < SIZE; j++)
var NITERS = 5;
var iters = [];
for (var i = 0; i < NITERS; i++) {
var iter = map.iterator();
assertEqArray(iter.next(), [0, 0]);
assertEqArray(iter.next(), [1, 1]);
var iter = map[std_iterator]();
assertIteratorResult(iter.next(), [0, 0], false);
assertIteratorResult(iter.next(), [1, 1], false);
iters[i] = iter;
}
@ -27,6 +26,6 @@ for (var j = 0; j < SIZE; j += 2)
for (var i = 0; i < NITERS; i++) {
var iter = iters[i];
for (var j = 3; j < SIZE; j += 2)
assertEqArray(iter.next(), [j, j]);
assertThrowsValue(function () { iter.next(); }, StopIteration);
assertIteratorResult(iter.next(), [j, j], false);
assertIteratorResult(iter.next(), undefined, true);
}

View File

@ -2,13 +2,13 @@
// entries that were not removed. (Compacting a Map must not be observable to
// script.)
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var map = Map();
for (var i = 0; i < 32; i++)
map.set(i, i);
var iter = map.iterator();
assertEq(iter.next()[0], 0);
var iter = map[std_iterator]();
assertIteratorResult(iter.next(), [0, 0], false);
for (var i = 0; i < 30; i++)
map.delete(i);
assertEq(map.size, 2);
@ -16,5 +16,6 @@ for (var i = 32; i < 100; i++)
map.set(i, i); // eventually triggers compaction
for (var i = 30; i < 100; i++)
assertEq(iter.next()[0], i);
assertThrowsValue(function () { iter.next(); }, StopIteration);
assertIteratorResult(iter.next(), [i, i], false);
assertIteratorResult(iter.next(), undefined, true);

View File

@ -1,10 +1,10 @@
// A closed Map iterator does not visit new entries added after a clear().
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var m = Map();
var it = m.iterator();
assertThrowsValue(it.next.bind(it), StopIteration); // close the iterator
var it = m[std_iterator]();
assertIteratorResult(it.next(), undefined, true); // close the iterator
m.clear();
m.set("a", 1);
assertThrowsValue(it.next.bind(it), StopIteration);
assertIteratorResult(it.next(), undefined, true); // iterator still closed

View File

@ -1,5 +1,7 @@
// Map surfaces
load(libdir + "iteration.js");
var desc = Object.getOwnPropertyDescriptor(this, "Map");
assertEq(desc.enumerable, false);
assertEq(desc.configurable, true);
@ -43,5 +45,5 @@ assertEq(desc.get.length, 0);
assertEq(desc.set, undefined);
checkMethod("clear", 0);
// Map.prototype.iterator and .entries are the same function object.
assertEq(Map.prototype.iterator, Map.prototype.entries);
// Map.prototype[@@iterator] and .entries are the same function object.
assertEq(Map.prototype[std_iterator], Map.prototype.entries);

View File

@ -1,17 +1,17 @@
// map.keys() and map.values() return iterators over the key or the value,
// respectively, of each key-value pair in the map.
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var data = [["one", 1], ["two", 2], ["three", 3], ["four", 4]];
var m = Map(data);
var ki = m.keys();
assertEq(ki.next(), "one");
assertEq(ki.next(), "two");
assertEq(ki.next(), "three");
assertEq(ki.next(), "four");
assertThrowsValue(function () { ki.next(); }, StopIteration);
assertIteratorResult(ki.next(), "one", false);
assertIteratorResult(ki.next(), "two", false);
assertIteratorResult(ki.next(), "three", false);
assertIteratorResult(ki.next(), "four", false);
assertIteratorResult(ki.next(), undefined, true);
assertEq([k for (k of m.keys())].toSource(), ["one", "two", "three", "four"].toSource());
assertEq([k for (k of m.values())].toSource(), [1, 2, 3, 4].toSource());

View File

@ -1,17 +1,17 @@
// A Set iterator does not visit entries removed by clear().
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var s = Set();
var it = s.iterator();
var it = s[std_iterator]();
s.clear();
assertThrowsValue(it.next.bind(it), StopIteration);
assertIteratorResult(it.next(), undefined, true);
s = Set(["a", "b", "c", "d"]);
it = s.iterator();
assertEq(it.next()[0], "a");
it = s[std_iterator]();
assertIteratorResult(it.next(), "a", false);
s.clear();
assertThrowsValue(it.next.bind(it), StopIteration);
assertIteratorResult(it.next(), undefined, true);
var log = "";
s = Set(["a", "b", "c", "d"]);

View File

@ -1,11 +1,11 @@
// A Set iterator continues to visit entries added after a clear().
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var s = Set(["a"]);
var it = s.iterator();
assertEq(it.next(), "a");
var it = s[std_iterator]();
assertIteratorResult(it.next(), "a", false);
s.clear();
s.add("b");
assertEq(it.next(), "b");
assertThrowsValue(it.next.bind(it), StopIteration);
assertIteratorResult(it.next(), "b", false);
assertIteratorResult(it.next(), undefined, true);

View File

@ -1,10 +1,10 @@
// A closed Set iterator does not visit new entries added after a clear().
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var s = Set();
var it = s.iterator();
assertThrowsValue(it.next.bind(it), StopIteration); // close the iterator
var it = s[std_iterator]();
assertIteratorResult(it.next(), undefined, true); // close the iterator
s.clear();
s.add("a");
assertThrowsValue(it.next.bind(it), StopIteration);
assertIteratorResult(it.next(), undefined, true);

View File

@ -1,6 +1,7 @@
/* test Set.prototype.forEach */
load(libdir + 'asserts.js');
load(libdir + 'iteration.js');
// testing success conditions of Set.prototype.forEach
@ -16,11 +17,11 @@ var initialSet = new Set(['a', 1, undefined]);
initialSet.forEach(callback);
// test that both the Sets are equal and are in same order
var iterator = initialSet.iterator();
var iterator = initialSet[std_iterator]();
var count = 0;
for (var v of testSet) {
assertEq(initialSet.has(v), true);
assertEq(iterator.next(), v);
assertIteratorResult(iterator.next(), v, false);
count++;
}
@ -46,12 +47,3 @@ var fn = 2;
assertThrowsInstanceOf(function() {
initialSet.forEach(fn);
}, TypeError, "Set.prototype.forEach should raise TypeError if callback is not a function");
// testing that Set#forEach uses internal next() function and does not stop when
// StopIteration exception is thrown
var s = new Set(["one", 1]);
Object.getPrototypeOf(s.iterator()).next = function () { throw "FAIL"; };
assertThrowsInstanceOf(function () {
s.forEach(function () { throw StopIteration; });
}, StopIteration, "Set.prototype.forEach should use intrinsic next method.");

View File

@ -1,10 +1,10 @@
// A Set iterator does not iterate over new entries added after it throws StopIteration.
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var set = Set();
var iter0 = set.iterator(), iter1 = set.iterator();
assertThrowsValue(function () { iter0.next(); }, StopIteration); // closes iter0
var iter0 = set[std_iterator](), iter1 = set[std_iterator]();
assertIteratorResult(iter0.next(), undefined, true); // closes iter0
set.add("x");
assertThrowsValue(function () { iter0.next(); }, StopIteration); // already closed
assertEq(iter1.next(), "x"); // was not yet closed
assertIteratorResult(iter0.next(), undefined, true); // already closed
assertIteratorResult(iter1.next(), "x", false); // was not yet closed

View File

@ -1,8 +1,10 @@
// A Set iterator keeps the data alive.
load(libdir + "referencesVia.js");
load(libdir + "iteration.js");
var key = {};
var set = Set([key]);
var iter = set.iterator();
var iter = set[std_iterator]();
referencesVia(iter, "**UNKNOWN SLOT 0**", set);
referencesVia(set, "key", key);

View File

@ -1,18 +1,20 @@
// map.iterator() and iter.next() are non-generic but work on cross-compartment wrappers.
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var g = newGlobal();
var iterator_fn = Set.prototype.iterator;
var iterator_fn = Set.prototype[std_iterator];
assertThrowsInstanceOf(function () { iterator_fn.call({}); }, TypeError);
assertThrowsInstanceOf(function () { iterator_fn.call(Map()); }, TypeError);
var setw = g.eval("Set(['x', 'y'])");
assertEq(iterator_fn.call(setw).next(), "x");
assertIteratorResult(iterator_fn.call(setw).next(), "x", false);
var next_fn = Set().iterator().next;
var next_fn = Set()[std_iterator]().next;
assertThrowsInstanceOf(function () { next_fn.call({}); }, TypeError);
assertThrowsInstanceOf(function () { next_fn.call(Map().iterator()); }, TypeError);
var iterw = setw.iterator();
assertEq(next_fn.call(iterw), "x");
assertEq(next_fn.call(iterw), "y");
assertThrowsValue(function () { next_fn.call(iterw); }, g.StopIteration);
assertThrowsInstanceOf(function () { next_fn.call(Map()[std_iterator]()); }, TypeError);
var iterw = setw[std_iterator]();
assertIteratorResult(next_fn.call(iterw), "x", false);
assertIteratorResult(next_fn.call(iterw), "y", false);
assertIteratorResult(next_fn.call(iterw), undefined, true);

View File

@ -1,7 +1,9 @@
// A map iterator can cope with removing the next entry.
load(libdir + "iteration.js");
var set = Set("abcd");
var iter = set.iterator();
var iter = set[std_iterator]();
var log = "";
for (let x of iter) {
log += x;

View File

@ -1,12 +1,12 @@
// A set iterator can cope with removing the next entry, then the current entry.
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var set = Set("abcd");
var iter = set.iterator();
assertEq(iter.next(), "a");
assertEq(iter.next(), "b");
var iter = set[std_iterator]();
assertIteratorResult(iter.next(), "a", false);
assertIteratorResult(iter.next(), "b", false);
set.delete("c");
set.delete("b");
assertEq(iter.next(), "d");
assertThrowsValue(function () { iter.next(); }, StopIteration);
assertIteratorResult(iter.next(), "d", false);
assertIteratorResult(iter.next(), undefined, true);

View File

@ -1,6 +1,6 @@
// Multiple live iterators on the same Set can cope with removing entries.
load(libdir + "asserts.js");
load(libdir + "iteration.js");
// Make a set.
var set = Set();
@ -12,9 +12,9 @@ for (var j = 0; j < SIZE; j++)
var NITERS = 5;
var iters = [];
for (var i = 0; i < NITERS; i++) {
var iter = set.iterator();
assertEq(iter.next(), 0);
assertEq(iter.next(), 1);
var iter = set[std_iterator]();
assertIteratorResult(iter.next(), 0, false);
assertIteratorResult(iter.next(), 1, false);
iters[i] = iter;
}
@ -26,6 +26,6 @@ for (var j = 0; j < SIZE; j += 2)
for (var i = 0; i < NITERS; i++) {
var iter = iters[i];
for (var j = 3; j < SIZE; j += 2)
assertEq(iter.next(), j);
assertThrowsValue(function () { iter.next(); }, StopIteration);
assertIteratorResult(iter.next(), j, false);
assertIteratorResult(iter.next(), undefined, true);
}

View File

@ -2,13 +2,13 @@
// entries that were not removed. (Compacting a Set must not be observable to
// script.)
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var set = Set();
for (var i = 0; i < 32; i++)
set.add(i);
var iter = set.iterator();
assertEq(iter.next(), 0);
var iter = set[std_iterator]();
assertIteratorResult(iter.next(), 0, false);
for (var i = 0; i < 30; i++)
set.delete(i);
assertEq(set.size, 2);
@ -16,5 +16,5 @@ for (var i = 32; i < 100; i++)
set.add(i); // eventually triggers compaction
for (var i = 30; i < 100; i++)
assertEq(iter.next(), i);
assertThrowsValue(function () { iter.next(); }, StopIteration);
assertIteratorResult(iter.next(), i, false);
assertIteratorResult(iter.next(), undefined, true);

View File

@ -1,5 +1,7 @@
// Set surfaces
load(libdir + "iteration.js");
var desc = Object.getOwnPropertyDescriptor(this, "Set");
assertEq(desc.enumerable, false);
assertEq(desc.configurable, true);
@ -43,4 +45,4 @@ checkMethod("clear", 0);
// Set.prototype.keys, .values, and .iterator are the same function object
assertEq(Set.prototype.keys, Set.prototype.values);
assertEq(Set.prototype.iterator, Set.prototype.values);
assertEq(Set.prototype[std_iterator], Set.prototype.values);

View File

@ -1,17 +1,17 @@
// set.keys() and set.values() return iterators over the elements
// and set.entries() returns an iterator that yields pairs [e, e].
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var data = [1, 2, 3, 4];
var s = Set(data);
var ki = s.keys();
assertEq(ki.next(), 1);
assertEq(ki.next(), 2);
assertEq(ki.next(), 3);
assertEq(ki.next(), 4);
assertThrowsValue(function () { ki.next(); }, StopIteration);
assertIteratorResult(ki.next(), 1, false);
assertIteratorResult(ki.next(), 2, false);
assertIteratorResult(ki.next(), 3, false);
assertIteratorResult(ki.next(), 4, false);
assertIteratorResult(ki.next(), undefined, true);
assertEq([...s.keys()].toSource(), data.toSource());
assertEq([...s.values()].toSource(), data.toSource());

View File

@ -1,12 +1,16 @@
// collection.iterator() returns an Iterator object.
load(libdir + "iteration.js");
function test(obj, name) {
var iter = obj.iterator();
var iter = obj[std_iterator]();
assertEq(typeof iter, "object");
assertEq(iter instanceof Iterator, true);
assertEq(iter.toString(), "[object " + obj.constructor.name + " Iterator]");
}
test([]);
// FIXME: Until arrays are converted to use the new iteration protocol,
// toString on this iterator doesn't work. Bug 919948.
// test([]);
test(new Map);
test(new Set);

View File

@ -1,10 +1,12 @@
// An iterator keeps its data alive.
load(libdir + "iteration.js");
load(libdir + "referencesVia.js");
var key = {};
function test(obj, edgeName) {
var iter = obj.iterator();
var iter = obj[std_iterator]();
referencesVia(iter, "**UNKNOWN SLOT 0**", obj);
referencesVia(obj, edgeName, key);
}

View File

@ -1,8 +1,10 @@
// All iterators of the same collection type share their immediate prototype.
// Those prototype objects in turn inherit directly from Iterator.prototype.
load(libdir + "iteration.js");
function test(obj0, obj1) {
var iter0 = obj0.iterator(), iter1 = obj1.iterator();
var iter0 = obj0[std_iterator](), iter1 = obj1[std_iterator]();
var proto = Object.getPrototypeOf(iter0);
assertEq(Object.getPrototypeOf(iter1), proto);
assertEq(Object.getPrototypeOf(proto), Iterator.prototype);

View File

@ -1,8 +1,10 @@
// Iterators of different collection types have different prototypes.
var aproto = Object.getPrototypeOf(Array().iterator());
var mproto = Object.getPrototypeOf(Map().iterator());
var sproto = Object.getPrototypeOf(Set().iterator());
load(libdir + "iteration.js");
var aproto = Object.getPrototypeOf(Array()[std_iterator]());
var mproto = Object.getPrototypeOf(Map()[std_iterator]());
var sproto = Object.getPrototypeOf(Set()[std_iterator]());
assertEq(aproto !== mproto, true);
assertEq(aproto !== sproto, true);
assertEq(mproto !== sproto, true);

View File

@ -1,20 +1,21 @@
// Iterator prototype surfaces.
load(libdir + "asserts.js");
load(libdir + "iteration.js");
function test(constructor) {
var proto = Object.getPrototypeOf(constructor().iterator());
var proto = Object.getPrototypeOf(constructor()[std_iterator]());
var names = Object.getOwnPropertyNames(proto);
assertEq(names.length, 1);
assertEq(names[0], 'next');
names.sort();
assertDeepEq(names, [std_iterator, 'next']);
var desc = Object.getOwnPropertyDescriptor(proto, 'next');
assertEq(desc.configurable, true);
assertEq(desc.enumerable, false);
assertEq(desc.writable, true);
assertEq(proto.iterator(), proto);
assertThrowsValue(function () { proto.next(); }, StopIteration);
assertEq(proto[std_iterator](), proto);
assertIteratorResult(proto.next(), undefined, true);
}
//test(Array);

View File

@ -1,8 +1,10 @@
// for-of can iterate arguments objects.
// Arguments objects do not have a .iterator() method by default.
load(libdir + "iteration.js");
// Arguments objects do not have a .@@iterator() method by default.
// Install one on Object.prototype.
Object.prototype.iterator = Array.prototype.iterator;
Object.prototype[std_iterator] = Array.prototype[std_iterator];
var s;
function test() {

View File

@ -1,12 +1,14 @@
// for-of can iterate arguments objects after returning.
load(libdir + "iteration.js");
function f() {
return arguments;
}
var s = '';
var args = f('a', 'b', 'c');
Object.prototype.iterator = Array.prototype.iterator;
Object.prototype[std_iterator] = Array.prototype[std_iterator];
for (var v of args)
s += v;
assertEq(s, 'abc');

View File

@ -1,6 +1,8 @@
// for-of can iterate strict arguments objects.
Object.prototype.iterator = Array.prototype.iterator;
load(libdir + "iteration.js");
Object.prototype[std_iterator] = Array.prototype[std_iterator];
var s;
function test() {

View File

@ -1,6 +1,8 @@
// for-of can iterate arguments objects for other active frames.
Object.prototype.iterator = Array.prototype.iterator;
load(libdir + "iteration.js");
Object.prototype[std_iterator] = Array.prototype[std_iterator];
var s;
function g(obj) {

View File

@ -1,6 +1,8 @@
// for-of can iterate strict arguments objects in non-strict code.
Object.prototype.iterator = Array.prototype.iterator;
load(libdir + "iteration.js");
Object.prototype[std_iterator] = Array.prototype[std_iterator];
var s;
function g(obj) {

View File

@ -1,6 +1,8 @@
// Changing arguments.length affects a for-of loop iterating over arguments.
Object.prototype.iterator = Array.prototype.iterator;
load(libdir + "iteration.js");
Object.prototype[std_iterator] = Array.prototype[std_iterator];
var s;
function f() {

View File

@ -1,6 +1,8 @@
// Changing arguments.length during a for-of loop iterating over arguments affects the loop.
Object.prototype.iterator = Array.prototype.iterator;
load(libdir + "iteration.js");
Object.prototype[std_iterator] = Array.prototype[std_iterator];
var s;
function f() {

View File

@ -1,10 +1,12 @@
// for-of on an Array consults the prototype chain when it encounters a hole.
load(libdir + "iteration.js");
var m = {1: 'peek'};
var a = [0, , 2, 3];
a.__proto__ = m;
var log = [];
Object.prototype.iterator = Array.prototype.iterator;
Object.prototype[std_iterator] = Array.prototype[std_iterator];
for (var x of a)
log.push(x);
assertEq(log[1], 'peek');

View File

@ -1,13 +1,15 @@
// Superficial tests for iterators created by Array.prototype.iterator
var proto = Object.getPrototypeOf([].iterator());
load(libdir + "iteration.js");
var proto = Object.getPrototypeOf([][std_iterator]());
assertEq(Object.getPrototypeOf(proto), Iterator.prototype);
function check(it) {
assertEq(typeof it, 'object');
assertEq(Object.getPrototypeOf(it), proto);
assertEq(Object.getOwnPropertyNames(it).length, 0);
assertEq(it.iterator(), it);
assertEq(it[std_iterator](), it);
// for-in enumerates the iterator's properties.
it.x = 0;
@ -17,6 +19,6 @@ function check(it) {
assertEq(s, 'x.');
}
check([].iterator());
check(Array.prototype.iterator.call({}));
check(Array.prototype.iterator.call(undefined));
check([][std_iterator]());
check(Array.prototype[std_iterator].call({}));
check(Array.prototype[std_iterator].call(undefined));

View File

@ -0,0 +1,6 @@
// The completion value of a for-of loop is the completion value of the
// last evaluation of the body, or undefined.
assertEq(eval("for (let x of [1, 2, 3]) { x }"), 3);
assertEq(eval("for (let x of [1, 2, 3]) { x * 2 }"), 6);
assertEq(eval("for (let x of []) { x }"), undefined);

View File

@ -1,4 +1,6 @@
// Breaking out of a for-of loop over a generator-iterator closes the iterator.
// Breaking out of a for-of loop over a generator-iterator does not close the iterator.
load(libdir + "iteration.js");
function range(n) {
for (var i = 0; i < n; i++)
@ -15,4 +17,4 @@ for (var x of r) {
s += '/';
for (var y of r)
s += y;
assertEq(s, '01234/');
assertEq(s, '01234/56789');

View File

@ -1,43 +0,0 @@
// Named break closes the right generator-iterators.
function g() {
for (;;)
yield 1;
}
function isClosed(it) {
try {
it.next();
return false;
} catch (exc) {
if (exc !== StopIteration)
throw exc;
return true;
}
}
var a = g(), b = g(), c = g(), d = g(), e = g(), f = g();
for (var aa of a) {
b_: for (var bb of b) {
c_: for (var cc of c) {
d_: for (var dd of d) {
e_: for (var ee of e) {
for (var ff of f)
break c_;
}
}
}
assertEq(isClosed(a), false);
assertEq(isClosed(b), false);
assertEq(isClosed(c), true);
assertEq(isClosed(d), true);
assertEq(isClosed(e), true);
assertEq(isClosed(f), true);
break b_;
}
assertEq(isClosed(a), false);
assertEq(isClosed(b), true);
break;
}
assertEq(isClosed(a), true);

View File

@ -0,0 +1,15 @@
// Manually advancing the iterator.
load(libdir + 'iteration.js');
function* g(n) { for (var i=0; i<n; i++) yield i; }
var inner = g(20);
var n = 0;
for (var x of inner) {
assertEq(x, n * 2);
assertIteratorResult(inner.next(), n * 2 + 1, false);
n++;
}
assertEq(n, 10);

View File

@ -0,0 +1,22 @@
// For-of passes one arg to "next".
load(libdir + 'iteration.js')
var log = '';
function Iter() {
function next() {
log += 'n';
assertEq(arguments.length, 1)
assertEq(arguments[0], undefined)
return { get value() { throw 42; }, done: true }
}
this[std_iterator] = function () { return this; }
this.next = next;
}
for (var x of new Iter())
throw 'not reached';
assertEq(log, 'n');

View File

@ -0,0 +1,41 @@
// Test for-of with iter.next and monkeypatching.
function* g(n) { for (var i=0; i<n; i++) yield i; }
var GeneratorObjectPrototype = Object.getPrototypeOf(g).prototype;
var GeneratorObjectPrototype_next = GeneratorObjectPrototype.next;
// Monkeypatch next on an iterator.
var inner = g(20);
var n = 0;
for (let x of inner) {
if (n == 0) {
assertEq(x, n++);
} else if (n == 1) {
assertEq(x, n++);
inner.next = function() { return { value: 42, done: false }; };
} else if (n == 2) {
assertEq(x, 42);
inner.next = function() { return { value: 100, done: true }; };
} else
throw 'not reached';
}
// Monkeypatch next on the prototype.
var inner = g(20);
var n = 0;
for (let x of inner) {
if (n == 0) {
assertEq(x, n++);
} else if (n == 1) {
assertEq(x, n++);
GeneratorObjectPrototype.next =
function() { return { value: 42, done: false }; };
} else if (n == 2) {
n++;
assertEq(x, 42);
GeneratorObjectPrototype.next = GeneratorObjectPrototype_next;
} else if (n <= 20) {
assertEq(x, n++ - 1);
} else
throw 'not reached';
}

View File

@ -1,10 +1,11 @@
// An exception thrown from a proxy trap while getting the .iterator method is propagated.
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var p = Proxy.create({
getPropertyDescriptor: function (name) {
if (name == "iterator")
if (name == std_iterator)
throw "fit";
return undefined;
}

View File

@ -7,5 +7,7 @@
// Deleting Array.prototype.iterator makes for-of stop working on arrays.
load(libdir + "asserts.js");
delete Array.prototype.iterator;
load(libdir + "iteration.js");
delete Array.prototype[std_iterator];
assertThrowsInstanceOf(function () { for (var x of []) ; }, TypeError);

View File

@ -1,8 +1,10 @@
// Replacing Array.prototype.iterator with something non-callable makes for-of throw.
load(libdir + "asserts.js");
load(libdir + "iteration.js");
function test(v) {
Array.prototype.iterator = v;
Array.prototype[std_iterator] = v;
assertThrowsInstanceOf(function () { for (var x of []) ; }, TypeError);
}
test(undefined);

View File

@ -1,6 +1,8 @@
// Replacing Array.prototype.iterator with a generator affects for-of behavior.
Array.prototype.iterator = function () {
load(libdir + "iteration.js");
Array.prototype[std_iterator] = function* () {
for (var i = this.length; --i >= 0; )
yield this[i];
};

View File

@ -1,7 +1,10 @@
// Giving an Array an own .iterator property affects for-of.
load(libdir + "asserts.js");
load(libdir + "iteration.js");
var a = [];
a.iterator = function () {
a[std_iterator] = function* () {
yield 'o';
yield 'k';
};
@ -10,6 +13,5 @@ for (var v of a)
s += v;
assertEq(s, 'ok');
load(libdir + "asserts.js");
a.iterator = undefined;
a[std_iterator] = undefined;
assertThrowsInstanceOf(function () { for (var v of a) ; }, TypeError);

View File

@ -1,6 +1,8 @@
// Deleting String.prototype.iterator makes for-of stop working on strings.
load(libdir + "asserts.js");
delete String.prototype.iterator;
load(libdir + "iteration.js");
delete String.prototype[std_iterator];
assertThrowsInstanceOf(function () { for (var v of "abc") ; }, TypeError);
assertThrowsInstanceOf(function () { for (var v of new String("abc")) ; }, TypeError);

View File

@ -2,7 +2,9 @@
// causes a TypeError at the next iteration.
load(libdir + "asserts.js");
var iterProto = Object.getPrototypeOf([].iterator());
load(libdir + "iteration.js");
var iterProto = Object.getPrototypeOf([][std_iterator]());
var s = '';
assertThrowsInstanceOf(function () {
for (var v of ['duck', 'duck', 'duck', 'goose', 'FAIL']) {

View File

@ -1,7 +1,9 @@
// A for-of loop exits if the iterator's .next method throws another compartment's StopIteration.
// Results from another compartment are correctly interpreted by for-of.
load(libdir + "iteration.js");
var g = newGlobal();
var it = g.eval("({ iterator: function () { return this; }, " +
"next: function () { throw StopIteration; } });");
var it = g.eval("({ '" + std_iterator + "': function () { return this; }, " +
"next: function () { return { done: true } } });");
for (x of it)
throw 'FAIL';

View File

@ -1,13 +1,15 @@
// for-of on a proxy causes a predictable sequence of trap calls.
load(libdir + "iteration.js");
var s = '';
var i = 0;
var next_fn = Proxy.createFunction({}, function () {
s += "n";
if (i == 3)
throw StopIteration;
return i++;
return { value: undefined, done: true };
return { value: i++, done: false };
});
var it = Proxy.create({
@ -29,7 +31,7 @@ var iterator_fn = Proxy.createFunction({}, function () {
var obj = Proxy.create({
get: function (receiver, name) {
assertEq(name, "iterator");
assertEq(name, std_iterator);
s += "I";
return iterator_fn;
}

View File

@ -0,0 +1,23 @@
// Test that each yield* loop just checks "done", and "value" is only
// fetched once at the end.
load(libdir + 'iteration.js');
var log = "";
function Iter(val, count) {
function next() {
return {
get done() { log += "d"; return count-- == 0; },
get value() { log += "v"; return val; }
}
}
this[std_iterator] = function() { return this; };
this.next = next;
}
for (var x of new Iter(42, 5))
assertEq(x, 42);
assertEq(log, "dvdvdvdvdvd");

View File

@ -1,4 +1,5 @@
// |jit-test| error: is not iterable
// |jit-test| error: TypeError
try {
x = []
y = function() {}

View File

@ -2469,6 +2469,12 @@ BaselineCompiler::emit_JSOP_ENTERLET1()
return emitEnterBlock();
}
bool
BaselineCompiler::emit_JSOP_ENTERLET2()
{
return emitEnterBlock();
}
typedef bool (*LeaveBlockFn)(JSContext *, BaselineFrame *);
static const VMFunction LeaveBlockInfo = FunctionInfo<LeaveBlockFn>(jit::LeaveBlock);

View File

@ -148,6 +148,7 @@ namespace jit {
_(JSOP_ENTERBLOCK) \
_(JSOP_ENTERLET0) \
_(JSOP_ENTERLET1) \
_(JSOP_ENTERLET2) \
_(JSOP_LEAVEBLOCK) \
_(JSOP_LEAVEBLOCKEXPR) \
_(JSOP_LEAVEFORLETIN) \

View File

@ -1242,6 +1242,7 @@ IonBuilder::snoopControlFlow(JSOp op)
case SRC_WHILE:
case SRC_FOR_IN:
case SRC_FOR_OF:
// while (cond) { }
return whileOrForInLoop(sn);
@ -2027,7 +2028,7 @@ IonBuilder::processDoWhileCondEnd(CFGState &state)
IonBuilder::ControlStatus
IonBuilder::processWhileCondEnd(CFGState &state)
{
JS_ASSERT(JSOp(*pc) == JSOP_IFNE);
JS_ASSERT(JSOp(*pc) == JSOP_IFNE || JSOp(*pc) == JSOP_IFEQ);
// Balance the stack past the IFNE.
MDefinition *ins = current->pop();
@ -2038,7 +2039,11 @@ IonBuilder::processWhileCondEnd(CFGState &state)
if (!body || !state.loop.successor)
return ControlStatus_Error;
MTest *test = MTest::New(ins, body, state.loop.successor);
MTest *test;
if (JSOp(*pc) == JSOP_IFNE)
test = MTest::New(ins, body, state.loop.successor);
else
test = MTest::New(ins, state.loop.successor, body);
current->end(test);
state.state = CFGState::WHILE_LOOP_BODY;
@ -2610,7 +2615,7 @@ IonBuilder::whileOrForInLoop(jssrcnote *sn)
// ...
// IFNE ; goes to LOOPHEAD
// for (x in y) { } loops are similar; the cond will be a MOREITER.
JS_ASSERT(SN_TYPE(sn) == SRC_FOR_IN || SN_TYPE(sn) == SRC_WHILE);
JS_ASSERT(SN_TYPE(sn) == SRC_FOR_OF || SN_TYPE(sn) == SRC_FOR_IN || SN_TYPE(sn) == SRC_WHILE);
int ifneOffset = js_GetSrcNoteOffset(sn, 0);
jsbytecode *ifne = pc + ifneOffset;
JS_ASSERT(ifne > pc);

View File

@ -269,7 +269,7 @@ MSG_DEF(JSMSG_BAD_GENERATOR_SYNTAX, 215, 1, JSEXN_SYNTAXERR, "{0} expression m
MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 216, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side")
MSG_DEF(JSMSG_UNUSED217, 217, 0, JSEXN_NONE, "")
MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE, 218, 0, JSEXN_TYPEERR, "reduce of empty array with no initial value")
MSG_DEF(JSMSG_UNUSED219, 219, 0, JSEXN_NONE, "")
MSG_DEF(JSMSG_BAD_SYMBOL, 219, 1, JSEXN_TYPEERR, "{0} is not a well-known @@-symbol")
MSG_DEF(JSMSG_BAD_DELETE_OPERAND, 220, 0, JSEXN_REFERENCEERR, "invalid delete operand")
MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 221, 0, JSEXN_REFERENCEERR, "invalid increment/decrement operand")
MSG_DEF(JSMSG_UNEXPECTED_TYPE, 222, 2, JSEXN_TYPEERR, "{0} is {1}")

View File

@ -252,6 +252,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
case JSOP_EVAL:
case JSOP_SPREADEVAL:
case JSOP_ENTERLET2:
case JSOP_ENTERWITH:
canTrackVars = false;
break;

View File

@ -4202,7 +4202,15 @@ JS_DefineFunctions(JSContext *cx, JSObject *objArg, const JSFunctionSpec *fs)
RootedObject ctor(cx);
for (; fs->name; fs++) {
RootedAtom atom(cx, Atomize(cx, fs->name, strlen(fs->name)));
RootedAtom atom(cx);
// If the name starts with "@@", it must be a well-known symbol.
if (fs->name[0] != '@' || fs->name[1] != '@')
atom = Atomize(cx, fs->name, strlen(fs->name));
else if (strcmp(fs->name, "@@iterator") == 0)
// FIXME: This atom should be a symbol: bug 918828.
atom = cx->names().std_iterator;
else
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_SYMBOL, fs->name);
if (!atom)
return false;

View File

@ -2892,6 +2892,7 @@ static const JSFunctionSpec array_methods[] = {
JS_SELF_HOSTED_FN("find", "ArrayFind", 1,0),
JS_SELF_HOSTED_FN("findIndex", "ArrayFindIndex", 1,0),
JS_SELF_HOSTED_FN("@@iterator", "ArrayIterator", 0,0),
JS_FN("iterator", JS_ArrayIterator, 0,0),
JS_FS_END
};

View File

@ -565,12 +565,12 @@ bool
js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleValue vp)
{
if (flags == JSITER_FOR_OF) {
// for-of loop. The iterator is simply |obj.iterator()|.
// for-of loop. The iterator is simply |obj[@@iterator]()|.
RootedValue method(cx);
if (!JSObject::getProperty(cx, obj, obj, cx->names().iterator, &method))
if (!JSObject::getProperty(cx, obj, obj, cx->names().std_iterator, &method))
return false;
// Throw if obj.iterator isn't callable. js::Invoke is about to check
// Throw if obj[@@iterator] isn't callable. js::Invoke is about to check
// for this kind of error anyway, but it would throw an inscrutable
// error message about |method| rather than this nice one about |obj|.
if (!method.isObject() || !method.toObject().isCallable()) {
@ -593,6 +593,8 @@ js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleVa
return true;
}
JS_ASSERT(!(flags & JSITER_FOR_OF));
Vector<Shape *, 8> shapes(cx);
uint32_t key = 0;
@ -725,6 +727,30 @@ js::GetIteratorObject(JSContext *cx, HandleObject obj, uint32_t flags)
return &value.toObject();
}
JSObject *
js::CreateItrResultObject(JSContext *cx, HandleValue value, bool done)
{
// FIXME: We can cache the iterator result object shape somewhere.
AssertHeapIsIdle(cx);
RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx));
if (!proto)
return nullptr;
RootedObject obj(cx, NewObjectWithGivenProto(cx, &JSObject::class_, proto, cx->global()));
if (!obj)
return nullptr;
if (!JSObject::defineProperty(cx, obj, cx->names().value, value))
return nullptr;
RootedValue doneBool(cx, BooleanValue(done));
if (!JSObject::defineProperty(cx, obj, cx->names().done, doneBool))
return nullptr;
return obj;
}
bool
js_ThrowStopIteration(JSContext *cx)
{
@ -781,14 +807,6 @@ iterator_next_impl(JSContext *cx, CallArgs args)
return js_IteratorNext(cx, thisObj, args.rval());
}
static bool
iterator_iterator(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().set(args.thisv());
return true;
}
bool
iterator_next(JSContext *cx, unsigned argc, Value *vp)
{
@ -797,7 +815,7 @@ iterator_next(JSContext *cx, unsigned argc, Value *vp)
}
static const JSFunctionSpec iterator_methods[] = {
JS_FN("iterator", iterator_iterator, 0, 0),
JS_SELF_HOSTED_FN("@@iterator", "LegacyIteratorShim", 0, 0),
JS_FN("next", iterator_next, 0, 0),
JS_FS_END
};
@ -948,6 +966,7 @@ const Class ElementIteratorObject::class_ = {
};
const JSFunctionSpec ElementIteratorObject::methods[] = {
JS_SELF_HOSTED_FN("@@iterator", "LegacyIteratorShim", 0, 0),
JS_FN("next", next, 0, 0),
JS_FS_END
};
@ -1308,6 +1327,38 @@ const Class StopIterationObject::class_ = {
NULL /* construct */
};
bool
ForOfIterator::next(MutableHandleValue vp, bool *done)
{
JS_ASSERT(iterator);
RootedValue method(cx);
if (!JSObject::getProperty(cx, iterator, iterator, cx->names().next, &method))
return false;
InvokeArgs args(cx);
if (!args.init(1))
return false;
args.setCallee(method);
args.setThis(ObjectValue(*iterator));
args[0].setUndefined();
if (!Invoke(cx, args))
return false;
RootedObject resultObj(cx, ToObject(cx, args.rval()));
if (!resultObj)
return false;
RootedValue doneVal(cx);
if (!JSObject::getProperty(cx, resultObj, resultObj, cx->names().done, &doneVal))
return false;
*done = ToBoolean(doneVal);
if (*done) {
vp.setUndefined();
return true;
}
return JSObject::getProperty(cx, resultObj, resultObj, cx->names().value, vp);
}
/*** Generators **********************************************************************************/
template<typename T>
@ -1796,14 +1847,14 @@ NativeMethod(JSContext *cx, unsigned argc, Value *vp)
#define JS_METHOD(name, T, impl, len, attrs) JS_FN(name, (NativeMethod<T,impl>), len, attrs)
static const JSFunctionSpec star_generator_methods[] = {
JS_FN("iterator", iterator_iterator, 0, 0),
JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0),
JS_METHOD("next", StarGeneratorObject, star_generator_next, 1, 0),
JS_METHOD("throw", StarGeneratorObject, star_generator_throw, 1, 0),
JS_FS_END
};
static const JSFunctionSpec legacy_generator_methods[] = {
JS_FN("iterator", iterator_iterator, 0, 0),
JS_SELF_HOSTED_FN("@@iterator", "LegacyGeneratorIteratorShim", 0, 0),
// "send" is an alias for "next".
JS_METHOD("next", LegacyGeneratorObject, legacy_generator_next, 1, JSPROP_ROPERM),
JS_METHOD("send", LegacyGeneratorObject, legacy_generator_next, 1, JSPROP_ROPERM),

View File

@ -235,23 +235,6 @@ js_ThrowStopIteration(JSContext *cx);
namespace js {
/*
* Get the next value from an iterator object.
*
* On success, store the next value in *vp and return true; if there are no
* more values, store the magic value JS_NO_ITER_VALUE in *vp and return true.
*/
inline bool
Next(JSContext *cx, HandleObject iter, MutableHandleValue vp)
{
if (!js_IteratorMore(cx, iter, vp))
return false;
if (vp.toBoolean())
return js_IteratorNext(cx, iter, vp);
vp.setMagic(JS_NO_ITER_VALUE);
return true;
}
/*
* Convenience class for imitating a JS level for-of loop. Typical usage:
*
@ -275,57 +258,31 @@ class ForOfIterator
private:
JSContext *cx;
RootedObject iterator;
RootedValue currentValue;
bool ok;
bool closed;
ForOfIterator(const ForOfIterator &) MOZ_DELETE;
ForOfIterator &operator=(const ForOfIterator &) MOZ_DELETE;
public:
ForOfIterator(JSContext *cx, const Value &iterable)
: cx(cx), iterator(cx, NULL), currentValue(cx), closed(false)
{
ForOfIterator(JSContext *cx) : cx(cx), iterator(cx) { }
bool init(HandleValue iterable) {
RootedValue iterv(cx, iterable);
ok = ValueToIterator(cx, JSITER_FOR_OF, &iterv);
iterator = ok ? &iterv.get().toObject() : NULL;
}
~ForOfIterator() {
if (!closed)
close();
}
bool next() {
JS_ASSERT(!closed);
ok = ok && Next(cx, iterator, &currentValue);
return ok && !currentValue.get().isMagic(JS_NO_ITER_VALUE);
}
MutableHandleValue value() {
JS_ASSERT(ok);
JS_ASSERT(!closed);
return &currentValue;
}
bool close() {
JS_ASSERT(!closed);
closed = true;
if (!iterator)
if (!ValueToIterator(cx, JSITER_FOR_OF, &iterv))
return false;
bool throwing = cx->isExceptionPending();
RootedValue exc(cx);
if (throwing) {
exc = cx->getPendingException();
cx->clearPendingException();
}
bool closedOK = CloseIterator(cx, iterator);
if (throwing && closedOK)
cx->setPendingException(exc);
return ok && !throwing && closedOK;
iterator = &iterv.get().toObject();
return true;
}
bool next(MutableHandleValue val, bool *done);
};
/*
* Create an object of the form { value: VALUE, done: DONE }.
* ES6 draft from 2013-09-05, section 25.4.3.4.
*/
extern JSObject *
CreateItrResultObject(JSContext *cx, js::HandleValue value, bool done);
} /* namespace js */
/*

View File

@ -119,9 +119,11 @@ js_GetVariableBytecodeLength(jsbytecode *pc)
static uint32_t
NumBlockSlots(JSScript *script, jsbytecode *pc)
{
JS_ASSERT(*pc == JSOP_ENTERBLOCK || *pc == JSOP_ENTERLET0 || *pc == JSOP_ENTERLET1);
JS_ASSERT(*pc == JSOP_ENTERBLOCK ||
*pc == JSOP_ENTERLET0 || *pc == JSOP_ENTERLET1 || *pc == JSOP_ENTERLET2);
JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET0_LENGTH);
JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET1_LENGTH);
JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET2_LENGTH);
return script->getObject(GET_UINT32_INDEX(pc))->as<StaticBlockObject>().slotCount();
}
@ -146,6 +148,8 @@ js::StackUses(JSScript *script, jsbytecode *pc)
return NumBlockSlots(script, pc);
case JSOP_ENTERLET1:
return NumBlockSlots(script, pc) + 1;
case JSOP_ENTERLET2:
return NumBlockSlots(script, pc) + 2;
default:
/* stack: fun, this, [argc arguments] */
JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL || op == JSOP_EVAL ||
@ -163,7 +167,11 @@ js::StackDefs(JSScript *script, jsbytecode *pc)
return cs.ndefs;
uint32_t n = NumBlockSlots(script, pc);
return op == JSOP_ENTERLET1 ? n + 1 : n;
if (op == JSOP_ENTERLET1)
return n + 1;
if (op == JSOP_ENTERLET2)
return n + 2;
return n;
}
static const char * const countBaseNames[] = {
@ -1051,7 +1059,8 @@ GetBlockChainAtPC(JSContext *cx, JSScript *script, jsbytecode *pc)
switch (op) {
case JSOP_ENTERBLOCK:
case JSOP_ENTERLET0:
case JSOP_ENTERLET1: {
case JSOP_ENTERLET1:
case JSOP_ENTERLET2: {
JSObject *child = script->getObject(p);
JS_ASSERT_IF(blockChain, child->as<BlockObject>().stackDepth() >=
blockChain->as<BlockObject>().stackDepth());

View File

@ -400,13 +400,14 @@ OPDEF(JSOP_ENTERLET0, 185,"enterlet0", NULL, 5, -1, -1, JOF_OBJECT)
/* Enter a let block/expr whose slots are 1 below the top of the stack. */
OPDEF(JSOP_ENTERLET1, 186,"enterlet1", NULL, 5, -1, -1, JOF_OBJECT)
/* Enter a let block/expr whose slots are 2 below the top of the stack. */
OPDEF(JSOP_ENTERLET2, 187,"enterlet2", NULL, 5, -1, -1, JOF_OBJECT)
/*
* Opcode to hold 24-bit immediate integer operands.
*/
OPDEF(JSOP_UINT24, 187,"uint24", NULL, 4, 0, 1, JOF_UINT24)
OPDEF(JSOP_UINT24, 188,"uint24", NULL, 4, 0, 1, JOF_UINT24)
OPDEF(JSOP_UNUSED188, 188,"unused188", NULL, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED189, 189,"unused189", NULL, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED190, 190,"unused190", NULL, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED191, 191,"unused191", NULL, 1, 0, 0, JOF_BYTE)

View File

@ -3658,6 +3658,7 @@ static const JSFunctionSpec string_methods[] = {
JS_FN("sub", str_sub, 0,0),
#endif
JS_SELF_HOSTED_FN("@@iterator", "ArrayIterator", 0,0),
JS_FN("iterator", JS_ArrayIterator, 0,0),
JS_FS_END
};

View File

@ -1804,6 +1804,7 @@ SrcNotes(JSContext *cx, HandleScript script, Sprinter *sp)
break;
case SRC_FOR_IN:
case SRC_FOR_OF:
Sprint(sp, " closingjump %u", unsigned(js_GetSrcNoteOffset(sn, 0)));
break;

View File

@ -17,6 +17,8 @@ function* g() { yield 1; }
var GeneratorFunctionPrototype = Object.getPrototypeOf(g);
var GeneratorFunction = GeneratorFunctionPrototype.constructor;
var GeneratorObjectPrototype = GeneratorFunctionPrototype.prototype;
// FIXME: This should be a symbol.
var std_iterator = "@@iterator";
// A generator function should have the same set of properties as any
@ -64,7 +66,7 @@ function TestGeneratorObjectPrototype() {
assertEq(Object.getPrototypeOf((function*(){yield 1}).prototype),
GeneratorObjectPrototype);
var expected_property_names = ["iterator", "next", "throw", "constructor"];
var expected_property_names = ["next", "throw", "constructor", std_iterator];
var found_property_names =
Object.getOwnPropertyNames(GeneratorObjectPrototype);

View File

@ -37,6 +37,7 @@
macro(constructor, constructor, "constructor") \
macro(currency, currency, "currency") \
macro(currencyDisplay, currencyDisplay, "currencyDisplay") \
macro(std_iterator, std_iterator, "@@iterator") \
macro(DateTimeFormat, DateTimeFormat, "DateTimeFormat") \
macro(DateTimeFormatFormatGet, DateTimeFormatFormatGet, "Intl_DateTimeFormat_format_get") \
macro(decodeURI, decodeURI, "decodeURI") \

View File

@ -1479,7 +1479,6 @@ BEGIN_CASE(JSOP_UNUSED180)
BEGIN_CASE(JSOP_UNUSED181)
BEGIN_CASE(JSOP_UNUSED182)
BEGIN_CASE(JSOP_UNUSED183)
BEGIN_CASE(JSOP_UNUSED188)
BEGIN_CASE(JSOP_UNUSED189)
BEGIN_CASE(JSOP_UNUSED190)
BEGIN_CASE(JSOP_UNUSED200)
@ -3044,21 +3043,26 @@ BEGIN_CASE(JSOP_SPREAD)
RootedObject &arr = rootObject0;
arr = &regs.sp[-3].toObject();
const Value iterable = regs.sp[-1];
ForOfIterator iter(cx, iterable);
ForOfIterator iter(cx);
RootedValue &iterVal = rootValue0;
while (iter.next()) {
iterVal.set(iterable);
if (!iter.init(iterVal))
goto error;
while (true) {
bool done;
if (!iter.next(&iterVal, &done))
goto error;
if (done)
break;
if (count == INT32_MAX) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
JSMSG_SPREAD_TOO_LARGE);
goto error;
}
iterVal = iter.value();
if (!JSObject::defineElement(cx, arr, count++, iterVal, nullptr, nullptr,
JSPROP_ENUMERATE))
goto error;
}
if (!iter.close())
goto error;
regs.sp[-2].setInt32(count);
regs.sp--;
}
@ -3175,6 +3179,7 @@ END_CASE(JSOP_DEBUGGER)
BEGIN_CASE(JSOP_ENTERBLOCK)
BEGIN_CASE(JSOP_ENTERLET0)
BEGIN_CASE(JSOP_ENTERLET1)
BEGIN_CASE(JSOP_ENTERLET2)
{
StaticBlockObject &blockObj = script->getObject(regs.pc)->as<StaticBlockObject>();

View File

@ -558,6 +558,20 @@ js::intrinsic_HaveSameClass(JSContext *cx, unsigned argc, Value *vp)
return true;
}
static bool
intrinsic_GetIteratorPrototype(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
JS_ASSERT(args.length() == 0);
JSObject *obj = cx->global()->getOrCreateIteratorPrototype(cx);
if (!obj)
return false;
args.rval().setObject(*obj);
return true;
}
/*
* ParallelTestsShouldPass(): Returns false if we are running in a
* mode (such as --ion-eager) that is known to cause additional
@ -639,6 +653,8 @@ const JSFunctionSpec intrinsic_functions[] = {
JS_FN("NewObjectWithClassPrototype", intrinsic_NewObjectWithClassPrototype, 1,0),
JS_FN("HaveSameClass", intrinsic_HaveSameClass, 2,0),
JS_FN("GetIteratorPrototype", intrinsic_GetIteratorPrototype, 0,0),
JS_FN("ForkJoin", intrinsic_ForkJoin, 2,0),
JS_FN("ForkJoinSlices", intrinsic_ForkJoinSlices, 0,0),
JS_FN("NewParallelArray", intrinsic_NewParallelArray, 3,0),

Some files were not shown because too many files have changed in this diff Show More