Bug 725907 - for-of improvements, part 2: Make for-of loops just call .iterator() instead of using the magic iteratorObject hook with an extra flag. r=bhackett.
This commit is contained in:
@@ -51,8 +51,6 @@ using namespace mozilla;
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
static JSObject *iterator_iterator(JSContext *cx, HandleObject obj, JSBool keysonly);
|
||||
|
||||
Class js::ElementIteratorClass = {
|
||||
"ElementIterator",
|
||||
JSCLASS_HAS_RESERVED_SLOTS(ElementIteratorObject::NumSlots),
|
||||
@@ -73,7 +71,7 @@ Class js::ElementIteratorClass = {
|
||||
NULL, /* equality */
|
||||
NULL, /* outerObject */
|
||||
NULL, /* innerObject */
|
||||
iterator_iterator,
|
||||
NULL, /* iteratorObject */
|
||||
NULL /* unused */
|
||||
}
|
||||
};
|
||||
@@ -368,18 +366,8 @@ GetCustomIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
/*
|
||||
* for-of iteration does not fall back on __iterator__ or property
|
||||
* enumeration. This is more conservative than the current proposed spec.
|
||||
*/
|
||||
if (flags == JSITER_FOR_OF) {
|
||||
js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NOT_ITERABLE,
|
||||
JSDVG_SEARCH_STACK, ObjectValue(*obj), NULL, NULL, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check whether we have a valid __iterator__ method. */
|
||||
PropertyName *name = cx->runtime->atomState.iteratorAtom;
|
||||
PropertyName *name = cx->runtime->atomState.iteratorIntrinsicAtom;
|
||||
if (!GetMethod(cx, obj, name, 0, vp))
|
||||
return false;
|
||||
|
||||
@@ -602,30 +590,42 @@ UpdateNativeIterator(NativeIterator *ni, JSObject *obj)
|
||||
bool
|
||||
GetIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp)
|
||||
{
|
||||
if (flags == JSITER_FOR_OF) {
|
||||
// for-of loop. The iterator is simply |obj.iterator()|.
|
||||
Value method;
|
||||
if (!obj->getProperty(cx, obj, cx->runtime->atomState.iteratorAtom, &method))
|
||||
return false;
|
||||
|
||||
// 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()) {
|
||||
char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, ObjectOrNullValue(obj), NULL);
|
||||
if (!bytes)
|
||||
return false;
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_ITERABLE, bytes);
|
||||
cx->free_(bytes);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Invoke(cx, ObjectOrNullValue(obj), method, 0, NULL, vp))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector<Shape *, 8> shapes(cx);
|
||||
uint32_t key = 0;
|
||||
|
||||
bool keysOnly = (flags == JSITER_ENUMERATE);
|
||||
|
||||
if (obj) {
|
||||
/* Enumerate Iterator.prototype directly. */
|
||||
if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) {
|
||||
/*
|
||||
* Arrays and other classes representing iterable collections have
|
||||
* the JSCLASS_FOR_OF_ITERATION flag. This flag means that the
|
||||
* object responds to all other kinds of enumeration (for-in,
|
||||
* for-each, Object.keys, Object.getOwnPropertyNames, etc.) in the
|
||||
* default way, ignoring the hook. The hook is used only when
|
||||
* iterating in the style of a for-of loop.
|
||||
*/
|
||||
if (!(obj->getClass()->flags & JSCLASS_FOR_OF_ITERATION) || flags == JSITER_FOR_OF) {
|
||||
JSObject *iterobj = op(cx, obj, !(flags & (JSITER_FOREACH | JSITER_FOR_OF)));
|
||||
if (!iterobj)
|
||||
return false;
|
||||
vp->setObject(*iterobj);
|
||||
types::MarkIteratorUnknown(cx);
|
||||
return true;
|
||||
}
|
||||
JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH));
|
||||
if (!iterobj)
|
||||
return false;
|
||||
vp->setObject(*iterobj);
|
||||
types::MarkIteratorUnknown(cx);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keysOnly) {
|
||||
@@ -734,12 +734,6 @@ GetIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp)
|
||||
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
iterator_iterator(JSContext *cx, HandleObject obj, JSBool keysonly)
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
Iterator(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
@@ -794,6 +788,14 @@ iterator_next_impl(JSContext *cx, CallArgs args)
|
||||
return js_IteratorNext(cx, thisObj, &args.rval());
|
||||
}
|
||||
|
||||
static JSBool
|
||||
iterator_iterator(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
args.rval() = args.thisv();
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
iterator_next(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
@@ -801,13 +803,18 @@ iterator_next(JSContext *cx, unsigned argc, Value *vp)
|
||||
return CallNonGenericMethod(cx, IsIterator, iterator_next_impl, args);
|
||||
}
|
||||
|
||||
#define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
|
||||
|
||||
static JSFunctionSpec iterator_methods[] = {
|
||||
JS_FN(js_next_str, iterator_next, 0,JSPROP_ROPERM),
|
||||
JS_FN("iterator", iterator_iterator, 0, 0),
|
||||
JS_FN("next", iterator_next, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
JSObject *
|
||||
iterator_iteratorObject(JSContext *cx, HandleObject obj, JSBool keysonly)
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
|
||||
void
|
||||
PropertyIteratorObject::trace(JSTracer *trc, JSObject *obj)
|
||||
{
|
||||
@@ -846,7 +853,7 @@ Class PropertyIteratorObject::class_ = {
|
||||
NULL, /* equality */
|
||||
NULL, /* outerObject */
|
||||
NULL, /* innerObject */
|
||||
iterator_iterator,
|
||||
iterator_iteratorObject,
|
||||
NULL /* unused */
|
||||
}
|
||||
};
|
||||
@@ -856,11 +863,7 @@ static JSBool
|
||||
CloseGenerator(JSContext *cx, JSObject *genobj);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
|
||||
* Otherwise construct the default iterator.
|
||||
*/
|
||||
JSBool
|
||||
bool
|
||||
js::ValueToIterator(JSContext *cx, unsigned flags, Value *vp)
|
||||
{
|
||||
/* JSITER_KEYVALUE must always come with JSITER_FOREACH */
|
||||
@@ -1291,7 +1294,7 @@ stopiter_hasInstance(JSContext *cx, HandleObject obj, const Value *v, JSBool *bp
|
||||
}
|
||||
|
||||
Class js::StopIterationClass = {
|
||||
js_StopIteration_str,
|
||||
"StopIteration",
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration) |
|
||||
JSCLASS_FREEZE_PROTO,
|
||||
JS_PropertyStub, /* addProperty */
|
||||
@@ -1406,7 +1409,7 @@ Class js::GeneratorClass = {
|
||||
NULL, /* equality */
|
||||
NULL, /* outerObject */
|
||||
NULL, /* innerObject */
|
||||
iterator_iterator,
|
||||
iterator_iteratorObject,
|
||||
NULL /* unused */
|
||||
}
|
||||
};
|
||||
@@ -1737,11 +1740,14 @@ generator_close(JSContext *cx, unsigned argc, Value *vp)
|
||||
return CallNonGenericMethod(cx, IsGenerator, generator_close_impl, args);
|
||||
}
|
||||
|
||||
#define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
|
||||
|
||||
static JSFunctionSpec generator_methods[] = {
|
||||
JS_FN(js_next_str, generator_next, 0,JSPROP_ROPERM),
|
||||
JS_FN(js_send_str, generator_send, 1,JSPROP_ROPERM),
|
||||
JS_FN(js_throw_str, generator_throw, 1,JSPROP_ROPERM),
|
||||
JS_FN(js_close_str, generator_close, 0,JSPROP_ROPERM),
|
||||
JS_FN("iterator", iterator_iterator, 0, 0),
|
||||
JS_FN("next", generator_next, 0,JSPROP_ROPERM),
|
||||
JS_FN("send", generator_send, 1,JSPROP_ROPERM),
|
||||
JS_FN("throw", generator_throw, 1,JSPROP_ROPERM),
|
||||
JS_FN("close", generator_close, 0,JSPROP_ROPERM),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user