diff --git a/content/html/content/test/test_formelements.html b/content/html/content/test/test_formelements.html index a4f849888c0f..c49f9f7117af 100644 --- a/content/html/content/test/test_formelements.html +++ b/content/html/content/test/test_formelements.html @@ -52,7 +52,7 @@ is(names[8], "z", "Entry 9") is(names[9], "something", "Entry 10") is(names[10], "namedItem", "Entry 11") is(names[11], "item", "Entry 12") -is(names[12], "iterator", "Entry 13") +is(names[12], "@@iterator", "Entry 13") is(names[13], "length", "Entry 14") diff --git a/content/html/content/test/test_htmlcollection.html b/content/html/content/test/test_htmlcollection.html index 715877d2d21c..556f482c8d84 100644 --- a/content/html/content/test/test_htmlcollection.html +++ b/content/html/content/test/test_htmlcollection.html @@ -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") diff --git a/content/html/content/test/test_rowscollection.html b/content/html/content/test/test_rowscollection.html index 2eed607cb3b3..650d7b89a1c2 100644 --- a/content/html/content/test/test_rowscollection.html +++ b/content/html/content/test/test_rowscollection.html @@ -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") diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 7e1bbbb684c1..90a19d153691 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -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) }) diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index b37a94145370..009b678aa561 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -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]) diff --git a/dom/bindings/test/test_forOf.html b/dom/bindings/test/test_forOf.html index b1a3032a385b..53969a23e7e6 100644 --- a/dom/bindings/test/test_forOf.html +++ b/dom/bindings/test/test_forOf.html @@ -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++; diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 62509ce0c553..e420382034f4 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -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 \ diff --git a/js/src/builtin/Iterator.js b/js/src/builtin/Iterator.js new file mode 100644 index 000000000000..2e31491340c1 --- /dev/null +++ b/js/src/builtin/Iterator.js @@ -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); +} diff --git a/js/src/builtin/Map.js b/js/src/builtin/Map.js index dfa86d4e806f..4326a5726693 100644 --- a/js/src/builtin/Map.js +++ b/js/src/builtin/Map.js @@ -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); } } diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp index bf4b139a9bea..b91cdab06479 100644 --- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -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(); 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, ToObject(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 = ToObject(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(); 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); diff --git a/js/src/builtin/Set.js b/js/src/builtin/Set.js index a99d35d14369..541eb30a8f6a 100644 --- a/js/src/builtin/Set.js +++ b/js/src/builtin/Set.js @@ -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); } } diff --git a/js/src/builtin/Utilities.js b/js/src/builtin/Utilities.js index bc45dfc917f0..4747110d8faa 100644 --- a/js/src/builtin/Utilities.js +++ b/js/src/builtin/Utilities.js @@ -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 **********/ diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 5158a75672af..242f0205e651 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -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 blockObj(cx, &pn->pn_objbox->object->as()); - 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 + blockObj(cx, letDecl ? &pn1->pn_objbox->object->as() : 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}, diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index d8d4e1e99cac..cf1eb06e24ba 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -3993,7 +3993,7 @@ Parser::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::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 && diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index 2d0175aea1f8..50f44e2d1d6f 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -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 }; diff --git a/js/src/frontend/SourceNotes.h b/js/src/frontend/SourceNotes.h index 230b947c3656..77407c9be485 100644 --- a/js/src/frontend/SourceNotes.h +++ b/js/src/frontend/SourceNotes.h @@ -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, diff --git a/js/src/jit-test/lib/iteration.js b/js/src/jit-test/lib/iteration.js index 7b1271cdf9bf..a5a51ada78ce 100644 --- a/js/src/jit-test/lib/iteration.js +++ b/js/src/jit-test/lib/iteration.js @@ -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); } diff --git a/js/src/jit-test/tests/basic/bug770952.js b/js/src/jit-test/tests/basic/bug770952.js index 64143284373a..5fce35f363e9 100644 --- a/js/src/jit-test/tests/basic/bug770952.js +++ b/js/src/jit-test/tests/basic/bug770952.js @@ -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; diff --git a/js/src/jit-test/tests/basic/spread-array.js b/js/src/jit-test/tests/basic/spread-array.js index 31efba8ca04d..0e6d355a0fb7 100644 --- a/js/src/jit-test/tests/basic/spread-array.js +++ b/js/src/jit-test/tests/basic/spread-array.js @@ -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]); diff --git a/js/src/jit-test/tests/basic/spread-call-eval.js b/js/src/jit-test/tests/basic/spread-call-eval.js index d2b510c8dd08..c6c2dde2ea57 100644 --- a/js/src/jit-test/tests/basic/spread-call-eval.js +++ b/js/src/jit-test/tests/basic/spread-call-eval.js @@ -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"); diff --git a/js/src/jit-test/tests/basic/spread-call-funapply.js b/js/src/jit-test/tests/basic/spread-call-funapply.js index d8848fa82f6e..bdc1cf26baf6 100644 --- a/js/src/jit-test/tests/basic/spread-call-funapply.js +++ b/js/src/jit-test/tests/basic/spread-call-funapply.js @@ -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]); diff --git a/js/src/jit-test/tests/basic/spread-call-length.js b/js/src/jit-test/tests/basic/spread-call-length.js index 463cbdcfd147..fa65cf4dd0da 100644 --- a/js/src/jit-test/tests/basic/spread-call-length.js +++ b/js/src/jit-test/tests/basic/spread-call-length.js @@ -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); diff --git a/js/src/jit-test/tests/basic/spread-call-maxarg.js b/js/src/jit-test/tests/basic/spread-call-maxarg.js index f13dca58eae8..c3225a13eee8 100644 --- a/js/src/jit-test/tests/basic/spread-call-maxarg.js +++ b/js/src/jit-test/tests/basic/spread-call-maxarg.js @@ -1,23 +1,30 @@ -let a = []; -a.length = getMaxArgs() + 1; -let f = function() { -}; +var config = getBuildConfiguration(); -try { - f(...a); -} catch (e) { - assertEq(e.message, "too many function arguments"); -} - -try { - new f(...a); -} catch (e) { - assertEq(e.message, "too many constructor arguments"); -} - -try { - eval(...a); -} catch (e) { - assertEq(e.message, "too many function arguments"); +// FIXME: ASAN debug builds run this too slowly for now. Re-enable +// after bug 919948 lands. +if (!(config.debug && config.asan)) { + let a = []; + a.length = getMaxArgs() + 1; + + let f = function() { + }; + + try { + f(...a); + } catch (e) { + assertEq(e.message, "too many function arguments"); + } + + try { + new f(...a); + } catch (e) { + assertEq(e.message, "too many constructor arguments"); + } + + try { + eval(...a); + } catch (e) { + assertEq(e.message, "too many function arguments"); + } } diff --git a/js/src/jit-test/tests/basic/spread-call-not-iterable.js b/js/src/jit-test/tests/basic/spread-call-not-iterable.js index a25805ce0fcd..d9c278ce25b3 100644 --- a/js/src/jit-test/tests/basic/spread-call-not-iterable.js +++ b/js/src/jit-test/tests/basic/spread-call-not-iterable.js @@ -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); diff --git a/js/src/jit-test/tests/basic/spread-call.js b/js/src/jit-test/tests/basic/spread-call.js index 4c8fbe035b87..4daa46cb6be1 100644 --- a/js/src/jit-test/tests/basic/spread-call.js +++ b/js/src/jit-test/tests/basic/spread-call.js @@ -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]); diff --git a/js/src/jit-test/tests/collections/Map-clear-iterators-1.js b/js/src/jit-test/tests/collections/Map-clear-iterators-1.js index ba02bc69cfc1..63771c3eea1f 100644 --- a/js/src/jit-test/tests/collections/Map-clear-iterators-1.js +++ b/js/src/jit-test/tests/collections/Map-clear-iterators-1.js @@ -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]]); diff --git a/js/src/jit-test/tests/collections/Map-clear-iterators-2.js b/js/src/jit-test/tests/collections/Map-clear-iterators-2.js index 27c491856b6c..06d903ea63b1 100644 --- a/js/src/jit-test/tests/collections/Map-clear-iterators-2.js +++ b/js/src/jit-test/tests/collections/Map-clear-iterators-2.js @@ -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); diff --git a/js/src/jit-test/tests/collections/Map-forEach.js b/js/src/jit-test/tests/collections/Map-forEach.js index 6d51dcd9a42b..70bb1ea06419 100644 --- a/js/src/jit-test/tests/collections/Map-forEach.js +++ b/js/src/jit-test/tests/collections/Map-forEach.js @@ -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."); diff --git a/js/src/jit-test/tests/collections/Map-iterator-add-2.js b/js/src/jit-test/tests/collections/Map-iterator-add-2.js index 5091fdf90e78..52a34b5a50d8 100644 --- a/js/src/jit-test/tests/collections/Map-iterator-add-2.js +++ b/js/src/jit-test/tests/collections/Map-iterator-add-2.js @@ -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 diff --git a/js/src/jit-test/tests/collections/Map-iterator-pairs-1.js b/js/src/jit-test/tests/collections/Map-iterator-pairs-1.js index 37bfcdd942ce..46517f417b02 100644 --- a/js/src/jit-test/tests/collections/Map-iterator-pairs-1.js +++ b/js/src/jit-test/tests/collections/Map-iterator-pairs-1.js @@ -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); diff --git a/js/src/jit-test/tests/collections/Map-iterator-pairs-2.js b/js/src/jit-test/tests/collections/Map-iterator-pairs-2.js index 3c1ab2bbffea..351f6ff2d0fb 100644 --- a/js/src/jit-test/tests/collections/Map-iterator-pairs-2.js +++ b/js/src/jit-test/tests/collections/Map-iterator-pairs-2.js @@ -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); diff --git a/js/src/jit-test/tests/collections/Map-iterator-pairs-3.js b/js/src/jit-test/tests/collections/Map-iterator-pairs-3.js index 944eded65596..0af196a69117 100644 --- a/js/src/jit-test/tests/collections/Map-iterator-pairs-3.js +++ b/js/src/jit-test/tests/collections/Map-iterator-pairs-3.js @@ -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); diff --git a/js/src/jit-test/tests/collections/Map-iterator-proxies-2.js b/js/src/jit-test/tests/collections/Map-iterator-proxies-2.js index 6999c8f43cd3..1d5937f6c47d 100644 --- a/js/src/jit-test/tests/collections/Map-iterator-proxies-2.js +++ b/js/src/jit-test/tests/collections/Map-iterator-proxies-2.js @@ -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); diff --git a/js/src/jit-test/tests/collections/Map-iterator-remove-2.js b/js/src/jit-test/tests/collections/Map-iterator-remove-2.js index a78ed7851eee..d00186fe3afc 100644 --- a/js/src/jit-test/tests/collections/Map-iterator-remove-2.js +++ b/js/src/jit-test/tests/collections/Map-iterator-remove-2.js @@ -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; diff --git a/js/src/jit-test/tests/collections/Map-iterator-remove-3.js b/js/src/jit-test/tests/collections/Map-iterator-remove-3.js index e1cffbf4bb65..ca46755f6be0 100644 --- a/js/src/jit-test/tests/collections/Map-iterator-remove-3.js +++ b/js/src/jit-test/tests/collections/Map-iterator-remove-3.js @@ -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); diff --git a/js/src/jit-test/tests/collections/Map-iterator-remove-4.js b/js/src/jit-test/tests/collections/Map-iterator-remove-4.js index 64869e102908..c065b10633f1 100644 --- a/js/src/jit-test/tests/collections/Map-iterator-remove-4.js +++ b/js/src/jit-test/tests/collections/Map-iterator-remove-4.js @@ -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); } diff --git a/js/src/jit-test/tests/collections/Map-iterator-remove-6.js b/js/src/jit-test/tests/collections/Map-iterator-remove-6.js index e25199482c7c..985bbd9edb4e 100644 --- a/js/src/jit-test/tests/collections/Map-iterator-remove-6.js +++ b/js/src/jit-test/tests/collections/Map-iterator-remove-6.js @@ -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); diff --git a/js/src/jit-test/tests/collections/Map-iterators-3.js b/js/src/jit-test/tests/collections/Map-iterators-3.js index 9f02d4fe35c2..22376bf390dd 100644 --- a/js/src/jit-test/tests/collections/Map-iterators-3.js +++ b/js/src/jit-test/tests/collections/Map-iterators-3.js @@ -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 diff --git a/js/src/jit-test/tests/collections/Map-surfaces-1.js b/js/src/jit-test/tests/collections/Map-surfaces-1.js index fda71bccbbba..254ad0312ca8 100644 --- a/js/src/jit-test/tests/collections/Map-surfaces-1.js +++ b/js/src/jit-test/tests/collections/Map-surfaces-1.js @@ -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); diff --git a/js/src/jit-test/tests/collections/Map-values-2.js b/js/src/jit-test/tests/collections/Map-values-2.js index 6bbbe15a3206..d30e3d0e5ef5 100644 --- a/js/src/jit-test/tests/collections/Map-values-2.js +++ b/js/src/jit-test/tests/collections/Map-values-2.js @@ -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()); diff --git a/js/src/jit-test/tests/collections/Set-clear-iterators-1.js b/js/src/jit-test/tests/collections/Set-clear-iterators-1.js index d8a8056a1a27..ad0eeb3de03d 100644 --- a/js/src/jit-test/tests/collections/Set-clear-iterators-1.js +++ b/js/src/jit-test/tests/collections/Set-clear-iterators-1.js @@ -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"]); diff --git a/js/src/jit-test/tests/collections/Set-clear-iterators-2.js b/js/src/jit-test/tests/collections/Set-clear-iterators-2.js index 640d4672e361..749bc47ff687 100644 --- a/js/src/jit-test/tests/collections/Set-clear-iterators-2.js +++ b/js/src/jit-test/tests/collections/Set-clear-iterators-2.js @@ -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); diff --git a/js/src/jit-test/tests/collections/Set-clear-iterators-3.js b/js/src/jit-test/tests/collections/Set-clear-iterators-3.js index 0866e92c5378..2dda99b140af 100644 --- a/js/src/jit-test/tests/collections/Set-clear-iterators-3.js +++ b/js/src/jit-test/tests/collections/Set-clear-iterators-3.js @@ -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); diff --git a/js/src/jit-test/tests/collections/Set-forEach.js b/js/src/jit-test/tests/collections/Set-forEach.js index fa7b85f3326b..97448203105e 100644 --- a/js/src/jit-test/tests/collections/Set-forEach.js +++ b/js/src/jit-test/tests/collections/Set-forEach.js @@ -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."); diff --git a/js/src/jit-test/tests/collections/Set-iterator-add-2.js b/js/src/jit-test/tests/collections/Set-iterator-add-2.js index 3acb5b0ad456..8f22f35e77b4 100644 --- a/js/src/jit-test/tests/collections/Set-iterator-add-2.js +++ b/js/src/jit-test/tests/collections/Set-iterator-add-2.js @@ -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 diff --git a/js/src/jit-test/tests/collections/Set-iterator-gc-1.js b/js/src/jit-test/tests/collections/Set-iterator-gc-1.js index 0d1d0e551dc0..f50e117648bf 100644 --- a/js/src/jit-test/tests/collections/Set-iterator-gc-1.js +++ b/js/src/jit-test/tests/collections/Set-iterator-gc-1.js @@ -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); diff --git a/js/src/jit-test/tests/collections/Set-iterator-proxies-2.js b/js/src/jit-test/tests/collections/Set-iterator-proxies-2.js index e86af51610c5..9e8ee1bc083c 100644 --- a/js/src/jit-test/tests/collections/Set-iterator-proxies-2.js +++ b/js/src/jit-test/tests/collections/Set-iterator-proxies-2.js @@ -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); diff --git a/js/src/jit-test/tests/collections/Set-iterator-remove-2.js b/js/src/jit-test/tests/collections/Set-iterator-remove-2.js index c88a456dd1b8..9284816143c7 100644 --- a/js/src/jit-test/tests/collections/Set-iterator-remove-2.js +++ b/js/src/jit-test/tests/collections/Set-iterator-remove-2.js @@ -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; diff --git a/js/src/jit-test/tests/collections/Set-iterator-remove-3.js b/js/src/jit-test/tests/collections/Set-iterator-remove-3.js index 3536b5043b72..e3523936c543 100644 --- a/js/src/jit-test/tests/collections/Set-iterator-remove-3.js +++ b/js/src/jit-test/tests/collections/Set-iterator-remove-3.js @@ -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); diff --git a/js/src/jit-test/tests/collections/Set-iterator-remove-4.js b/js/src/jit-test/tests/collections/Set-iterator-remove-4.js index b29396affb17..3078a6365a15 100644 --- a/js/src/jit-test/tests/collections/Set-iterator-remove-4.js +++ b/js/src/jit-test/tests/collections/Set-iterator-remove-4.js @@ -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); } diff --git a/js/src/jit-test/tests/collections/Set-iterator-remove-6.js b/js/src/jit-test/tests/collections/Set-iterator-remove-6.js index d21b19500783..893793954587 100644 --- a/js/src/jit-test/tests/collections/Set-iterator-remove-6.js +++ b/js/src/jit-test/tests/collections/Set-iterator-remove-6.js @@ -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); diff --git a/js/src/jit-test/tests/collections/Set-surfaces-1.js b/js/src/jit-test/tests/collections/Set-surfaces-1.js index 094846eebfdc..bee9b78a8f03 100644 --- a/js/src/jit-test/tests/collections/Set-surfaces-1.js +++ b/js/src/jit-test/tests/collections/Set-surfaces-1.js @@ -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); diff --git a/js/src/jit-test/tests/collections/Set-values-2.js b/js/src/jit-test/tests/collections/Set-values-2.js index c9304ff8f352..c9edc444ddc5 100644 --- a/js/src/jit-test/tests/collections/Set-values-2.js +++ b/js/src/jit-test/tests/collections/Set-values-2.js @@ -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()); diff --git a/js/src/jit-test/tests/collections/iterator-1.js b/js/src/jit-test/tests/collections/iterator-1.js index 9b4d7f146253..5f0b71c086ab 100644 --- a/js/src/jit-test/tests/collections/iterator-1.js +++ b/js/src/jit-test/tests/collections/iterator-1.js @@ -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); diff --git a/js/src/jit-test/tests/collections/iterator-gc.js b/js/src/jit-test/tests/collections/iterator-gc.js index 511eb7cb8100..8365fb322dcd 100644 --- a/js/src/jit-test/tests/collections/iterator-gc.js +++ b/js/src/jit-test/tests/collections/iterator-gc.js @@ -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); } diff --git a/js/src/jit-test/tests/collections/iterator-proto-1.js b/js/src/jit-test/tests/collections/iterator-proto-1.js index 977ecb5a92cd..547e9db435ff 100644 --- a/js/src/jit-test/tests/collections/iterator-proto-1.js +++ b/js/src/jit-test/tests/collections/iterator-proto-1.js @@ -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); diff --git a/js/src/jit-test/tests/collections/iterator-proto-2.js b/js/src/jit-test/tests/collections/iterator-proto-2.js index c6ce26eef485..820298b6e7c3 100644 --- a/js/src/jit-test/tests/collections/iterator-proto-2.js +++ b/js/src/jit-test/tests/collections/iterator-proto-2.js @@ -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); diff --git a/js/src/jit-test/tests/collections/iterator-proto-surfaces.js b/js/src/jit-test/tests/collections/iterator-proto-surfaces.js index 51191d06d1e5..86377d0e8bbf 100644 --- a/js/src/jit-test/tests/collections/iterator-proto-surfaces.js +++ b/js/src/jit-test/tests/collections/iterator-proto-surfaces.js @@ -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); diff --git a/js/src/jit-test/tests/for-of/arguments-1.js b/js/src/jit-test/tests/for-of/arguments-1.js index d9c6b526a37d..7fbf19a45959 100644 --- a/js/src/jit-test/tests/for-of/arguments-1.js +++ b/js/src/jit-test/tests/for-of/arguments-1.js @@ -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() { diff --git a/js/src/jit-test/tests/for-of/arguments-2.js b/js/src/jit-test/tests/for-of/arguments-2.js index 534b396d8f34..f2bdedf375b6 100644 --- a/js/src/jit-test/tests/for-of/arguments-2.js +++ b/js/src/jit-test/tests/for-of/arguments-2.js @@ -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'); diff --git a/js/src/jit-test/tests/for-of/arguments-3.js b/js/src/jit-test/tests/for-of/arguments-3.js index 6bb221c23c7e..d91da2d2afdf 100644 --- a/js/src/jit-test/tests/for-of/arguments-3.js +++ b/js/src/jit-test/tests/for-of/arguments-3.js @@ -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() { diff --git a/js/src/jit-test/tests/for-of/arguments-4.js b/js/src/jit-test/tests/for-of/arguments-4.js index 8e8b7d31e3e7..776f97418639 100644 --- a/js/src/jit-test/tests/for-of/arguments-4.js +++ b/js/src/jit-test/tests/for-of/arguments-4.js @@ -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) { diff --git a/js/src/jit-test/tests/for-of/arguments-5.js b/js/src/jit-test/tests/for-of/arguments-5.js index 49a13ac40518..52d55d376b9f 100644 --- a/js/src/jit-test/tests/for-of/arguments-5.js +++ b/js/src/jit-test/tests/for-of/arguments-5.js @@ -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) { diff --git a/js/src/jit-test/tests/for-of/arguments-6.js b/js/src/jit-test/tests/for-of/arguments-6.js index 3d0bf1d8f1d7..a84f9b6b25e9 100644 --- a/js/src/jit-test/tests/for-of/arguments-6.js +++ b/js/src/jit-test/tests/for-of/arguments-6.js @@ -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() { diff --git a/js/src/jit-test/tests/for-of/arguments-7.js b/js/src/jit-test/tests/for-of/arguments-7.js index b6871346a4ef..42d73fb6a8ba 100644 --- a/js/src/jit-test/tests/for-of/arguments-7.js +++ b/js/src/jit-test/tests/for-of/arguments-7.js @@ -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() { diff --git a/js/src/jit-test/tests/for-of/array-holes-4.js b/js/src/jit-test/tests/for-of/array-holes-4.js index 8c6696ab9a1d..e058e21143b4 100644 --- a/js/src/jit-test/tests/for-of/array-holes-4.js +++ b/js/src/jit-test/tests/for-of/array-holes-4.js @@ -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'); diff --git a/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js b/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js index 00be20a61230..f66154208bd1 100644 --- a/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js +++ b/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js @@ -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)); diff --git a/js/src/jit-test/tests/for-of/completion.js b/js/src/jit-test/tests/for-of/completion.js new file mode 100644 index 000000000000..91dc96e5717d --- /dev/null +++ b/js/src/jit-test/tests/for-of/completion.js @@ -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); diff --git a/js/src/jit-test/tests/for-of/generators-5.js b/js/src/jit-test/tests/for-of/generators-5.js index b74d924fd69d..c76e53050c33 100644 --- a/js/src/jit-test/tests/for-of/generators-5.js +++ b/js/src/jit-test/tests/for-of/generators-5.js @@ -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'); diff --git a/js/src/jit-test/tests/for-of/generators-6.js b/js/src/jit-test/tests/for-of/generators-6.js deleted file mode 100644 index 08badf18f942..000000000000 --- a/js/src/jit-test/tests/for-of/generators-6.js +++ /dev/null @@ -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); diff --git a/js/src/jit-test/tests/for-of/manual-advance.js b/js/src/jit-test/tests/for-of/manual-advance.js new file mode 100644 index 000000000000..a240e3b71271 --- /dev/null +++ b/js/src/jit-test/tests/for-of/manual-advance.js @@ -0,0 +1,15 @@ +// Manually advancing the iterator. + +load(libdir + 'iteration.js'); + +function* g(n) { for (var i=0; i= 0; ) yield this[i]; }; diff --git a/js/src/jit-test/tests/for-of/semantics-04.js b/js/src/jit-test/tests/for-of/semantics-04.js index a86b38bd8a54..5d1d2f902878 100644 --- a/js/src/jit-test/tests/for-of/semantics-04.js +++ b/js/src/jit-test/tests/for-of/semantics-04.js @@ -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); diff --git a/js/src/jit-test/tests/for-of/semantics-05.js b/js/src/jit-test/tests/for-of/semantics-05.js index 9adccbe63056..4b7c22a5e3e1 100644 --- a/js/src/jit-test/tests/for-of/semantics-05.js +++ b/js/src/jit-test/tests/for-of/semantics-05.js @@ -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); diff --git a/js/src/jit-test/tests/for-of/semantics-07.js b/js/src/jit-test/tests/for-of/semantics-07.js index 03ea49efa406..107c40f59896 100644 --- a/js/src/jit-test/tests/for-of/semantics-07.js +++ b/js/src/jit-test/tests/for-of/semantics-07.js @@ -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']) { diff --git a/js/src/jit-test/tests/for-of/semantics-08.js b/js/src/jit-test/tests/for-of/semantics-08.js index e08ae456ac78..9c0cc08ea994 100644 --- a/js/src/jit-test/tests/for-of/semantics-08.js +++ b/js/src/jit-test/tests/for-of/semantics-08.js @@ -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'; diff --git a/js/src/jit-test/tests/for-of/semantics-11.js b/js/src/jit-test/tests/for-of/semantics-11.js index 6e6c4988d39c..3ea6cdac7d49 100644 --- a/js/src/jit-test/tests/for-of/semantics-11.js +++ b/js/src/jit-test/tests/for-of/semantics-11.js @@ -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; } diff --git a/js/src/jit-test/tests/for-of/value-done-access.js b/js/src/jit-test/tests/for-of/value-done-access.js new file mode 100644 index 000000000000..225bc2863ad9 --- /dev/null +++ b/js/src/jit-test/tests/for-of/value-done-access.js @@ -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"); diff --git a/js/src/jit-test/tests/ion/bug800179.js b/js/src/jit-test/tests/ion/bug800179.js index 44088e1808e5..91b9d87db210 100644 --- a/js/src/jit-test/tests/ion/bug800179.js +++ b/js/src/jit-test/tests/ion/bug800179.js @@ -1,4 +1,5 @@ -// |jit-test| error: is not iterable +// |jit-test| error: TypeError + try { x = [] y = function() {} diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 1f041ddffe00..5769be9b87af 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -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(jit::LeaveBlock); diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index 5371c021bca7..d07f8885d470 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -148,6 +148,7 @@ namespace jit { _(JSOP_ENTERBLOCK) \ _(JSOP_ENTERLET0) \ _(JSOP_ENTERLET1) \ + _(JSOP_ENTERLET2) \ _(JSOP_LEAVEBLOCK) \ _(JSOP_LEAVEBLOCKEXPR) \ _(JSOP_LEAVEFORLETIN) \ diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 0884b3bb4610..56abe2239d81 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -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); diff --git a/js/src/js.msg b/js/src/js.msg index ecba7a34c6f1..a39de1d0f516 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -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}") diff --git a/js/src/jsanalyze.cpp b/js/src/jsanalyze.cpp index f55bf6a2a1be..d3b9b2c1626d 100644 --- a/js/src/jsanalyze.cpp +++ b/js/src/jsanalyze.cpp @@ -252,6 +252,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) case JSOP_EVAL: case JSOP_SPREADEVAL: + case JSOP_ENTERLET2: case JSOP_ENTERWITH: canTrackVars = false; break; diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index f85699b90c71..9ae627fd7858 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4221,7 +4221,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; diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index f71a9d1a4ec2..620aaafb58dc 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -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 }; diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 4e072b5f6f50..f57ef22b9eb4 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -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 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 }; @@ -1302,6 +1321,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 @@ -1790,14 +1841,14 @@ NativeMethod(JSContext *cx, unsigned argc, Value *vp) #define JS_METHOD(name, T, impl, len, attrs) JS_FN(name, (NativeMethod), 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), diff --git a/js/src/jsiter.h b/js/src/jsiter.h index b1e354479a1b..ab3f64fbe27e 100644 --- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -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, ¤tValue); - return ok && !currentValue.get().isMagic(JS_NO_ITER_VALUE); - } - - MutableHandleValue value() { - JS_ASSERT(ok); - JS_ASSERT(!closed); - return ¤tValue; - } - - 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 */ /* diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 3ffb91820d1d..6c4353b1278c 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -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().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().stackDepth() >= blockChain->as().stackDepth()); diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 4d9351477bcd..13245d6db7b6 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -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) diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 8a5db3c0478f..94459a665697 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -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 }; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index e0e452a48c50..25a7b32ee7f9 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -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; diff --git a/js/src/tests/ecma_6/Generators/runtime.js b/js/src/tests/ecma_6/Generators/runtime.js index 93c6f746b2d4..4c4ece9c3b84 100644 --- a/js/src/tests/ecma_6/Generators/runtime.js +++ b/js/src/tests/ecma_6/Generators/runtime.js @@ -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); diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 916e4c63764a..959976748dc0 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -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") \ diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index f1057268f96b..77714e47d59b 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1485,7 +1485,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) @@ -3050,21 +3049,26 @@ BEGIN_CASE(JSOP_SPREAD) RootedObject &arr = rootObject0; arr = ®s.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--; } @@ -3181,6 +3185,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(); diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 9eba1b07ca64..9d60620e59a3 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -455,6 +455,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 @@ -533,6 +547,8 @@ const JSFunctionSpec intrinsic_functions[] = { JS_FN("UnsafeGetReservedSlot", intrinsic_UnsafeGetReservedSlot, 2,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), diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index fdccccd831f0..94aa222b49ea 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -3426,6 +3426,7 @@ const JSFunctionSpec ArrayBufferObject::jsfuncs[] = { #ifndef RELEASE_BUILD # define IMPL_TYPED_ARRAY_STATICS(_typedArray) \ const JSFunctionSpec _typedArray##Object::jsfuncs[] = { \ + JS_SELF_HOSTED_FN("@@iterator", "ArrayIterator", 0, 0), \ JS_FN("iterator", JS_ArrayIterator, 0, 0), \ JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \ JS_FN("set", _typedArray##Object::fun_set, 2, JSFUN_GENERIC_NATIVE), \ @@ -3435,6 +3436,7 @@ const JSFunctionSpec _typedArray##Object::jsfuncs[] = { #else # define IMPL_TYPED_ARRAY_STATICS(_typedArray) \ const JSFunctionSpec _typedArray##Object::jsfuncs[] = { \ + JS_SELF_HOSTED_FN("@@iterator", "ArrayIterator", 0, 0), \ JS_FN("iterator", JS_ArrayIterator, 0, 0), \ JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \ JS_FN("set", _typedArray##Object::fun_set, 2, JSFUN_GENERIC_NATIVE), \ diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 3024a74f761f..a587d5c0ed22 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -22,7 +22,7 @@ namespace js { * and saved versions. If deserialization fails, the data should be * invalidated if possible. */ -static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 152); +static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 153); class XDRBuffer { public: