mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-04 02:57:38 +00:00
Bug 907077: Change for-of to conform to latest ES6 specification. r=jwalden
--HG-- extra : rebase_source : 93c5b9d6a28a42468d9ca9ac7da3b4e1b6d92820
This commit is contained in:
parent
b26ec8cdeb
commit
28a193c95f
@ -52,7 +52,7 @@ is(names[8], "z", "Entry 9")
|
||||
is(names[9], "something", "Entry 10")
|
||||
is(names[10], "item", "Entry 11")
|
||||
is(names[11], "namedItem", "Entry 12")
|
||||
is(names[12], "iterator", "Entry 13")
|
||||
is(names[12], "@@iterator", "Entry 13")
|
||||
is(names[13], "length", "Entry 14")
|
||||
</script>
|
||||
</pre>
|
||||
|
@ -40,7 +40,7 @@ is(names[7], "w", "Entry 8")
|
||||
is(names[8], "something", "Entry 9")
|
||||
is(names[9], "item", "Entry 10")
|
||||
is(names[10], "namedItem", "Entry 11")
|
||||
is(names[11], "iterator", "Entry 12")
|
||||
is(names[11], "@@iterator", "Entry 12")
|
||||
is(names[12], "length", "Entry 13")
|
||||
</script>
|
||||
</pre>
|
||||
|
@ -52,7 +52,7 @@ is(names[9], "w", "Entry 10")
|
||||
is(names[10], "something", "Entry 11")
|
||||
is(names[11], "item", "Entry 12")
|
||||
is(names[12], "namedItem", "Entry 13")
|
||||
is(names[13], "iterator", "Entry 14")
|
||||
is(names[13], "@@iterator", "Entry 14")
|
||||
is(names[14], "length", "Entry 15")
|
||||
</script>
|
||||
</pre>
|
||||
|
@ -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) })
|
||||
|
@ -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])
|
||||
|
@ -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++;
|
||||
|
@ -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 \
|
||||
|
97
js/src/builtin/Iterator.js
Normal file
97
js/src/builtin/Iterator.js
Normal file
@ -0,0 +1,97 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function IteratorIdentity() {
|
||||
return this;
|
||||
}
|
||||
|
||||
var LegacyIteratorWrapperMap = new std_WeakMap();
|
||||
|
||||
function IteratorResult(value, done) {
|
||||
var result = std_Object_create(null);
|
||||
result.value = value;
|
||||
result.done = done;
|
||||
return result;
|
||||
}
|
||||
|
||||
function LegacyIteratorNext(arg) {
|
||||
var iter = callFunction(std_WeakMap_get, LegacyIteratorWrapperMap, this);
|
||||
try {
|
||||
return IteratorResult(iter.next(arg), false);
|
||||
} catch (e) {
|
||||
if (e instanceof std_StopIteration)
|
||||
return IteratorResult(undefined, true);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function LegacyIteratorThrow(exn) {
|
||||
var iter = callFunction(std_WeakMap_get, LegacyIteratorWrapperMap, this);
|
||||
try {
|
||||
return IteratorResult(iter.throw(exn), false);
|
||||
} catch (e) {
|
||||
if (e instanceof std_StopIteration)
|
||||
return IteratorResult(undefined, true);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function LegacyIterator(iter) {
|
||||
callFunction(std_WeakMap_set, LegacyIteratorWrapperMap, this, iter);
|
||||
}
|
||||
|
||||
function LegacyGeneratorIterator(iter) {
|
||||
callFunction(std_WeakMap_set, LegacyIteratorWrapperMap, this, iter);
|
||||
}
|
||||
|
||||
var LegacyIteratorsInitialized = std_Object_create(null);
|
||||
|
||||
function InitLegacyIterators() {
|
||||
var props = std_Object_create(null);
|
||||
|
||||
props.next = std_Object_create(null);
|
||||
props.next.value = LegacyIteratorNext;
|
||||
props.next.enumerable = false;
|
||||
props.next.configurable = true;
|
||||
props.next.writable = true;
|
||||
|
||||
props[std_iterator] = std_Object_create(null);
|
||||
props[std_iterator].value = IteratorIdentity;
|
||||
props[std_iterator].enumerable = false;
|
||||
props[std_iterator].configurable = true;
|
||||
props[std_iterator].writable = true;
|
||||
|
||||
var LegacyIteratorProto = std_Object_create(GetIteratorPrototype(), props);
|
||||
MakeConstructible(LegacyIterator, LegacyIteratorProto);
|
||||
|
||||
props.throw = std_Object_create(null);
|
||||
props.throw.value = LegacyIteratorThrow;
|
||||
props.throw.enumerable = false;
|
||||
props.throw.configurable = true;
|
||||
props.throw.writable = true;
|
||||
|
||||
var LegacyGeneratorIteratorProto = std_Object_create(GetIteratorPrototype(), props);
|
||||
MakeConstructible(LegacyGeneratorIterator, LegacyGeneratorIteratorProto);
|
||||
|
||||
LegacyIteratorsInitialized.initialized = true;
|
||||
}
|
||||
|
||||
function NewLegacyIterator(iter, wrapper) {
|
||||
if (!LegacyIteratorsInitialized.initialized)
|
||||
InitLegacyIterators();
|
||||
|
||||
return new wrapper(iter);
|
||||
}
|
||||
|
||||
function LegacyIteratorShim() {
|
||||
return NewLegacyIterator(ToObject(this), LegacyIterator);
|
||||
}
|
||||
|
||||
function LegacyGeneratorIteratorShim() {
|
||||
return NewLegacyIterator(ToObject(this), LegacyGeneratorIterator);
|
||||
}
|
||||
|
||||
function ArrayIterator() {
|
||||
return NewLegacyIterator(callFunction(std_Array_iterator, this), LegacyIterator);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ using mozilla::DoubleIsInt32;
|
||||
using mozilla::IsNaN;
|
||||
using mozilla::OldMove;
|
||||
using mozilla::MoveRef;
|
||||
using mozilla::ArrayLength;
|
||||
using JS::DoubleNaNValue;
|
||||
|
||||
|
||||
@ -889,6 +890,7 @@ const Class MapIteratorObject::class_ = {
|
||||
};
|
||||
|
||||
const JSFunctionSpec MapIteratorObject::methods[] = {
|
||||
JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0),
|
||||
JS_FN("next", next, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
@ -965,35 +967,44 @@ MapIteratorObject::next_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
MapIteratorObject &thisobj = args.thisv().toObject().as<MapIteratorObject>();
|
||||
ValueMap::Range *range = thisobj.range();
|
||||
if (!range)
|
||||
return js_ThrowStopIteration(cx);
|
||||
if (range->empty()) {
|
||||
RootedValue value(cx);
|
||||
bool done;
|
||||
|
||||
if (!range || range->empty()) {
|
||||
js_delete(range);
|
||||
thisobj.setReservedSlot(RangeSlot, PrivateValue(nullptr));
|
||||
return js_ThrowStopIteration(cx);
|
||||
value.setUndefined();
|
||||
done = true;
|
||||
} else {
|
||||
switch (thisobj.kind()) {
|
||||
case MapObject::Keys:
|
||||
value = range->front().key.get();
|
||||
break;
|
||||
|
||||
case MapObject::Values:
|
||||
value = range->front().value;
|
||||
break;
|
||||
|
||||
case MapObject::Entries: {
|
||||
Value pair[2] = { range->front().key.get(), range->front().value };
|
||||
AutoValueArray root(cx, pair, ArrayLength(pair));
|
||||
|
||||
JSObject *pairobj = NewDenseCopiedArray(cx, ArrayLength(pair), pair);
|
||||
if (!pairobj)
|
||||
return false;
|
||||
value.setObject(*pairobj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
range->popFront();
|
||||
done = false;
|
||||
}
|
||||
|
||||
switch (thisobj.kind()) {
|
||||
case MapObject::Keys:
|
||||
args.rval().set(range->front().key.get());
|
||||
break;
|
||||
RootedObject result(cx, CreateItrResultObject(cx, value, done));
|
||||
if (!result)
|
||||
return false;
|
||||
args.rval().setObject(*result);
|
||||
|
||||
case MapObject::Values:
|
||||
args.rval().set(range->front().value);
|
||||
break;
|
||||
|
||||
case MapObject::Entries: {
|
||||
Value pair[2] = { range->front().key.get(), range->front().value };
|
||||
AutoValueArray root(cx, pair, 2);
|
||||
|
||||
JSObject *pairobj = NewDenseCopiedArray(cx, 2, pair);
|
||||
if (!pairobj)
|
||||
return false;
|
||||
args.rval().setObject(*pairobj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
range->popFront();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1077,7 +1088,7 @@ MapObject::initClass(JSContext *cx, JSObject *obj)
|
||||
|
||||
// Define its alias.
|
||||
RootedValue funval(cx, ObjectValue(*fun));
|
||||
if (!JS_DefineProperty(cx, proto, "iterator", funval, nullptr, nullptr, 0))
|
||||
if (!JS_DefineProperty(cx, proto, js_std_iterator_str, funval, nullptr, nullptr, 0))
|
||||
return nullptr;
|
||||
}
|
||||
return proto;
|
||||
@ -1173,14 +1184,25 @@ MapObject::construct(JSContext *cx, unsigned argc, Value *vp)
|
||||
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.hasDefined(0)) {
|
||||
ForOfIterator iter(cx, args[0]);
|
||||
while (iter.next()) {
|
||||
RootedObject pairobj(cx, js_ValueToNonNullObject(cx, iter.value()));
|
||||
if (!pairobj)
|
||||
ForOfIterator iter(cx);
|
||||
if (!iter.init(args[0]))
|
||||
return false;
|
||||
RootedValue pairVal(cx);
|
||||
RootedObject pairObj(cx);
|
||||
while (true) {
|
||||
bool done;
|
||||
if (!iter.next(&pairVal, &done))
|
||||
return false;
|
||||
if (done)
|
||||
break;
|
||||
// FIXME: We're supposed to throw if pairVal isn't an object. Bug
|
||||
// 918341.
|
||||
pairObj = js_ValueToNonNullObject(cx, pairVal);
|
||||
if (!pairObj)
|
||||
return false;
|
||||
|
||||
RootedValue key(cx);
|
||||
if (!JSObject::getElement(cx, pairobj, pairobj, 0, &key))
|
||||
if (!JSObject::getElement(cx, pairObj, pairObj, 0, &key))
|
||||
return false;
|
||||
|
||||
AutoHashableValueRooter hkey(cx);
|
||||
@ -1188,7 +1210,7 @@ MapObject::construct(JSContext *cx, unsigned argc, Value *vp)
|
||||
return false;
|
||||
|
||||
RootedValue val(cx);
|
||||
if (!JSObject::getElement(cx, pairobj, pairobj, 1, &val))
|
||||
if (!JSObject::getElement(cx, pairObj, pairObj, 1, &val))
|
||||
return false;
|
||||
|
||||
RelocatableValue rval(val);
|
||||
@ -1198,8 +1220,6 @@ MapObject::construct(JSContext *cx, unsigned argc, Value *vp)
|
||||
}
|
||||
WriteBarrierPost(cx->runtime(), map, hkey);
|
||||
}
|
||||
if (!iter.close())
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*obj);
|
||||
@ -1456,6 +1476,7 @@ const Class SetIteratorObject::class_ = {
|
||||
};
|
||||
|
||||
const JSFunctionSpec SetIteratorObject::methods[] = {
|
||||
JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0),
|
||||
JS_FN("next", next, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
@ -1531,32 +1552,40 @@ SetIteratorObject::next_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
SetIteratorObject &thisobj = args.thisv().toObject().as<SetIteratorObject>();
|
||||
ValueSet::Range *range = thisobj.range();
|
||||
if (!range)
|
||||
return js_ThrowStopIteration(cx);
|
||||
if (range->empty()) {
|
||||
RootedValue value(cx);
|
||||
bool done;
|
||||
|
||||
if (!range || range->empty()) {
|
||||
js_delete(range);
|
||||
thisobj.setReservedSlot(RangeSlot, PrivateValue(nullptr));
|
||||
return js_ThrowStopIteration(cx);
|
||||
value.setUndefined();
|
||||
done = true;
|
||||
} else {
|
||||
switch (thisobj.kind()) {
|
||||
case SetObject::Values:
|
||||
value = range->front().get();
|
||||
break;
|
||||
|
||||
case SetObject::Entries: {
|
||||
Value pair[2] = { range->front().get(), range->front().get() };
|
||||
AutoValueArray root(cx, pair, 2);
|
||||
|
||||
JSObject *pairObj = NewDenseCopiedArray(cx, 2, pair);
|
||||
if (!pairObj)
|
||||
return false;
|
||||
value.setObject(*pairObj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
range->popFront();
|
||||
done = false;
|
||||
}
|
||||
|
||||
switch (thisobj.kind()) {
|
||||
case SetObject::Values:
|
||||
args.rval().set(range->front().get());
|
||||
break;
|
||||
RootedObject result(cx, CreateItrResultObject(cx, value, done));
|
||||
if (!result)
|
||||
return false;
|
||||
args.rval().setObject(*result);
|
||||
|
||||
case SetObject::Entries: {
|
||||
Value pair[2] = { range->front().get(), range->front().get() };
|
||||
AutoValueArray root(cx, pair, 2);
|
||||
|
||||
JSObject *pairobj = NewDenseCopiedArray(cx, 2, pair);
|
||||
if (!pairobj)
|
||||
return false;
|
||||
args.rval().setObject(*pairobj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
range->popFront();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1620,7 +1649,7 @@ SetObject::initClass(JSContext *cx, JSObject *obj)
|
||||
RootedValue funval(cx, ObjectValue(*fun));
|
||||
if (!JS_DefineProperty(cx, proto, "keys", funval, nullptr, nullptr, 0))
|
||||
return nullptr;
|
||||
if (!JS_DefineProperty(cx, proto, "iterator", funval, nullptr, nullptr, 0))
|
||||
if (!JS_DefineProperty(cx, proto, js_std_iterator_str, funval, nullptr, nullptr, 0))
|
||||
return nullptr;
|
||||
}
|
||||
return proto;
|
||||
@ -1662,10 +1691,18 @@ SetObject::construct(JSContext *cx, unsigned argc, Value *vp)
|
||||
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.hasDefined(0)) {
|
||||
ForOfIterator iter(cx, args[0]);
|
||||
while (iter.next()) {
|
||||
AutoHashableValueRooter key(cx);
|
||||
if (!key.setValue(cx, iter.value()))
|
||||
RootedValue keyVal(cx);
|
||||
ForOfIterator iter(cx);
|
||||
if (!iter.init(args[0]))
|
||||
return false;
|
||||
AutoHashableValueRooter key(cx);
|
||||
while (true) {
|
||||
bool done;
|
||||
if (!iter.next(&keyVal, &done))
|
||||
return false;
|
||||
if (done)
|
||||
break;
|
||||
if (!key.setValue(cx, keyVal))
|
||||
return false;
|
||||
if (!set->put(key)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
@ -1673,8 +1710,6 @@ SetObject::construct(JSContext *cx, unsigned argc, Value *vp)
|
||||
}
|
||||
WriteBarrierPost(cx->runtime(), set, key);
|
||||
}
|
||||
if (!iter.close())
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*obj);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 **********/
|
||||
|
||||
|
@ -177,6 +177,8 @@ UpdateDepth(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t target)
|
||||
nuses = ndefs = CurrentBlock(bce->topStmt).slotCount();
|
||||
} else if (op == JSOP_ENTERLET1) {
|
||||
nuses = ndefs = CurrentBlock(bce->topStmt).slotCount() + 1;
|
||||
} else if (op == JSOP_ENTERLET2) {
|
||||
nuses = ndefs = CurrentBlock(bce->topStmt).slotCount() + 2;
|
||||
} else {
|
||||
nuses = StackUses(nullptr, pc);
|
||||
ndefs = StackDefs(nullptr, pc);
|
||||
@ -298,6 +300,7 @@ static const char * const statementName[] = {
|
||||
"do loop", /* DO_LOOP */
|
||||
"for loop", /* FOR_LOOP */
|
||||
"for/in loop", /* FOR_IN_LOOP */
|
||||
"for/of loop", /* FOR_OF_LOOP */
|
||||
"while loop", /* WHILE_LOOP */
|
||||
};
|
||||
|
||||
@ -555,6 +558,10 @@ EmitNonLocalJumpFixup(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *t
|
||||
return false;
|
||||
break;
|
||||
|
||||
case STMT_FOR_OF_LOOP:
|
||||
npops += 2;
|
||||
break;
|
||||
|
||||
case STMT_FOR_IN_LOOP:
|
||||
FLUSH_POPS();
|
||||
if (!PopIterator(cx, bce))
|
||||
@ -577,11 +584,11 @@ EmitNonLocalJumpFixup(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *t
|
||||
unsigned blockObjCount = stmt->blockObj->slotCount();
|
||||
if (stmt->isForLetBlock) {
|
||||
/*
|
||||
* For a for-let-in statement, pushing/popping the block is
|
||||
* For a for-let-in/of statement, pushing/popping the block is
|
||||
* interleaved with JSOP_(END)ITER. Just handle both together
|
||||
* here and skip over the enclosing STMT_FOR_IN_LOOP.
|
||||
*/
|
||||
JS_ASSERT(stmt->down->type == STMT_FOR_IN_LOOP);
|
||||
unsigned popCount = blockObjCount;
|
||||
stmt = stmt->down;
|
||||
if (stmt == toStmt)
|
||||
break;
|
||||
@ -589,11 +596,16 @@ EmitNonLocalJumpFixup(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *t
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_LEAVEFORLETIN) < 0)
|
||||
return false;
|
||||
if (!PopIterator(cx, bce))
|
||||
return false;
|
||||
if (stmt->type == STMT_FOR_OF_LOOP) {
|
||||
popCount += 2;
|
||||
} else {
|
||||
JS_ASSERT(stmt->type == STMT_FOR_IN_LOOP);
|
||||
if (!PopIterator(cx, bce))
|
||||
return false;
|
||||
}
|
||||
if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
|
||||
return false;
|
||||
EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount);
|
||||
EMIT_UINT16_IMM_OP(JSOP_POPN, popCount);
|
||||
} else {
|
||||
/* There is a Block object with locals on the stack to pop. */
|
||||
if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
|
||||
@ -1052,8 +1064,12 @@ EmitEnterBlock(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp o
|
||||
|
||||
Rooted<StaticBlockObject*> blockObj(cx, &pn->pn_objbox->object->as<StaticBlockObject>());
|
||||
|
||||
int depth = bce->stackDepth -
|
||||
(blockObj->slotCount() + ((op == JSOP_ENTERLET1) ? 1 : 0));
|
||||
int extraSlots = (op == JSOP_ENTERLET1)
|
||||
? 1
|
||||
: (op == JSOP_ENTERLET2)
|
||||
? 2
|
||||
: 0;
|
||||
int depth = bce->stackDepth - (blockObj->slotCount() + extraSlots);
|
||||
JS_ASSERT(depth >= 0);
|
||||
|
||||
blockObj->setStackDepth(depth);
|
||||
@ -3518,10 +3534,11 @@ EmitAssignment(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp
|
||||
return false;
|
||||
} else {
|
||||
/*
|
||||
* The value to assign is the next enumeration value in a for-in loop.
|
||||
* That value is produced by a JSOP_ITERNEXT op, previously emitted.
|
||||
* If offset == 1, that slot is already at the top of the
|
||||
* stack. Otherwise, rearrange the stack to put that value on top.
|
||||
* The value to assign is the next enumeration value in a for-in or
|
||||
* for-of loop. That value has already been emitted: by JSOP_ITERNEXT
|
||||
* in the for-in case, or via a GETPROP "value" on the result object in
|
||||
* the for-of case. If offset == 1, that slot is already at the top of
|
||||
* the stack. Otherwise, rearrange the stack to put that value on top.
|
||||
*/
|
||||
if (offset != 1 && Emit2(cx, bce, JSOP_PICK, offset - 1) < 0)
|
||||
return false;
|
||||
@ -4234,6 +4251,170 @@ EmitWith(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
return PopStatementBCE(cx, bce);
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitForOf(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
||||
{
|
||||
StmtInfoBCE stmtInfo(cx);
|
||||
PushStatementBCE(bce, &stmtInfo, STMT_FOR_OF_LOOP, top);
|
||||
|
||||
ParseNode *forHead = pn->pn_left;
|
||||
ParseNode *forBody = pn->pn_right;
|
||||
|
||||
ParseNode *pn1 = forHead->pn_kid1;
|
||||
bool letDecl = pn1 && pn1->isKind(PNK_LEXICALSCOPE);
|
||||
JS_ASSERT_IF(letDecl, pn1->isLet());
|
||||
|
||||
Rooted<StaticBlockObject*>
|
||||
blockObj(cx, letDecl ? &pn1->pn_objbox->object->as<StaticBlockObject>() : nullptr);
|
||||
uint32_t blockObjCount = blockObj ? blockObj->slotCount() : 0;
|
||||
|
||||
// For-of loops run with two values on the stack: the iterator and the
|
||||
// current result object. If the loop also has a lexical block, those
|
||||
// lexicals are deeper on the stack than the iterator.
|
||||
for (uint32_t i = 0; i < blockObjCount; ++i) {
|
||||
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the left part is 'var x', emit code to define x if necessary using a
|
||||
// prolog opcode, but do not emit a pop.
|
||||
if (pn1) {
|
||||
ParseNode *decl = letDecl ? pn1->pn_expr : pn1;
|
||||
JS_ASSERT(decl->isKind(PNK_VAR) || decl->isKind(PNK_LET));
|
||||
bce->emittingForInit = true;
|
||||
if (!EmitVariables(cx, bce, decl, DefineVars))
|
||||
return false;
|
||||
bce->emittingForInit = false;
|
||||
}
|
||||
|
||||
// Compile the object expression to the right of 'of'.
|
||||
if (!EmitTree(cx, bce, forHead->pn_kid3))
|
||||
return false;
|
||||
|
||||
// Convert iterable to iterator.
|
||||
if (Emit1(cx, bce, JSOP_DUP) < 0) // OBJ OBJ
|
||||
return false;
|
||||
if (!EmitAtomOp(cx, cx->names().std_iterator, JSOP_CALLPROP, bce)) // OBJ @@ITERATOR
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_SWAP) < 0) // @@ITERATOR OBJ
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_NOTEARG) < 0)
|
||||
return false;
|
||||
if (EmitCall(cx, bce, JSOP_CALL, 0) < 0) // ITER
|
||||
return false;
|
||||
CheckTypeSet(cx, bce, JSOP_CALL);
|
||||
|
||||
// Push a dummy result so that we properly enter iteration midstream.
|
||||
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) // ITER RESULT
|
||||
return false;
|
||||
|
||||
// Enter the block before the loop body, after evaluating the obj.
|
||||
StmtInfoBCE letStmt(cx);
|
||||
if (letDecl) {
|
||||
PushBlockScopeBCE(bce, &letStmt, *blockObj, bce->offset());
|
||||
letStmt.isForLetBlock = true;
|
||||
if (!EmitEnterBlock(cx, bce, pn1, JSOP_ENTERLET2))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Jump down to the loop condition to minimize overhead assuming at least
|
||||
// one iteration, as the other loop forms do. Annotate so IonMonkey can
|
||||
// find the loop-closing jump.
|
||||
int noteIndex = NewSrcNote(cx, bce, SRC_FOR_OF);
|
||||
if (noteIndex < 0)
|
||||
return false;
|
||||
ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
|
||||
if (jmp < 0)
|
||||
return false;
|
||||
|
||||
top = bce->offset();
|
||||
SET_STATEMENT_TOP(&stmtInfo, top);
|
||||
if (EmitLoopHead(cx, bce, nullptr) < 0)
|
||||
return false;
|
||||
|
||||
#ifdef DEBUG
|
||||
int loopDepth = bce->stackDepth;
|
||||
#endif
|
||||
|
||||
// Emit code to assign result.value to the iteration variable.
|
||||
if (Emit1(cx, bce, JSOP_DUP) < 0) // ITER RESULT RESULT
|
||||
return false;
|
||||
if (!EmitAtomOp(cx, cx->names().value, JSOP_GETPROP, bce)) // ITER RESULT VALUE
|
||||
return false;
|
||||
if (!EmitAssignment(cx, bce, forHead->pn_kid2, JSOP_NOP, nullptr)) // ITER RESULT VALUE
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_POP) < 0) // ITER RESULT
|
||||
return false;
|
||||
|
||||
// The stack should be balanced around the assignment opcode sequence.
|
||||
JS_ASSERT(bce->stackDepth == loopDepth);
|
||||
|
||||
// Emit code for the loop body.
|
||||
if (!EmitTree(cx, bce, forBody))
|
||||
return false;
|
||||
|
||||
// Set loop and enclosing "update" offsets, for continue.
|
||||
StmtInfoBCE *stmt = &stmtInfo;
|
||||
do {
|
||||
stmt->update = bce->offset();
|
||||
} while ((stmt = stmt->down) != nullptr && stmt->type == STMT_LABEL);
|
||||
|
||||
// COME FROM the beginning of the loop to here.
|
||||
SetJumpOffsetAt(bce, jmp);
|
||||
if (!EmitLoopEntry(cx, bce, nullptr))
|
||||
return false;
|
||||
|
||||
if (Emit1(cx, bce, JSOP_POP) < 0) // ITER
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_DUP) < 0) // ITER ITER
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_DUP) < 0) // ITER ITER ITER
|
||||
return false;
|
||||
if (!EmitAtomOp(cx, cx->names().next, JSOP_CALLPROP, bce)) // ITER ITER NEXT
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_SWAP) < 0) // ITER NEXT ITER
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_NOTEARG) < 0)
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) // ITER NEXT ITER UNDEFINED
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_NOTEARG) < 0)
|
||||
return false;
|
||||
if (EmitCall(cx, bce, JSOP_CALL, 1) < 0) // ITER RESULT
|
||||
return false;
|
||||
CheckTypeSet(cx, bce, JSOP_CALL);
|
||||
if (Emit1(cx, bce, JSOP_DUP) < 0) // ITER RESULT RESULT
|
||||
return false;
|
||||
if (!EmitAtomOp(cx, cx->names().done, JSOP_GETPROP, bce)) // ITER RESULT DONE?
|
||||
return false;
|
||||
|
||||
ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, top - bce->offset()); // ITER RESULT
|
||||
if (beq < 0)
|
||||
return false;
|
||||
|
||||
JS_ASSERT(bce->stackDepth == loopDepth);
|
||||
|
||||
// Let Ion know where the closing jump of this loop is.
|
||||
if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, beq - jmp))
|
||||
return false;
|
||||
|
||||
// Fixup breaks and continues.
|
||||
if (!PopStatementBCE(cx, bce))
|
||||
return false;
|
||||
|
||||
if (letDecl) {
|
||||
if (!PopStatementBCE(cx, bce))
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_LEAVEFORLETIN) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pop result, iter, and slots from the lexical block (if any).
|
||||
EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount + 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
||||
{
|
||||
@ -4553,10 +4734,14 @@ EmitNormalFor(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff
|
||||
static inline bool
|
||||
EmitFor(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
||||
{
|
||||
JS_ASSERT(pn->pn_left->isKind(PNK_FORIN) || pn->pn_left->isKind(PNK_FORHEAD));
|
||||
return pn->pn_left->isKind(PNK_FORIN)
|
||||
? EmitForIn(cx, bce, pn, top)
|
||||
: EmitNormalFor(cx, bce, pn, top);
|
||||
if (pn->pn_left->isKind(PNK_FORIN)) {
|
||||
// FIXME: Give for-of loops their own PNK. Bug 922066.
|
||||
if (pn->pn_iflags == JSITER_FOR_OF)
|
||||
return EmitForOf(cx, bce, pn, top);
|
||||
return EmitForIn(cx, bce, pn, top);
|
||||
}
|
||||
JS_ASSERT(pn->pn_left->isKind(PNK_FORHEAD));
|
||||
return EmitNormalFor(cx, bce, pn, top);
|
||||
}
|
||||
|
||||
static JS_NEVER_INLINE bool
|
||||
@ -4992,6 +5177,7 @@ EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter)
|
||||
return false;
|
||||
if (EmitCall(cx, bce, JSOP_CALL, 1) < 0) // ITER RESULT
|
||||
return false;
|
||||
CheckTypeSet(cx, bce, JSOP_CALL);
|
||||
JS_ASSERT(bce->stackDepth == depth + 1);
|
||||
ptrdiff_t checkResult = -1;
|
||||
if (EmitBackPatchOp(cx, bce, &checkResult) < 0) // goto checkResult
|
||||
@ -5032,6 +5218,7 @@ EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter)
|
||||
return false;
|
||||
if (EmitCall(cx, bce, JSOP_CALL, 1) < 0) // ITER RESULT
|
||||
return false;
|
||||
CheckTypeSet(cx, bce, JSOP_CALL);
|
||||
JS_ASSERT(bce->stackDepth == depth + 1);
|
||||
|
||||
if (!BackPatch(cx, bce, checkResult, bce->code().end(), JSOP_GOTO)) // checkResult:
|
||||
@ -6672,6 +6859,8 @@ CGConstList::finish(ConstArray *array)
|
||||
/*
|
||||
* We should try to get rid of offsetBias (always 0 or 1, where 1 is
|
||||
* JSOP_{NOP,POP}_LENGTH), which is used only by SRC_FOR.
|
||||
*
|
||||
* FIXME: Generate this using a higher-order macro. Bug 922070.
|
||||
*/
|
||||
const JSSrcNoteSpec js_SrcNoteSpec[] = {
|
||||
/* 0 */ {"null", 0},
|
||||
@ -6684,29 +6873,29 @@ const JSSrcNoteSpec js_SrcNoteSpec[] = {
|
||||
|
||||
/* 5 */ {"while", 1},
|
||||
/* 6 */ {"for-in", 1},
|
||||
/* 7 */ {"continue", 0},
|
||||
/* 8 */ {"break", 0},
|
||||
/* 9 */ {"break2label", 0},
|
||||
/* 10 */ {"switchbreak", 0},
|
||||
/* 7 */ {"for-of", 1},
|
||||
/* 8 */ {"continue", 0},
|
||||
/* 9 */ {"break", 0},
|
||||
/* 10 */ {"break2label", 0},
|
||||
/* 11 */ {"switchbreak", 0},
|
||||
|
||||
/* 11 */ {"tableswitch", 1},
|
||||
/* 12 */ {"condswitch", 2},
|
||||
/* 12 */ {"tableswitch", 1},
|
||||
/* 13 */ {"condswitch", 2},
|
||||
|
||||
/* 13 */ {"nextcase", 1},
|
||||
/* 14 */ {"nextcase", 1},
|
||||
|
||||
/* 14 */ {"assignop", 0},
|
||||
/* 15 */ {"assignop", 0},
|
||||
|
||||
/* 15 */ {"hidden", 0},
|
||||
/* 16 */ {"hidden", 0},
|
||||
|
||||
/* 16 */ {"catch", 0},
|
||||
/* 17 */ {"catch", 0},
|
||||
|
||||
/* 17 */ {"try", 1},
|
||||
/* 18 */ {"try", 1},
|
||||
|
||||
/* 18 */ {"colspan", 1},
|
||||
/* 19 */ {"newline", 0},
|
||||
/* 20 */ {"setline", 1},
|
||||
/* 19 */ {"colspan", 1},
|
||||
/* 20 */ {"newline", 0},
|
||||
/* 21 */ {"setline", 1},
|
||||
|
||||
/* 21 */ {"unused21", 0},
|
||||
/* 22 */ {"unused22", 0},
|
||||
/* 23 */ {"unused23", 0},
|
||||
|
||||
|
@ -3993,7 +3993,7 @@ Parser<FullParseHandler>::forStatement()
|
||||
* that receives the enumeration value each iteration, and pn3 is the
|
||||
* rhs of 'in'.
|
||||
*/
|
||||
forStmt.type = STMT_FOR_IN_LOOP;
|
||||
forStmt.type = isForOf ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
|
||||
|
||||
/* Set iflags and rule out invalid combinations. */
|
||||
if (isForOf && isForEach) {
|
||||
@ -4275,7 +4275,7 @@ Parser<SyntaxParseHandler>::forStatement()
|
||||
bool isForOf;
|
||||
if (lhsNode && matchInOrOf(&isForOf)) {
|
||||
/* Parse the rest of the for/in or for/of head. */
|
||||
forStmt.type = STMT_FOR_IN_LOOP;
|
||||
forStmt.type = isForOf ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
|
||||
|
||||
/* Check that the left side of the 'in' or 'of' is valid. */
|
||||
if (!isForDecl &&
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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]);
|
||||
|
@ -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");
|
||||
|
@ -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]);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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]);
|
||||
|
||||
|
@ -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]]);
|
||||
|
@ -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);
|
||||
|
@ -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.");
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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"]);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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.");
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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');
|
||||
|
@ -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() {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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');
|
||||
|
@ -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));
|
||||
|
6
js/src/jit-test/tests/for-of/completion.js
Normal file
6
js/src/jit-test/tests/for-of/completion.js
Normal file
@ -0,0 +1,6 @@
|
||||
// The completion value of a for-of loop is the completion value of the
|
||||
// last evaluation of the body, or undefined.
|
||||
|
||||
assertEq(eval("for (let x of [1, 2, 3]) { x }"), 3);
|
||||
assertEq(eval("for (let x of [1, 2, 3]) { x * 2 }"), 6);
|
||||
assertEq(eval("for (let x of []) { x }"), undefined);
|
@ -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');
|
||||
|
@ -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);
|
15
js/src/jit-test/tests/for-of/manual-advance.js
Normal file
15
js/src/jit-test/tests/for-of/manual-advance.js
Normal file
@ -0,0 +1,15 @@
|
||||
// Manually advancing the iterator.
|
||||
|
||||
load(libdir + 'iteration.js');
|
||||
|
||||
function* g(n) { for (var i=0; i<n; i++) yield i; }
|
||||
|
||||
var inner = g(20);
|
||||
|
||||
var n = 0;
|
||||
for (var x of inner) {
|
||||
assertEq(x, n * 2);
|
||||
assertIteratorResult(inner.next(), n * 2 + 1, false);
|
||||
n++;
|
||||
}
|
||||
assertEq(n, 10);
|
22
js/src/jit-test/tests/for-of/next-arity.js
Normal file
22
js/src/jit-test/tests/for-of/next-arity.js
Normal file
@ -0,0 +1,22 @@
|
||||
// For-of passes one arg to "next".
|
||||
|
||||
load(libdir + 'iteration.js')
|
||||
|
||||
var log = '';
|
||||
|
||||
function Iter() {
|
||||
function next() {
|
||||
log += 'n';
|
||||
assertEq(arguments.length, 1)
|
||||
assertEq(arguments[0], undefined)
|
||||
return { get value() { throw 42; }, done: true }
|
||||
}
|
||||
|
||||
this[std_iterator] = function () { return this; }
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
for (var x of new Iter())
|
||||
throw 'not reached';
|
||||
|
||||
assertEq(log, 'n');
|
41
js/src/jit-test/tests/for-of/next-shenanigans.js
Normal file
41
js/src/jit-test/tests/for-of/next-shenanigans.js
Normal file
@ -0,0 +1,41 @@
|
||||
// Test for-of with iter.next and monkeypatching.
|
||||
|
||||
function* g(n) { for (var i=0; i<n; i++) yield i; }
|
||||
var GeneratorObjectPrototype = Object.getPrototypeOf(g).prototype;
|
||||
var GeneratorObjectPrototype_next = GeneratorObjectPrototype.next;
|
||||
|
||||
// Monkeypatch next on an iterator.
|
||||
var inner = g(20);
|
||||
var n = 0;
|
||||
for (let x of inner) {
|
||||
if (n == 0) {
|
||||
assertEq(x, n++);
|
||||
} else if (n == 1) {
|
||||
assertEq(x, n++);
|
||||
inner.next = function() { return { value: 42, done: false }; };
|
||||
} else if (n == 2) {
|
||||
assertEq(x, 42);
|
||||
inner.next = function() { return { value: 100, done: true }; };
|
||||
} else
|
||||
throw 'not reached';
|
||||
}
|
||||
|
||||
// Monkeypatch next on the prototype.
|
||||
var inner = g(20);
|
||||
var n = 0;
|
||||
for (let x of inner) {
|
||||
if (n == 0) {
|
||||
assertEq(x, n++);
|
||||
} else if (n == 1) {
|
||||
assertEq(x, n++);
|
||||
GeneratorObjectPrototype.next =
|
||||
function() { return { value: 42, done: false }; };
|
||||
} else if (n == 2) {
|
||||
n++;
|
||||
assertEq(x, 42);
|
||||
GeneratorObjectPrototype.next = GeneratorObjectPrototype_next;
|
||||
} else if (n <= 20) {
|
||||
assertEq(x, n++ - 1);
|
||||
} else
|
||||
throw 'not reached';
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
// An exception thrown from a proxy trap while getting the .iterator method is propagated.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
load(libdir + "iteration.js");
|
||||
|
||||
var p = Proxy.create({
|
||||
getPropertyDescriptor: function (name) {
|
||||
if (name == "iterator")
|
||||
if (name == std_iterator)
|
||||
throw "fit";
|
||||
return undefined;
|
||||
}
|
||||
|
@ -7,5 +7,7 @@
|
||||
// Deleting Array.prototype.iterator makes for-of stop working on arrays.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
delete Array.prototype.iterator;
|
||||
load(libdir + "iteration.js");
|
||||
|
||||
delete Array.prototype[std_iterator];
|
||||
assertThrowsInstanceOf(function () { for (var x of []) ; }, TypeError);
|
||||
|
@ -1,8 +1,10 @@
|
||||
// Replacing Array.prototype.iterator with something non-callable makes for-of throw.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
load(libdir + "iteration.js");
|
||||
|
||||
function test(v) {
|
||||
Array.prototype.iterator = v;
|
||||
Array.prototype[std_iterator] = v;
|
||||
assertThrowsInstanceOf(function () { for (var x of []) ; }, TypeError);
|
||||
}
|
||||
test(undefined);
|
||||
|
@ -1,6 +1,8 @@
|
||||
// Replacing Array.prototype.iterator with a generator affects for-of behavior.
|
||||
|
||||
Array.prototype.iterator = function () {
|
||||
load(libdir + "iteration.js");
|
||||
|
||||
Array.prototype[std_iterator] = function* () {
|
||||
for (var i = this.length; --i >= 0; )
|
||||
yield this[i];
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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']) {
|
||||
|
@ -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';
|
||||
|
@ -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;
|
||||
}
|
||||
|
23
js/src/jit-test/tests/for-of/value-done-access.js
Normal file
23
js/src/jit-test/tests/for-of/value-done-access.js
Normal file
@ -0,0 +1,23 @@
|
||||
// Test that each yield* loop just checks "done", and "value" is only
|
||||
// fetched once at the end.
|
||||
|
||||
load(libdir + 'iteration.js');
|
||||
|
||||
var log = "";
|
||||
|
||||
function Iter(val, count) {
|
||||
function next() {
|
||||
return {
|
||||
get done() { log += "d"; return count-- == 0; },
|
||||
get value() { log += "v"; return val; }
|
||||
}
|
||||
}
|
||||
|
||||
this[std_iterator] = function() { return this; };
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
for (var x of new Iter(42, 5))
|
||||
assertEq(x, 42);
|
||||
|
||||
assertEq(log, "dvdvdvdvdvd");
|
@ -1,4 +1,5 @@
|
||||
// |jit-test| error: is not iterable
|
||||
// |jit-test| error: TypeError
|
||||
|
||||
try {
|
||||
x = []
|
||||
y = function() {}
|
||||
|
@ -2469,6 +2469,12 @@ BaselineCompiler::emit_JSOP_ENTERLET1()
|
||||
return emitEnterBlock();
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_ENTERLET2()
|
||||
{
|
||||
return emitEnterBlock();
|
||||
}
|
||||
|
||||
typedef bool (*LeaveBlockFn)(JSContext *, BaselineFrame *);
|
||||
static const VMFunction LeaveBlockInfo = FunctionInfo<LeaveBlockFn>(jit::LeaveBlock);
|
||||
|
||||
|
@ -148,6 +148,7 @@ namespace jit {
|
||||
_(JSOP_ENTERBLOCK) \
|
||||
_(JSOP_ENTERLET0) \
|
||||
_(JSOP_ENTERLET1) \
|
||||
_(JSOP_ENTERLET2) \
|
||||
_(JSOP_LEAVEBLOCK) \
|
||||
_(JSOP_LEAVEBLOCKEXPR) \
|
||||
_(JSOP_LEAVEFORLETIN) \
|
||||
|
@ -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);
|
||||
|
@ -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}")
|
||||
|
@ -252,6 +252,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
|
||||
|
||||
case JSOP_EVAL:
|
||||
case JSOP_SPREADEVAL:
|
||||
case JSOP_ENTERLET2:
|
||||
case JSOP_ENTERWITH:
|
||||
canTrackVars = false;
|
||||
break;
|
||||
|
@ -4202,7 +4202,15 @@ JS_DefineFunctions(JSContext *cx, JSObject *objArg, const JSFunctionSpec *fs)
|
||||
RootedObject ctor(cx);
|
||||
|
||||
for (; fs->name; fs++) {
|
||||
RootedAtom atom(cx, Atomize(cx, fs->name, strlen(fs->name)));
|
||||
RootedAtom atom(cx);
|
||||
// If the name starts with "@@", it must be a well-known symbol.
|
||||
if (fs->name[0] != '@' || fs->name[1] != '@')
|
||||
atom = Atomize(cx, fs->name, strlen(fs->name));
|
||||
else if (strcmp(fs->name, "@@iterator") == 0)
|
||||
// FIXME: This atom should be a symbol: bug 918828.
|
||||
atom = cx->names().std_iterator;
|
||||
else
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_SYMBOL, fs->name);
|
||||
if (!atom)
|
||||
return false;
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -565,12 +565,12 @@ bool
|
||||
js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleValue vp)
|
||||
{
|
||||
if (flags == JSITER_FOR_OF) {
|
||||
// for-of loop. The iterator is simply |obj.iterator()|.
|
||||
// for-of loop. The iterator is simply |obj[@@iterator]()|.
|
||||
RootedValue method(cx);
|
||||
if (!JSObject::getProperty(cx, obj, obj, cx->names().iterator, &method))
|
||||
if (!JSObject::getProperty(cx, obj, obj, cx->names().std_iterator, &method))
|
||||
return false;
|
||||
|
||||
// Throw if obj.iterator isn't callable. js::Invoke is about to check
|
||||
// Throw if obj[@@iterator] isn't callable. js::Invoke is about to check
|
||||
// for this kind of error anyway, but it would throw an inscrutable
|
||||
// error message about |method| rather than this nice one about |obj|.
|
||||
if (!method.isObject() || !method.toObject().isCallable()) {
|
||||
@ -593,6 +593,8 @@ js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleVa
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_ASSERT(!(flags & JSITER_FOR_OF));
|
||||
|
||||
Vector<Shape *, 8> shapes(cx);
|
||||
uint32_t key = 0;
|
||||
|
||||
@ -725,6 +727,30 @@ js::GetIteratorObject(JSContext *cx, HandleObject obj, uint32_t flags)
|
||||
return &value.toObject();
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js::CreateItrResultObject(JSContext *cx, HandleValue value, bool done)
|
||||
{
|
||||
// FIXME: We can cache the iterator result object shape somewhere.
|
||||
AssertHeapIsIdle(cx);
|
||||
|
||||
RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx));
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
|
||||
RootedObject obj(cx, NewObjectWithGivenProto(cx, &JSObject::class_, proto, cx->global()));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
if (!JSObject::defineProperty(cx, obj, cx->names().value, value))
|
||||
return nullptr;
|
||||
|
||||
RootedValue doneBool(cx, BooleanValue(done));
|
||||
if (!JSObject::defineProperty(cx, obj, cx->names().done, doneBool))
|
||||
return nullptr;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool
|
||||
js_ThrowStopIteration(JSContext *cx)
|
||||
{
|
||||
@ -781,14 +807,6 @@ iterator_next_impl(JSContext *cx, CallArgs args)
|
||||
return js_IteratorNext(cx, thisObj, args.rval());
|
||||
}
|
||||
|
||||
static bool
|
||||
iterator_iterator(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval().set(args.thisv());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
iterator_next(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
@ -797,7 +815,7 @@ iterator_next(JSContext *cx, unsigned argc, Value *vp)
|
||||
}
|
||||
|
||||
static const JSFunctionSpec iterator_methods[] = {
|
||||
JS_FN("iterator", iterator_iterator, 0, 0),
|
||||
JS_SELF_HOSTED_FN("@@iterator", "LegacyIteratorShim", 0, 0),
|
||||
JS_FN("next", iterator_next, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
@ -948,6 +966,7 @@ const Class ElementIteratorObject::class_ = {
|
||||
};
|
||||
|
||||
const JSFunctionSpec ElementIteratorObject::methods[] = {
|
||||
JS_SELF_HOSTED_FN("@@iterator", "LegacyIteratorShim", 0, 0),
|
||||
JS_FN("next", next, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
@ -1308,6 +1327,38 @@ const Class StopIterationObject::class_ = {
|
||||
NULL /* construct */
|
||||
};
|
||||
|
||||
bool
|
||||
ForOfIterator::next(MutableHandleValue vp, bool *done)
|
||||
{
|
||||
JS_ASSERT(iterator);
|
||||
|
||||
RootedValue method(cx);
|
||||
if (!JSObject::getProperty(cx, iterator, iterator, cx->names().next, &method))
|
||||
return false;
|
||||
|
||||
InvokeArgs args(cx);
|
||||
if (!args.init(1))
|
||||
return false;
|
||||
args.setCallee(method);
|
||||
args.setThis(ObjectValue(*iterator));
|
||||
args[0].setUndefined();
|
||||
if (!Invoke(cx, args))
|
||||
return false;
|
||||
|
||||
RootedObject resultObj(cx, ToObject(cx, args.rval()));
|
||||
if (!resultObj)
|
||||
return false;
|
||||
RootedValue doneVal(cx);
|
||||
if (!JSObject::getProperty(cx, resultObj, resultObj, cx->names().done, &doneVal))
|
||||
return false;
|
||||
*done = ToBoolean(doneVal);
|
||||
if (*done) {
|
||||
vp.setUndefined();
|
||||
return true;
|
||||
}
|
||||
return JSObject::getProperty(cx, resultObj, resultObj, cx->names().value, vp);
|
||||
}
|
||||
|
||||
/*** Generators **********************************************************************************/
|
||||
|
||||
template<typename T>
|
||||
@ -1796,14 +1847,14 @@ NativeMethod(JSContext *cx, unsigned argc, Value *vp)
|
||||
#define JS_METHOD(name, T, impl, len, attrs) JS_FN(name, (NativeMethod<T,impl>), len, attrs)
|
||||
|
||||
static const JSFunctionSpec star_generator_methods[] = {
|
||||
JS_FN("iterator", iterator_iterator, 0, 0),
|
||||
JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0),
|
||||
JS_METHOD("next", StarGeneratorObject, star_generator_next, 1, 0),
|
||||
JS_METHOD("throw", StarGeneratorObject, star_generator_throw, 1, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static const JSFunctionSpec legacy_generator_methods[] = {
|
||||
JS_FN("iterator", iterator_iterator, 0, 0),
|
||||
JS_SELF_HOSTED_FN("@@iterator", "LegacyGeneratorIteratorShim", 0, 0),
|
||||
// "send" is an alias for "next".
|
||||
JS_METHOD("next", LegacyGeneratorObject, legacy_generator_next, 1, JSPROP_ROPERM),
|
||||
JS_METHOD("send", LegacyGeneratorObject, legacy_generator_next, 1, JSPROP_ROPERM),
|
||||
|
@ -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 */
|
||||
|
||||
/*
|
||||
|
@ -119,9 +119,11 @@ js_GetVariableBytecodeLength(jsbytecode *pc)
|
||||
static uint32_t
|
||||
NumBlockSlots(JSScript *script, jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(*pc == JSOP_ENTERBLOCK || *pc == JSOP_ENTERLET0 || *pc == JSOP_ENTERLET1);
|
||||
JS_ASSERT(*pc == JSOP_ENTERBLOCK ||
|
||||
*pc == JSOP_ENTERLET0 || *pc == JSOP_ENTERLET1 || *pc == JSOP_ENTERLET2);
|
||||
JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET0_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET1_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET2_LENGTH);
|
||||
|
||||
return script->getObject(GET_UINT32_INDEX(pc))->as<StaticBlockObject>().slotCount();
|
||||
}
|
||||
@ -146,6 +148,8 @@ js::StackUses(JSScript *script, jsbytecode *pc)
|
||||
return NumBlockSlots(script, pc);
|
||||
case JSOP_ENTERLET1:
|
||||
return NumBlockSlots(script, pc) + 1;
|
||||
case JSOP_ENTERLET2:
|
||||
return NumBlockSlots(script, pc) + 2;
|
||||
default:
|
||||
/* stack: fun, this, [argc arguments] */
|
||||
JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL || op == JSOP_EVAL ||
|
||||
@ -163,7 +167,11 @@ js::StackDefs(JSScript *script, jsbytecode *pc)
|
||||
return cs.ndefs;
|
||||
|
||||
uint32_t n = NumBlockSlots(script, pc);
|
||||
return op == JSOP_ENTERLET1 ? n + 1 : n;
|
||||
if (op == JSOP_ENTERLET1)
|
||||
return n + 1;
|
||||
if (op == JSOP_ENTERLET2)
|
||||
return n + 2;
|
||||
return n;
|
||||
}
|
||||
|
||||
static const char * const countBaseNames[] = {
|
||||
@ -1051,7 +1059,8 @@ GetBlockChainAtPC(JSContext *cx, JSScript *script, jsbytecode *pc)
|
||||
switch (op) {
|
||||
case JSOP_ENTERBLOCK:
|
||||
case JSOP_ENTERLET0:
|
||||
case JSOP_ENTERLET1: {
|
||||
case JSOP_ENTERLET1:
|
||||
case JSOP_ENTERLET2: {
|
||||
JSObject *child = script->getObject(p);
|
||||
JS_ASSERT_IF(blockChain, child->as<BlockObject>().stackDepth() >=
|
||||
blockChain->as<BlockObject>().stackDepth());
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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") \
|
||||
|
@ -1479,7 +1479,6 @@ BEGIN_CASE(JSOP_UNUSED180)
|
||||
BEGIN_CASE(JSOP_UNUSED181)
|
||||
BEGIN_CASE(JSOP_UNUSED182)
|
||||
BEGIN_CASE(JSOP_UNUSED183)
|
||||
BEGIN_CASE(JSOP_UNUSED188)
|
||||
BEGIN_CASE(JSOP_UNUSED189)
|
||||
BEGIN_CASE(JSOP_UNUSED190)
|
||||
BEGIN_CASE(JSOP_UNUSED200)
|
||||
@ -3044,21 +3043,26 @@ BEGIN_CASE(JSOP_SPREAD)
|
||||
RootedObject &arr = rootObject0;
|
||||
arr = ®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--;
|
||||
}
|
||||
@ -3175,6 +3179,7 @@ END_CASE(JSOP_DEBUGGER)
|
||||
BEGIN_CASE(JSOP_ENTERBLOCK)
|
||||
BEGIN_CASE(JSOP_ENTERLET0)
|
||||
BEGIN_CASE(JSOP_ENTERLET1)
|
||||
BEGIN_CASE(JSOP_ENTERLET2)
|
||||
{
|
||||
StaticBlockObject &blockObj = script->getObject(regs.pc)->as<StaticBlockObject>();
|
||||
|
||||
|
@ -558,6 +558,20 @@ js::intrinsic_HaveSameClass(JSContext *cx, unsigned argc, Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
intrinsic_GetIteratorPrototype(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JS_ASSERT(args.length() == 0);
|
||||
|
||||
JSObject *obj = cx->global()->getOrCreateIteratorPrototype(cx);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* ParallelTestsShouldPass(): Returns false if we are running in a
|
||||
* mode (such as --ion-eager) that is known to cause additional
|
||||
@ -639,6 +653,8 @@ const JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_FN("NewObjectWithClassPrototype", intrinsic_NewObjectWithClassPrototype, 1,0),
|
||||
JS_FN("HaveSameClass", intrinsic_HaveSameClass, 2,0),
|
||||
|
||||
JS_FN("GetIteratorPrototype", intrinsic_GetIteratorPrototype, 0,0),
|
||||
|
||||
JS_FN("ForkJoin", intrinsic_ForkJoin, 2,0),
|
||||
JS_FN("ForkJoinSlices", intrinsic_ForkJoinSlices, 0,0),
|
||||
JS_FN("NewParallelArray", intrinsic_NewParallelArray, 3,0),
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user