From 6426c64ef1a1e8f73503039bd3dfd1aa0533ec9b Mon Sep 17 00:00:00 2001 From: "mrbkap@gmail.com" Date: Sat, 15 Sep 2007 09:58:45 -0700 Subject: [PATCH] Create a JSClass hook to allow object classes to easily support custom iteration without having to override __iterator__ in a resolve hook. bug 393306, r+a=brendan --- js/src/jsapi.h | 4 +-- js/src/jsiter.c | 87 ++++++++++++++++++++++++++++-------------------- js/src/jspubtd.h | 8 +++++ 3 files changed, 61 insertions(+), 38 deletions(-) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 937562a9a4e..e80b71549a2 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1214,11 +1214,11 @@ struct JSExtendedClass { JSEqualityOp equality; JSObjectOp outerObject; JSObjectOp innerObject; + JSIteratorOp iteratorObject; void (*reserved0)(); void (*reserved1)(); void (*reserved2)(); void (*reserved3)(); - void (*reserved4)(); }; #define JSCLASS_HAS_PRIVATE (1<<0) /* objects have private slot */ @@ -1287,7 +1287,7 @@ struct JSExtendedClass { /* Initializer for unused members of statically initialized JSClass structs. */ #define JSCLASS_NO_OPTIONAL_MEMBERS 0,0,0,0,0,0,0,0 -#define JSCLASS_NO_RESERVED_MEMBERS 0,0,0,0,0 +#define JSCLASS_NO_RESERVED_MEMBERS 0,0,0,0 /* For detailed comments on these function pointer types, see jspubtd.h. */ struct JSObjectOps { diff --git a/js/src/jsiter.c b/js/src/jsiter.c index 43dbfe6d1d0..8fe22eddb84 100644 --- a/js/src/jsiter.c +++ b/js/src/jsiter.c @@ -334,6 +334,8 @@ js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp) JSObject *obj; JSTempValueRooter tvr; JSAtom *atom; + JSClass *clasp; + JSExtendedClass *xclasp; JSBool ok; JSObject *iterobj; jsval arg; @@ -370,47 +372,60 @@ js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp) JS_ASSERT(obj); JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); - atom = cx->runtime->atomState.iteratorAtom; -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, obj)) { - if (!js_GetXMLFunction(cx, obj, ATOM_TO_JSID(atom), vp)) - goto bad; - } else -#endif - { - if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp)) - goto bad; - } - - if (JSVAL_IS_VOID(*vp)) { - default_iter: - /* - * Fail over to the default enumerating native iterator. - * - * Create iterobj with a NULL parent to ensure that we use the correct - * scope chain to lookup the iterator's constructor. Since we use the - * parent slot to keep track of the iterable, we must fix it up after. - */ - iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL); + clasp = OBJ_GET_CLASS(cx, obj); + if ((clasp->flags & JSCLASS_IS_EXTENDED) && + (xclasp = (JSExtendedClass *) clasp)->iteratorObject) { + iterobj = xclasp->iteratorObject(cx, obj, !(flags & JSITER_FOREACH)); if (!iterobj) goto bad; - - /* Store iterobj in *vp to protect it from GC (callers must root vp). */ *vp = OBJECT_TO_JSVAL(iterobj); - - if (!InitNativeIterator(cx, iterobj, obj, flags)) - goto bad; } else { - arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0); - if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg, vp)) - goto bad; - if (JSVAL_IS_PRIMITIVE(*vp)) { - const char *printable = js_AtomToPrintableString(cx, atom); - if (printable) { - js_ReportValueError2(cx, JSMSG_BAD_ITERATOR_RETURN, - JSDVG_SEARCH_STACK, *vp, NULL, printable); + atom = cx->runtime->atomState.iteratorAtom; +#if JS_HAS_XML_SUPPORT + if (OBJECT_IS_XML(cx, obj)) { + if (!js_GetXMLFunction(cx, obj, ATOM_TO_JSID(atom), vp)) + goto bad; + } else +#endif + { + if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp)) + goto bad; + } + + if (JSVAL_IS_VOID(*vp)) { + default_iter: + /* + * Fail over to the default enumerating native iterator. + * + * Create iterobj with a NULL parent to ensure that we use the + * correct scope chain to lookup the iterator's constructor. Since + * we use the parent slot to keep track of the iterable, we must + * fix it up after. + */ + iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL); + if (!iterobj) + goto bad; + + /* Store in *vp to protect it from GC (callers must root vp). */ + *vp = OBJECT_TO_JSVAL(iterobj); + + if (!InitNativeIterator(cx, iterobj, obj, flags)) + goto bad; + } else { + arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0); + if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg, + vp)) { + goto bad; + } + if (JSVAL_IS_PRIMITIVE(*vp)) { + const char *printable = js_AtomToPrintableString(cx, atom); + if (printable) { + js_ReportValueError2(cx, JSMSG_BAD_ITERATOR_RETURN, + JSDVG_SEARCH_STACK, *vp, NULL, + printable); + } + goto bad; } - goto bad; } } diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index f72b5b30edc..63757037dae 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -524,6 +524,14 @@ typedef JSBool typedef JSObject * (* JS_DLL_CALLBACK JSObjectOp)(JSContext *cx, JSObject *obj); +/* + * Hook that creates an iterator object for a given object. Returns the + * iterator object or null if an error or exception was thrown on cx. + */ +typedef JSObject * +(* JS_DLL_CALLBACK JSIteratorOp)(JSContext *cx, JSObject *obj, + JSBool keysonly); + /* * A generic type for functions taking a context, object, and property, with * no return value. Used by JSObjectOps.dropProperty currently (see above,