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: