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

This commit is contained in:
mrbkap@gmail.com 2007-09-15 09:58:45 -07:00
parent eaabef106d
commit 6426c64ef1
3 changed files with 61 additions and 38 deletions

View File

@ -1214,11 +1214,11 @@ struct JSExtendedClass {
JSEqualityOp equality; JSEqualityOp equality;
JSObjectOp outerObject; JSObjectOp outerObject;
JSObjectOp innerObject; JSObjectOp innerObject;
JSIteratorOp iteratorObject;
void (*reserved0)(); void (*reserved0)();
void (*reserved1)(); void (*reserved1)();
void (*reserved2)(); void (*reserved2)();
void (*reserved3)(); void (*reserved3)();
void (*reserved4)();
}; };
#define JSCLASS_HAS_PRIVATE (1<<0) /* objects have private slot */ #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. */ /* 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_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. */ /* For detailed comments on these function pointer types, see jspubtd.h. */
struct JSObjectOps { struct JSObjectOps {

View File

@ -334,6 +334,8 @@ js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp)
JSObject *obj; JSObject *obj;
JSTempValueRooter tvr; JSTempValueRooter tvr;
JSAtom *atom; JSAtom *atom;
JSClass *clasp;
JSExtendedClass *xclasp;
JSBool ok; JSBool ok;
JSObject *iterobj; JSObject *iterobj;
jsval arg; jsval arg;
@ -370,47 +372,60 @@ js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp)
JS_ASSERT(obj); JS_ASSERT(obj);
JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
atom = cx->runtime->atomState.iteratorAtom; clasp = OBJ_GET_CLASS(cx, obj);
#if JS_HAS_XML_SUPPORT if ((clasp->flags & JSCLASS_IS_EXTENDED) &&
if (OBJECT_IS_XML(cx, obj)) { (xclasp = (JSExtendedClass *) clasp)->iteratorObject) {
if (!js_GetXMLFunction(cx, obj, ATOM_TO_JSID(atom), vp)) iterobj = xclasp->iteratorObject(cx, obj, !(flags & JSITER_FOREACH));
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) if (!iterobj)
goto bad; goto bad;
/* Store iterobj in *vp to protect it from GC (callers must root vp). */
*vp = OBJECT_TO_JSVAL(iterobj); *vp = OBJECT_TO_JSVAL(iterobj);
if (!InitNativeIterator(cx, iterobj, obj, flags))
goto bad;
} else { } else {
arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0); atom = cx->runtime->atomState.iteratorAtom;
if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg, vp)) #if JS_HAS_XML_SUPPORT
goto bad; if (OBJECT_IS_XML(cx, obj)) {
if (JSVAL_IS_PRIMITIVE(*vp)) { if (!js_GetXMLFunction(cx, obj, ATOM_TO_JSID(atom), vp))
const char *printable = js_AtomToPrintableString(cx, atom); goto bad;
if (printable) { } else
js_ReportValueError2(cx, JSMSG_BAD_ITERATOR_RETURN, #endif
JSDVG_SEARCH_STACK, *vp, NULL, printable); {
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;
} }
} }

View File

@ -524,6 +524,14 @@ typedef JSBool
typedef JSObject * typedef JSObject *
(* JS_DLL_CALLBACK JSObjectOp)(JSContext *cx, JSObject *obj); (* 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 * A generic type for functions taking a context, object, and property, with
* no return value. Used by JSObjectOps.dropProperty currently (see above, * no return value. Used by JSObjectOps.dropProperty currently (see above,