diff options
Diffstat (limited to 'third_party/js-1.7/jsiter.c')
-rw-r--r-- | third_party/js-1.7/jsiter.c | 1080 |
1 files changed, 0 insertions, 1080 deletions
diff --git a/third_party/js-1.7/jsiter.c b/third_party/js-1.7/jsiter.c deleted file mode 100644 index 0a4de54214f..00000000000 --- a/third_party/js-1.7/jsiter.c +++ /dev/null @@ -1,1080 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JavaScript iterators. - */ -#include "jsstddef.h" -#include <string.h> /* for memcpy */ -#include "jstypes.h" -#include "jsutil.h" -#include "jsarena.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsexn.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jsiter.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsscope.h" -#include "jsscript.h" - -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - -extern const char js_throw_str[]; /* from jsscan.h */ - -#define JSSLOT_ITER_STATE (JSSLOT_PRIVATE) -#define JSSLOT_ITER_FLAGS (JSSLOT_PRIVATE + 1) - -#if JSSLOT_ITER_FLAGS >= JS_INITIAL_NSLOTS -#error JS_INITIAL_NSLOTS must be greater than JSSLOT_ITER_FLAGS. -#endif - -/* - * Shared code to close iterator's state either through an explicit call or - * when GC detects that the iterator is no longer reachable. - */ -void -js_CloseIteratorState(JSContext *cx, JSObject *iterobj) -{ - jsval *slots; - jsval state, parent; - JSObject *iterable; - - JS_ASSERT(JS_InstanceOf(cx, iterobj, &js_IteratorClass, NULL)); - slots = iterobj->slots; - - /* Avoid double work if js_CloseNativeIterator was called on obj. */ - state = slots[JSSLOT_ITER_STATE]; - if (JSVAL_IS_NULL(state)) - return; - - /* Protect against failure to fully initialize obj. */ - parent = slots[JSSLOT_PARENT]; - if (!JSVAL_IS_PRIMITIVE(parent)) { - iterable = JSVAL_TO_OBJECT(parent); -#if JS_HAS_XML_SUPPORT - if ((JSVAL_TO_INT(slots[JSSLOT_ITER_FLAGS]) & JSITER_FOREACH) && - OBJECT_IS_XML(cx, iterable)) { - ((JSXMLObjectOps *) iterable->map->ops)-> - enumerateValues(cx, iterable, JSENUMERATE_DESTROY, &state, - NULL, NULL); - } else -#endif - OBJ_ENUMERATE(cx, iterable, JSENUMERATE_DESTROY, &state, NULL); - } - slots[JSSLOT_ITER_STATE] = JSVAL_NULL; -} - -JSClass js_IteratorClass = { - "Iterator", - JSCLASS_HAS_RESERVED_SLOTS(2) | /* slots for state and flags */ - JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -static JSBool -InitNativeIterator(JSContext *cx, JSObject *iterobj, JSObject *obj, uintN flags) -{ - jsval state; - JSBool ok; - - JS_ASSERT(JSVAL_TO_PRIVATE(iterobj->slots[JSSLOT_CLASS]) == - &js_IteratorClass); - - /* Initialize iterobj in case of enumerate hook failure. */ - iterobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); - iterobj->slots[JSSLOT_ITER_STATE] = JSVAL_NULL; - iterobj->slots[JSSLOT_ITER_FLAGS] = INT_TO_JSVAL(flags); - if (!js_RegisterCloseableIterator(cx, iterobj)) - return JS_FALSE; - if (!obj) - return JS_TRUE; - - ok = -#if JS_HAS_XML_SUPPORT - ((flags & JSITER_FOREACH) && OBJECT_IS_XML(cx, obj)) - ? ((JSXMLObjectOps *) obj->map->ops)-> - enumerateValues(cx, obj, JSENUMERATE_INIT, &state, NULL, NULL) - : -#endif - OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL); - if (!ok) - return JS_FALSE; - - iterobj->slots[JSSLOT_ITER_STATE] = state; - if (flags & JSITER_ENUMERATE) { - /* - * The enumerating iterator needs the original object to suppress - * enumeration of deleted or shadowed prototype properties. Since the - * enumerator never escapes to scripts, we use the prototype slot to - * store the original object. - */ - JS_ASSERT(obj != iterobj); - iterobj->slots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(obj); - } - return JS_TRUE; -} - -static JSBool -Iterator(JSContext *cx, JSObject *iterobj, uintN argc, jsval *argv, jsval *rval) -{ - JSBool keyonly; - uintN flags; - JSObject *obj; - - keyonly = JS_FALSE; - if (!js_ValueToBoolean(cx, argv[1], &keyonly)) - return JS_FALSE; - flags = keyonly ? 0 : JSITER_FOREACH; - - if (cx->fp->flags & JSFRAME_CONSTRUCTING) { - /* XXX work around old valueOf call hidden beneath js_ValueToObject */ - if (!JSVAL_IS_PRIMITIVE(argv[0])) { - obj = JSVAL_TO_OBJECT(argv[0]); - } else { - obj = js_ValueToNonNullObject(cx, argv[0]); - if (!obj) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(obj); - } - return InitNativeIterator(cx, iterobj, obj, flags); - } - - *rval = argv[0]; - return js_ValueToIterator(cx, flags, rval); -} - -static JSBool -NewKeyValuePair(JSContext *cx, jsid key, jsval val, jsval *rval) -{ - jsval vec[2]; - JSTempValueRooter tvr; - JSObject *aobj; - - vec[0] = ID_TO_VALUE(key); - vec[1] = val; - - JS_PUSH_TEMP_ROOT(cx, 2, vec, &tvr); - aobj = js_NewArrayObject(cx, 2, vec); - *rval = OBJECT_TO_JSVAL(aobj); - JS_POP_TEMP_ROOT(cx, &tvr); - - return aobj != NULL; -} - -static JSBool -IteratorNextImpl(JSContext *cx, JSObject *obj, jsval *rval) -{ - JSObject *iterable; - jsval state; - uintN flags; - JSBool foreach, ok; - jsid id; - - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_IteratorClass); - - iterable = OBJ_GET_PARENT(cx, obj); - JS_ASSERT(iterable); - state = OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_STATE); - if (JSVAL_IS_NULL(state)) - goto stop; - - flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_FLAGS)); - JS_ASSERT(!(flags & JSITER_ENUMERATE)); - foreach = (flags & JSITER_FOREACH) != 0; - ok = -#if JS_HAS_XML_SUPPORT - (foreach && OBJECT_IS_XML(cx, iterable)) - ? ((JSXMLObjectOps *) iterable->map->ops)-> - enumerateValues(cx, iterable, JSENUMERATE_NEXT, &state, - &id, rval) - : -#endif - OBJ_ENUMERATE(cx, iterable, JSENUMERATE_NEXT, &state, &id); - if (!ok) - return JS_FALSE; - - OBJ_SET_SLOT(cx, obj, JSSLOT_ITER_STATE, state); - if (JSVAL_IS_NULL(state)) - goto stop; - - if (foreach) { -#if JS_HAS_XML_SUPPORT - if (!OBJECT_IS_XML(cx, iterable) && - !OBJ_GET_PROPERTY(cx, iterable, id, rval)) { - return JS_FALSE; - } -#endif - if (!NewKeyValuePair(cx, id, *rval, rval)) - return JS_FALSE; - } else { - *rval = ID_TO_VALUE(id); - } - return JS_TRUE; - - stop: - JS_ASSERT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_STATE) == JSVAL_NULL); - *rval = JSVAL_HOLE; - return JS_TRUE; -} - -static JSBool -js_ThrowStopIteration(JSContext *cx, JSObject *obj) -{ - jsval v; - - JS_ASSERT(!JS_IsExceptionPending(cx)); - if (js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_StopIteration), &v)) - JS_SetPendingException(cx, v); - return JS_FALSE; -} - -static JSBool -iterator_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - if (!JS_InstanceOf(cx, obj, &js_IteratorClass, argv)) - return JS_FALSE; - - if (!IteratorNextImpl(cx, obj, rval)) - return JS_FALSE; - - if (*rval == JSVAL_HOLE) { - *rval = JSVAL_NULL; - js_ThrowStopIteration(cx, obj); - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -iterator_self(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSFunctionSpec iterator_methods[] = { - {js_iterator_str, iterator_self, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_next_str, iterator_next, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {0,0,0,0,0} -}; - -uintN -js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj) -{ - if (OBJ_GET_CLASS(cx, iterobj) != &js_IteratorClass) - return 0; - return JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS)); -} - -void -js_CloseNativeIterator(JSContext *cx, JSObject *iterobj) -{ - uintN flags; - - /* - * If this iterator is not an instance of the native default iterator - * class, leave it to be GC'ed. - */ - if (!JS_InstanceOf(cx, iterobj, &js_IteratorClass, NULL)) - return; - - /* - * If this iterator was not created by js_ValueToIterator called from the - * for-in loop code in js_Interpret, leave it to be GC'ed. - */ - flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS)); - if (!(flags & JSITER_ENUMERATE)) - return; - - js_CloseIteratorState(cx, iterobj); -} - -/* - * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists. - * Otherwise construct the defualt iterator. - */ -JSBool -js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp) -{ - JSObject *obj; - JSTempValueRooter tvr; - const JSAtom *atom; - JSBool ok; - JSObject *iterobj; - jsval arg; - JSString *str; - - JS_ASSERT(!(flags & ~(JSITER_ENUMERATE | - JSITER_FOREACH | - JSITER_KEYVALUE))); - - /* JSITER_KEYVALUE must always come with JSITER_FOREACH */ - JS_ASSERT(!(flags & JSITER_KEYVALUE) || (flags & JSITER_FOREACH)); - - /* XXX work around old valueOf call hidden beneath js_ValueToObject */ - if (!JSVAL_IS_PRIMITIVE(*vp)) { - obj = JSVAL_TO_OBJECT(*vp); - } else { - /* - * Enumerating over null and undefined gives an empty enumerator. - * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of - * the first production in 12.6.4 and step 4 of the second production, - * but it's "web JS" compatible. - */ - if ((flags & JSITER_ENUMERATE)) { - if (!js_ValueToObject(cx, *vp, &obj)) - return JS_FALSE; - if (!obj) - goto default_iter; - } else { - obj = js_ValueToNonNullObject(cx, *vp); - if (!obj) - return JS_FALSE; - } - } - - 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); - 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)) { - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, *vp, NULL); - if (str) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ITERATOR_RETURN, - JSSTRING_CHARS(str), - JSSTRING_CHARS(ATOM_TO_STRING(atom))); - } - goto bad; - } - } - - ok = JS_TRUE; - out: - if (obj) - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; - bad: - ok = JS_FALSE; - goto out; -} - -static JSBool -CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval) -{ - JSObject *obj, *origobj; - jsval state; - JSBool foreach; - jsid id; - JSObject *obj2; - JSBool cond; - JSClass *clasp; - JSExtendedClass *xclasp; - JSProperty *prop; - JSString *str; - - JS_ASSERT(flags & JSITER_ENUMERATE); - JS_ASSERT(JSVAL_TO_PRIVATE(iterobj->slots[JSSLOT_CLASS]) == - &js_IteratorClass); - - obj = JSVAL_TO_OBJECT(iterobj->slots[JSSLOT_PARENT]); - origobj = JSVAL_TO_OBJECT(iterobj->slots[JSSLOT_PROTO]); - state = iterobj->slots[JSSLOT_ITER_STATE]; - if (JSVAL_IS_NULL(state)) - goto stop; - - foreach = (flags & JSITER_FOREACH) != 0; -#if JS_HAS_XML_SUPPORT - /* - * Treat an XML object specially only when it starts the prototype chain. - * Otherwise we need to do the usual deleted and shadowed property checks. - */ - if (obj == origobj && OBJECT_IS_XML(cx, obj)) { - if (foreach) { - JSXMLObjectOps *xmlops = (JSXMLObjectOps *) obj->map->ops; - - if (!xmlops->enumerateValues(cx, obj, JSENUMERATE_NEXT, &state, - &id, rval)) { - return JS_FALSE; - } - } else { - if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) - return JS_FALSE; - } - iterobj->slots[JSSLOT_ITER_STATE] = state; - if (JSVAL_IS_NULL(state)) - goto stop; - } else -#endif - { - restart: - if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) - return JS_TRUE; - - iterobj->slots[JSSLOT_ITER_STATE] = state; - if (JSVAL_IS_NULL(state)) { -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, obj)) { - /* - * We just finished enumerating an XML obj that is present on - * the prototype chain of a non-XML origobj. Stop further - * prototype chain searches because XML objects don't - * enumerate prototypes. - */ - JS_ASSERT(origobj != obj); - JS_ASSERT(!OBJECT_IS_XML(cx, origobj)); - } else -#endif - { - obj = OBJ_GET_PROTO(cx, obj); - if (obj) { - iterobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); - if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL)) - return JS_FALSE; - iterobj->slots[JSSLOT_ITER_STATE] = state; - if (!JSVAL_IS_NULL(state)) - goto restart; - } - } - goto stop; - } - - /* Skip properties not in obj when looking from origobj. */ - if (!OBJ_LOOKUP_PROPERTY(cx, origobj, id, &obj2, &prop)) - return JS_FALSE; - if (!prop) - goto restart; - OBJ_DROP_PROPERTY(cx, obj2, prop); - - /* - * If the id was found in a prototype object or an unrelated object - * (specifically, not in an inner object for obj), skip it. This step - * means that all OBJ_LOOKUP_PROPERTY implementations must return an - * object further along on the prototype chain, or else possibly an - * object returned by the JSExtendedClass.outerObject optional hook. - */ - if (obj != obj2) { - cond = JS_FALSE; - clasp = OBJ_GET_CLASS(cx, obj2); - if (clasp->flags & JSCLASS_IS_EXTENDED) { - xclasp = (JSExtendedClass *) clasp; - cond = xclasp->outerObject && - xclasp->outerObject(cx, obj2) == obj; - } - if (!cond) - goto restart; - } - - if (foreach) { - /* Get property querying the original object. */ - if (!OBJ_GET_PROPERTY(cx, origobj, id, rval)) - return JS_FALSE; - } - } - - if (foreach) { - if (flags & JSITER_KEYVALUE) { - if (!NewKeyValuePair(cx, id, *rval, rval)) - return JS_FALSE; - } - } else { - /* Make rval a string for uniformity and compatibility. */ - if (JSID_IS_ATOM(id)) { - *rval = ATOM_KEY(JSID_TO_ATOM(id)); - } -#if JS_HAS_XML_SUPPORT - else if (JSID_IS_OBJECT(id)) { - str = js_ValueToString(cx, OBJECT_JSID_TO_JSVAL(id)); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - } -#endif - else { - str = js_NumberToString(cx, (jsdouble)JSID_TO_INT(id)); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - } - } - return JS_TRUE; - - stop: - JS_ASSERT(iterobj->slots[JSSLOT_ITER_STATE] == JSVAL_NULL); - *rval = JSVAL_HOLE; - return JS_TRUE; -} - -JSBool -js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval) -{ - uintN flags; - - /* Fast path for native iterators */ - if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass) { - flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS)); - if (flags & JSITER_ENUMERATE) - return CallEnumeratorNext(cx, iterobj, flags, rval); - - /* - * Call next directly as all the methods of the native iterator are - * read-only and permanent. - */ - if (!IteratorNextImpl(cx, iterobj, rval)) - return JS_FALSE; - } else { - jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom); - - if (!JS_GetMethodById(cx, iterobj, id, &iterobj, rval)) - return JS_FALSE; - if (!js_InternalCall(cx, iterobj, *rval, 0, NULL, rval)) { - /* Check for StopIteration. */ - if (!cx->throwing || - JSVAL_IS_PRIMITIVE(cx->exception) || - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(cx->exception)) - != &js_StopIterationClass) { - return JS_FALSE; - } - - /* Inline JS_ClearPendingException(cx). */ - cx->throwing = JS_FALSE; - cx->exception = JSVAL_VOID; - *rval = JSVAL_HOLE; - return JS_TRUE; - } - } - - return JS_TRUE; -} - -static JSBool -stopiter_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - *bp = !JSVAL_IS_PRIMITIVE(v) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_StopIterationClass; - return JS_TRUE; -} - -JSClass js_StopIterationClass = { - js_StopIteration_str, - JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration), - JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, JS_FinalizeStub, - NULL, NULL, - NULL, NULL, - NULL, stopiter_hasInstance, - NULL, NULL -}; - -#if JS_HAS_GENERATORS - -static void -generator_finalize(JSContext *cx, JSObject *obj) -{ - JSGenerator *gen; - - gen = (JSGenerator *) JS_GetPrivate(cx, obj); - if (gen) { - /* - * gen can be open on shutdown when close hooks are ignored or when - * the embedding cancels scheduled close hooks. - */ - JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_CLOSED || - gen->state == JSGEN_OPEN); - JS_free(cx, gen); - } -} - -static uint32 -generator_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSGenerator *gen; - - gen = (JSGenerator *) JS_GetPrivate(cx, obj); - if (gen) { - /* - * We must mark argv[-2], as js_MarkStackFrame will not. Note that - * js_MarkStackFrame will mark thisp (argv[-1]) and actual arguments, - * plus any missing formals and local GC roots. - */ - JS_ASSERT(!JSVAL_IS_PRIMITIVE(gen->frame.argv[-2])); - GC_MARK(cx, JSVAL_TO_GCTHING(gen->frame.argv[-2]), "generator"); - js_MarkStackFrame(cx, &gen->frame); - } - return 0; -} - -JSClass js_GeneratorClass = { - js_Generator_str, - JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS | - JSCLASS_HAS_CACHED_PROTO(JSProto_Generator), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, generator_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, generator_mark, NULL -}; - -/* - * Called from the JSOP_GENERATOR case in the interpreter, with fp referring - * to the frame by which the generator function was activated. Create a new - * JSGenerator object, which contains its own JSStackFrame that we populate - * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return - * from the activation in fp, so we can steal away fp->callobj and fp->argsobj - * if they are non-null. - */ -JSObject * -js_NewGenerator(JSContext *cx, JSStackFrame *fp) -{ - JSObject *obj; - uintN argc, nargs, nvars, depth, nslots; - JSGenerator *gen; - jsval *newsp; - - /* After the following return, failing control flow must goto bad. */ - obj = js_NewObject(cx, &js_GeneratorClass, NULL, NULL); - if (!obj) - return NULL; - - /* Load and compute stack slot counts. */ - argc = fp->argc; - nargs = JS_MAX(argc, fp->fun->nargs); - nvars = fp->nvars; - depth = fp->script->depth; - nslots = 2 + nargs + nvars + 2 * depth; - - /* Allocate obj's private data struct. */ - gen = (JSGenerator *) - JS_malloc(cx, sizeof(JSGenerator) + (nslots - 1) * sizeof(jsval)); - if (!gen) - goto bad; - - gen->obj = obj; - - /* Steal away objects reflecting fp and point them at gen->frame. */ - gen->frame.callobj = fp->callobj; - if (fp->callobj) { - JS_SetPrivate(cx, fp->callobj, &gen->frame); - fp->callobj = NULL; - } - gen->frame.argsobj = fp->argsobj; - if (fp->argsobj) { - JS_SetPrivate(cx, fp->argsobj, &gen->frame); - fp->argsobj = NULL; - } - - /* These two references can be shared with fp until it goes away. */ - gen->frame.varobj = fp->varobj; - gen->frame.thisp = fp->thisp; - - /* Copy call-invariant script and function references. */ - gen->frame.script = fp->script; - gen->frame.fun = fp->fun; - - /* Use newsp to carve space out of gen->stack. */ - newsp = gen->stack; - gen->arena.next = NULL; - gen->arena.base = (jsuword) newsp; - gen->arena.limit = gen->arena.avail = (jsuword) (newsp + nslots); - -#define COPY_STACK_ARRAY(vec,cnt,num) \ - JS_BEGIN_MACRO \ - gen->frame.cnt = cnt; \ - gen->frame.vec = newsp; \ - newsp += (num); \ - memcpy(gen->frame.vec, fp->vec, (num) * sizeof(jsval)); \ - JS_END_MACRO - - /* Copy argv, rval, and vars. */ - *newsp++ = fp->argv[-2]; - *newsp++ = fp->argv[-1]; - COPY_STACK_ARRAY(argv, argc, nargs); - gen->frame.rval = fp->rval; - COPY_STACK_ARRAY(vars, nvars, nvars); - -#undef COPY_STACK_ARRAY - - /* Initialize or copy virtual machine state. */ - gen->frame.down = NULL; - gen->frame.annotation = NULL; - gen->frame.scopeChain = fp->scopeChain; - gen->frame.pc = fp->pc; - - /* Allocate generating pc and operand stack space. */ - gen->frame.spbase = gen->frame.sp = newsp + depth; - - /* Copy remaining state (XXX sharp* and xml* should be local vars). */ - gen->frame.sharpDepth = 0; - gen->frame.sharpArray = NULL; - gen->frame.flags = fp->flags | JSFRAME_GENERATOR; - gen->frame.dormantNext = NULL; - gen->frame.xmlNamespace = NULL; - gen->frame.blockChain = NULL; - - /* Note that gen is newborn. */ - gen->state = JSGEN_NEWBORN; - - if (!JS_SetPrivate(cx, obj, gen)) { - JS_free(cx, gen); - goto bad; - } - - /* - * Register with GC to ensure that suspended finally blocks will be - * executed. - */ - js_RegisterGenerator(cx, gen); - return obj; - - bad: - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; -} - -typedef enum JSGeneratorOp { - JSGENOP_NEXT, - JSGENOP_SEND, - JSGENOP_THROW, - JSGENOP_CLOSE -} JSGeneratorOp; - -/* - * Start newborn or restart yielding generator and perform the requested - * operation inside its frame. - */ -static JSBool -SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, - JSGenerator *gen, jsval arg, jsval *rval) -{ - JSStackFrame *fp; - jsval junk; - JSArena *arena; - JSBool ok; - - JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN); - switch (op) { - case JSGENOP_NEXT: - case JSGENOP_SEND: - if (gen->state == JSGEN_OPEN) { - /* - * Store the argument to send as the result of the yield - * expression. - */ - gen->frame.sp[-1] = arg; - } - gen->state = JSGEN_RUNNING; - break; - - case JSGENOP_THROW: - JS_SetPendingException(cx, arg); - gen->state = JSGEN_RUNNING; - break; - - default: - JS_ASSERT(op == JSGENOP_CLOSE); - JS_SetPendingException(cx, JSVAL_ARETURN); - gen->state = JSGEN_CLOSING; - break; - } - - /* Extend the current stack pool with gen->arena. */ - arena = cx->stackPool.current; - JS_ASSERT(!arena->next); - JS_ASSERT(!gen->arena.next); - JS_ASSERT(cx->stackPool.current != &gen->arena); - cx->stackPool.current = arena->next = &gen->arena; - - /* Push gen->frame around the interpreter activation. */ - fp = cx->fp; - cx->fp = &gen->frame; - gen->frame.down = fp; - ok = js_Interpret(cx, gen->frame.pc, &junk); - cx->fp = fp; - gen->frame.down = NULL; - - /* Retract the stack pool and sanitize gen->arena. */ - JS_ASSERT(!gen->arena.next); - JS_ASSERT(arena->next == &gen->arena); - JS_ASSERT(cx->stackPool.current == &gen->arena); - cx->stackPool.current = arena; - arena->next = NULL; - - if (gen->frame.flags & JSFRAME_YIELDING) { - /* Yield cannot fail, throw or be called on closing. */ - JS_ASSERT(ok); - JS_ASSERT(!cx->throwing); - JS_ASSERT(gen->state == JSGEN_RUNNING); - JS_ASSERT(op != JSGENOP_CLOSE); - gen->frame.flags &= ~JSFRAME_YIELDING; - gen->state = JSGEN_OPEN; - *rval = gen->frame.rval; - return JS_TRUE; - } - - gen->state = JSGEN_CLOSED; - - if (ok) { - /* Returned, explicitly or by falling off the end. */ - if (op == JSGENOP_CLOSE) - return JS_TRUE; - return js_ThrowStopIteration(cx, obj); - } - - /* - * An error, silent termination by branch callback or an exception. - * Propagate the condition to the caller. - */ - return JS_FALSE; -} - -/* - * Execute gen's close hook after the GC detects that the object has become - * unreachable. - */ -JSBool -js_CloseGeneratorObject(JSContext *cx, JSGenerator *gen) -{ - /* We pass null as rval since SendToGenerator never uses it with CLOSE. */ - return SendToGenerator(cx, JSGENOP_CLOSE, gen->obj, gen, JSVAL_VOID, NULL); -} - -/* - * Common subroutine of generator_(next|send|throw|close) methods. - */ -static JSBool -generator_op(JSContext *cx, JSGeneratorOp op, - JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSGenerator *gen; - JSString *str; - jsval arg; - - if (!JS_InstanceOf(cx, obj, &js_GeneratorClass, argv)) - return JS_FALSE; - - gen = (JSGenerator *) JS_GetPrivate(cx, obj); - if (gen == NULL) { - /* This happens when obj is the generator prototype. See bug 352885. */ - goto closed_generator; - } - - switch (gen->state) { - case JSGEN_NEWBORN: - switch (op) { - case JSGENOP_NEXT: - case JSGENOP_THROW: - break; - - case JSGENOP_SEND: - if (!JSVAL_IS_VOID(argv[0])) { - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, - argv[0], NULL); - if (str) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_GENERATOR_SEND, - JSSTRING_CHARS(str)); - } - return JS_FALSE; - } - break; - - default: - JS_ASSERT(op == JSGENOP_CLOSE); - gen->state = JSGEN_CLOSED; - return JS_TRUE; - } - break; - - case JSGEN_OPEN: - break; - - case JSGEN_RUNNING: - case JSGEN_CLOSING: - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, argv[-1], - JS_GetFunctionId(gen->frame.fun)); - if (str) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_NESTING_GENERATOR, - JSSTRING_CHARS(str)); - } - return JS_FALSE; - - default: - JS_ASSERT(gen->state == JSGEN_CLOSED); - - closed_generator: - switch (op) { - case JSGENOP_NEXT: - case JSGENOP_SEND: - return js_ThrowStopIteration(cx, obj); - case JSGENOP_THROW: - JS_SetPendingException(cx, argv[0]); - return JS_FALSE; - default: - JS_ASSERT(op == JSGENOP_CLOSE); - return JS_TRUE; - } - } - - arg = (op == JSGENOP_SEND || op == JSGENOP_THROW) - ? argv[0] - : JSVAL_VOID; - if (!SendToGenerator(cx, op, obj, gen, arg, rval)) - return JS_FALSE; - return JS_TRUE; -} - -static JSBool -generator_send(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return generator_op(cx, JSGENOP_SEND, obj, argc, argv, rval); -} - -static JSBool -generator_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return generator_op(cx, JSGENOP_NEXT, obj, argc, argv, rval); -} - -static JSBool -generator_throw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return generator_op(cx, JSGENOP_THROW, obj, argc, argv, rval); -} - -static JSBool -generator_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return generator_op(cx, JSGENOP_CLOSE, obj, argc, argv, rval); -} - -static JSFunctionSpec generator_methods[] = { - {js_iterator_str, iterator_self, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_next_str, generator_next, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_send_str, generator_send, 1,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_throw_str, generator_throw, 1,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_close_str, generator_close, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {0,0,0,0,0} -}; - -#endif /* JS_HAS_GENERATORS */ - -JSObject * -js_InitIteratorClasses(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *stop; - - /* Idempotency required: we initialize several things, possibly lazily. */ - if (!js_GetClassObject(cx, obj, JSProto_StopIteration, &stop)) - return NULL; - if (stop) - return stop; - - proto = JS_InitClass(cx, obj, NULL, &js_IteratorClass, Iterator, 2, - NULL, iterator_methods, NULL, NULL); - if (!proto) - return NULL; - proto->slots[JSSLOT_ITER_STATE] = JSVAL_NULL; - -#if JS_HAS_GENERATORS - /* Initialize the generator internals if configured. */ - if (!JS_InitClass(cx, obj, NULL, &js_GeneratorClass, NULL, 0, - NULL, generator_methods, NULL, NULL)) { - return NULL; - } -#endif - - return JS_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0, - NULL, NULL, NULL, NULL); -} |