2009-06-10 18:29:44 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
2007-03-22 10:30:00 -07:00
|
|
|
* 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 <string.h> /* for memcpy */
|
|
|
|
#include "jstypes.h"
|
2009-03-18 11:38:16 -07:00
|
|
|
#include "jsstdint.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jsutil.h"
|
|
|
|
#include "jsarena.h"
|
|
|
|
#include "jsapi.h"
|
|
|
|
#include "jsarray.h"
|
|
|
|
#include "jsatom.h"
|
|
|
|
#include "jsbool.h"
|
2009-05-05 17:49:29 -07:00
|
|
|
#include "jsbuiltins.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jscntxt.h"
|
2008-09-05 10:19:17 -07:00
|
|
|
#include "jsversion.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#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"
|
2007-07-05 13:37:47 -07:00
|
|
|
#include "jsscan.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jsscope.h"
|
|
|
|
#include "jsscript.h"
|
2009-01-30 15:40:05 -08:00
|
|
|
#include "jsstaticcheck.h"
|
|
|
|
#include "jstracer.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#if JS_HAS_XML_SUPPORT
|
|
|
|
#include "jsxml.h"
|
|
|
|
#endif
|
|
|
|
|
2009-08-04 14:06:55 -07:00
|
|
|
JS_STATIC_ASSERT(JSSLOT_ITER_FLAGS < JS_INITIAL_NSLOTS);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-07-13 00:28:47 -07:00
|
|
|
#if JS_HAS_GENERATORS
|
|
|
|
|
2009-01-30 15:40:05 -08:00
|
|
|
static JS_REQUIRES_STACK JSBool
|
2007-07-13 00:28:47 -07:00
|
|
|
CloseGenerator(JSContext *cx, JSObject *genobj);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
/*
|
|
|
|
* Shared code to close iterator's state either through an explicit call or
|
|
|
|
* when GC detects that the iterator is no longer reachable.
|
|
|
|
*/
|
|
|
|
void
|
2007-07-02 05:13:23 -07:00
|
|
|
js_CloseNativeIterator(JSContext *cx, JSObject *iterobj)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
jsval state;
|
|
|
|
JSObject *iterable;
|
|
|
|
|
2007-07-02 05:13:23 -07:00
|
|
|
JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/* Avoid double work if js_CloseNativeIterator was called on obj. */
|
|
|
|
state = STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE);
|
|
|
|
if (JSVAL_IS_NULL(state))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Protect against failure to fully initialize obj. */
|
2009-09-19 02:40:43 -07:00
|
|
|
iterable = iterobj->getParent();
|
2007-03-22 10:30:00 -07:00
|
|
|
if (iterable) {
|
|
|
|
#if JS_HAS_XML_SUPPORT
|
|
|
|
uintN flags = JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS));
|
|
|
|
if ((flags & JSITER_FOREACH) && OBJECT_IS_XML(cx, iterable)) {
|
2009-03-14 09:41:47 -07:00
|
|
|
js_EnumerateXMLValues(cx, iterable, JSENUMERATE_DESTROY, &state,
|
|
|
|
NULL, NULL);
|
2007-03-22 10:30:00 -07:00
|
|
|
} else
|
|
|
|
#endif
|
2009-08-11 13:05:44 -07:00
|
|
|
iterable->enumerate(cx, JSENUMERATE_DESTROY, &state, NULL);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, JSVAL_NULL);
|
|
|
|
}
|
|
|
|
|
2009-09-19 02:40:43 -07:00
|
|
|
static void
|
|
|
|
iterator_trace(JSTracer *trc, JSObject *obj)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* The GC marks iter_state during the normal slot scanning if
|
|
|
|
* JSVAL_IS_TRACEABLE(iter_state) is true duplicating the efforts of
|
|
|
|
* js_MarkEnumeratorState. But this is rare so we optimize for code
|
|
|
|
* simplicity.
|
|
|
|
*/
|
|
|
|
JSObject *iterable = obj->getParent();
|
2009-09-21 01:49:12 -07:00
|
|
|
if (!iterable) {
|
|
|
|
/* for (x in null) creates an iterator object with a null parent. */
|
|
|
|
return;
|
|
|
|
}
|
2009-09-19 02:40:43 -07:00
|
|
|
jsval iter_state = obj->fslots[JSSLOT_ITER_STATE];
|
|
|
|
js_MarkEnumeratorState(trc, iterable, iter_state);
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
JSClass js_IteratorClass = {
|
|
|
|
"Iterator",
|
|
|
|
JSCLASS_HAS_RESERVED_SLOTS(2) | /* slots for state and flags */
|
2009-09-19 02:40:43 -07:00
|
|
|
JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
|
|
|
|
JSCLASS_MARK_IS_TRACE,
|
2007-03-22 10:30:00 -07:00
|
|
|
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
2009-07-22 09:23:21 -07:00
|
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
|
2009-09-19 02:40:43 -07:00
|
|
|
NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, JS_CLASS_TRACE(iterator_trace), NULL
|
2007-03-22 10:30:00 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
InitNativeIterator(JSContext *cx, JSObject *iterobj, JSObject *obj, uintN flags)
|
|
|
|
{
|
|
|
|
jsval state;
|
|
|
|
JSBool ok;
|
|
|
|
|
|
|
|
JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass);
|
|
|
|
|
|
|
|
/* Initialize iterobj in case of enumerate hook failure. */
|
|
|
|
STOBJ_SET_PARENT(iterobj, obj);
|
|
|
|
STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, JSVAL_NULL);
|
|
|
|
STOBJ_SET_SLOT(iterobj, 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))
|
2009-03-14 09:41:47 -07:00
|
|
|
? js_EnumerateXMLValues(cx, obj, JSENUMERATE_INIT, &state, NULL, NULL)
|
2007-03-22 10:30:00 -07:00
|
|
|
:
|
|
|
|
#endif
|
2009-08-11 13:05:44 -07:00
|
|
|
obj->enumerate(cx, JSENUMERATE_INIT, &state, NULL);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!ok)
|
|
|
|
return JS_FALSE;
|
|
|
|
|
|
|
|
STOBJ_SET_SLOT(iterobj, 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);
|
|
|
|
STOBJ_SET_PROTO(iterobj, obj);
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
Iterator(JSContext *cx, JSObject *iterobj, uintN argc, jsval *argv, jsval *rval)
|
|
|
|
{
|
|
|
|
JSBool keyonly;
|
|
|
|
uintN flags;
|
|
|
|
JSObject *obj;
|
|
|
|
|
2008-01-19 16:56:08 -08:00
|
|
|
keyonly = js_ValueToBoolean(argv[1]);
|
2007-03-22 10:30:00 -07:00
|
|
|
flags = keyonly ? 0 : JSITER_FOREACH;
|
|
|
|
|
2008-12-09 08:38:32 -08:00
|
|
|
if (JS_IsConstructing(cx)) {
|
2007-03-22 10:30:00 -07:00
|
|
|
/* 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);
|
2008-07-30 16:32:33 -07:00
|
|
|
state = STOBJ_GET_SLOT(obj, JSSLOT_ITER_STATE);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (JSVAL_IS_NULL(state))
|
|
|
|
goto stop;
|
|
|
|
|
2008-07-30 16:32:33 -07:00
|
|
|
flags = JSVAL_TO_INT(STOBJ_GET_SLOT(obj, JSSLOT_ITER_FLAGS));
|
2007-03-22 10:30:00 -07:00
|
|
|
JS_ASSERT(!(flags & JSITER_ENUMERATE));
|
|
|
|
foreach = (flags & JSITER_FOREACH) != 0;
|
|
|
|
ok =
|
|
|
|
#if JS_HAS_XML_SUPPORT
|
|
|
|
(foreach && OBJECT_IS_XML(cx, iterable))
|
2009-03-14 09:41:47 -07:00
|
|
|
? js_EnumerateXMLValues(cx, iterable, JSENUMERATE_NEXT, &state,
|
|
|
|
&id, rval)
|
2007-03-22 10:30:00 -07:00
|
|
|
:
|
|
|
|
#endif
|
2009-08-11 13:05:44 -07:00
|
|
|
iterable->enumerate(cx, JSENUMERATE_NEXT, &state, &id);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!ok)
|
|
|
|
return JS_FALSE;
|
|
|
|
|
2008-02-07 15:18:45 -08:00
|
|
|
STOBJ_SET_SLOT(obj, JSSLOT_ITER_STATE, state);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (JSVAL_IS_NULL(state))
|
|
|
|
goto stop;
|
|
|
|
|
|
|
|
if (foreach) {
|
|
|
|
#if JS_HAS_XML_SUPPORT
|
|
|
|
if (!OBJECT_IS_XML(cx, iterable) &&
|
2009-08-11 13:05:44 -07:00
|
|
|
!iterable->getProperty(cx, id, rval)) {
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (!NewKeyValuePair(cx, id, *rval, rval))
|
|
|
|
return JS_FALSE;
|
|
|
|
} else {
|
|
|
|
*rval = ID_TO_VALUE(id);
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
stop:
|
2008-07-30 16:32:33 -07:00
|
|
|
JS_ASSERT(STOBJ_GET_SLOT(obj, JSSLOT_ITER_STATE) == JSVAL_NULL);
|
2007-03-22 10:30:00 -07:00
|
|
|
*rval = JSVAL_HOLE;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2007-09-30 17:31:22 -07:00
|
|
|
JSBool
|
|
|
|
js_ThrowStopIteration(JSContext *cx)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
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
|
2007-08-01 21:33:52 -07:00
|
|
|
iterator_next(JSContext *cx, uintN argc, jsval *vp)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-08-01 21:33:52 -07:00
|
|
|
JSObject *obj;
|
|
|
|
|
2008-02-17 16:12:33 -08:00
|
|
|
obj = JS_THIS_OBJECT(cx, vp);
|
2007-08-01 21:33:52 -07:00
|
|
|
if (!JS_InstanceOf(cx, obj, &js_IteratorClass, vp + 2))
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_FALSE;
|
|
|
|
|
2007-08-01 21:33:52 -07:00
|
|
|
if (!IteratorNextImpl(cx, obj, vp))
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_FALSE;
|
|
|
|
|
2007-08-01 21:33:52 -07:00
|
|
|
if (*vp == JSVAL_HOLE) {
|
|
|
|
*vp = JSVAL_NULL;
|
2007-09-30 17:31:22 -07:00
|
|
|
js_ThrowStopIteration(cx);
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
2007-08-01 21:33:52 -07:00
|
|
|
iterator_self(JSContext *cx, uintN argc, jsval *vp)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-02-17 16:12:33 -08:00
|
|
|
*vp = JS_THIS(cx, vp);
|
|
|
|
return !JSVAL_IS_NULL(*vp);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-08-01 21:33:52 -07:00
|
|
|
#define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
static JSFunctionSpec iterator_methods[] = {
|
2008-08-08 09:02:50 -07:00
|
|
|
JS_FN(js_iterator_str, iterator_self, 0,JSPROP_ROPERM),
|
|
|
|
JS_FN(js_next_str, iterator_next, 0,JSPROP_ROPERM),
|
2007-08-01 21:33:52 -07:00
|
|
|
JS_FS_END
|
2007-03-22 10:30:00 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
uintN
|
|
|
|
js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj)
|
|
|
|
{
|
|
|
|
if (OBJ_GET_CLASS(cx, iterobj) != &js_IteratorClass)
|
|
|
|
return 0;
|
2008-07-30 16:32:33 -07:00
|
|
|
return JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS));
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
|
2008-02-18 13:01:47 -08:00
|
|
|
* Otherwise construct the default iterator.
|
2007-03-22 10:30:00 -07:00
|
|
|
*/
|
2007-12-27 13:34:03 -08:00
|
|
|
JS_FRIEND_API(JSBool)
|
2007-03-22 10:30:00 -07:00
|
|
|
js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp)
|
|
|
|
{
|
|
|
|
JSObject *obj;
|
|
|
|
JSTempValueRooter tvr;
|
|
|
|
JSAtom *atom;
|
2007-09-15 09:58:45 -07:00
|
|
|
JSClass *clasp;
|
|
|
|
JSExtendedClass *xclasp;
|
2007-03-22 10:30:00 -07:00
|
|
|
JSBool ok;
|
|
|
|
JSObject *iterobj;
|
|
|
|
jsval arg;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2007-09-15 09:58:45 -07:00
|
|
|
clasp = OBJ_GET_CLASS(cx, obj);
|
|
|
|
if ((clasp->flags & JSCLASS_IS_EXTENDED) &&
|
|
|
|
(xclasp = (JSExtendedClass *) clasp)->iteratorObject) {
|
|
|
|
iterobj = xclasp->iteratorObject(cx, obj, !(flags & JSITER_FOREACH));
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!iterobj)
|
|
|
|
goto bad;
|
|
|
|
*vp = OBJECT_TO_JSVAL(iterobj);
|
|
|
|
} else {
|
2007-09-15 09:58:45 -07:00
|
|
|
atom = cx->runtime->atomState.iteratorAtom;
|
2009-09-03 14:41:19 -07:00
|
|
|
if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, vp))
|
2009-03-14 09:41:47 -07:00
|
|
|
goto bad;
|
2007-09-15 09:58:45 -07:00
|
|
|
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.
|
|
|
|
*/
|
2009-07-14 17:06:09 -07:00
|
|
|
iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL);
|
2007-09-15 09:58:45 -07:00
|
|
|
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 {
|
2009-01-30 15:40:05 -08:00
|
|
|
js_LeaveTrace(cx);
|
2007-09-15 09:58:45 -07:00
|
|
|
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)) {
|
2009-02-07 11:28:23 -08:00
|
|
|
js_ReportValueError(cx, JSMSG_BAD_ITERATOR_RETURN,
|
|
|
|
JSDVG_SEARCH_STACK, *vp, NULL);
|
2007-09-15 09:58:45 -07:00
|
|
|
goto bad;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ok = JS_TRUE;
|
|
|
|
out:
|
|
|
|
if (obj)
|
|
|
|
JS_POP_TEMP_ROOT(cx, &tvr);
|
|
|
|
return ok;
|
|
|
|
bad:
|
|
|
|
ok = JS_FALSE;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2008-10-08 15:08:33 -07:00
|
|
|
JS_FRIEND_API(JSBool) JS_FASTCALL
|
2007-07-02 05:13:23 -07:00
|
|
|
js_CloseIterator(JSContext *cx, jsval v)
|
|
|
|
{
|
|
|
|
JSObject *obj;
|
|
|
|
JSClass *clasp;
|
|
|
|
|
|
|
|
JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
|
|
|
|
obj = JSVAL_TO_OBJECT(v);
|
|
|
|
clasp = OBJ_GET_CLASS(cx, obj);
|
|
|
|
|
|
|
|
if (clasp == &js_IteratorClass) {
|
|
|
|
js_CloseNativeIterator(cx, obj);
|
|
|
|
}
|
|
|
|
#if JS_HAS_GENERATORS
|
|
|
|
else if (clasp == &js_GeneratorClass) {
|
2009-01-30 15:40:05 -08:00
|
|
|
JS_ASSERT_NOT_ON_TRACE(cx);
|
2007-07-13 00:28:47 -07:00
|
|
|
if (!CloseGenerator(cx, obj))
|
2007-07-02 05:13:23 -07:00
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
2009-05-05 17:36:26 -07:00
|
|
|
JS_DEFINE_CALLINFO_2(FRIEND, BOOL, js_CloseIterator, CONTEXT, JSVAL, 0, 0)
|
2007-07-02 05:13:23 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
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(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass);
|
|
|
|
|
|
|
|
obj = STOBJ_GET_PARENT(iterobj);
|
|
|
|
origobj = STOBJ_GET_PROTO(iterobj);
|
|
|
|
state = STOBJ_GET_SLOT(iterobj, 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) {
|
2009-03-14 09:41:47 -07:00
|
|
|
if (!js_EnumerateXMLValues(cx, obj, JSENUMERATE_NEXT, &state,
|
|
|
|
&id, rval)) {
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
} else {
|
2009-08-11 13:05:44 -07:00
|
|
|
if (!obj->enumerate(cx, JSENUMERATE_NEXT, &state, &id))
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state);
|
|
|
|
if (JSVAL_IS_NULL(state))
|
|
|
|
goto stop;
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
restart:
|
2009-08-11 13:05:44 -07:00
|
|
|
if (!obj->enumerate(cx, JSENUMERATE_NEXT, &state, &id))
|
2008-02-07 12:09:48 -08:00
|
|
|
return JS_FALSE;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
STOBJ_SET_SLOT(iterobj, 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) {
|
|
|
|
STOBJ_SET_PARENT(iterobj, obj);
|
2009-08-11 13:05:44 -07:00
|
|
|
if (!obj->enumerate(cx, JSENUMERATE_INIT, &state, NULL))
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_FALSE;
|
|
|
|
STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state);
|
|
|
|
if (!JSVAL_IS_NULL(state))
|
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
goto stop;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip properties not in obj when looking from origobj. */
|
2009-08-11 13:05:44 -07:00
|
|
|
if (!origobj->lookupProperty(cx, id, &obj2, &prop))
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_FALSE;
|
|
|
|
if (!prop)
|
|
|
|
goto restart;
|
2009-08-11 13:05:44 -07:00
|
|
|
obj2->dropProperty(cx, prop);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
2009-08-11 13:05:44 -07:00
|
|
|
* means that all lookupProperty implementations must return an
|
2007-03-22 10:30:00 -07:00
|
|
|
* 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. */
|
2009-08-11 13:05:44 -07:00
|
|
|
if (!origobj->getProperty(cx, id, rval))
|
2007-03-22 10:30:00 -07:00
|
|
|
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. */
|
2007-08-11 13:25:16 -07:00
|
|
|
str = js_ValueToString(cx, ID_TO_VALUE(id));
|
|
|
|
if (!str)
|
|
|
|
return JS_FALSE;
|
|
|
|
*rval = STRING_TO_JSVAL(str);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
stop:
|
|
|
|
JS_ASSERT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE) == JSVAL_NULL);
|
|
|
|
*rval = JSVAL_HOLE;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2007-12-27 13:34:03 -08:00
|
|
|
JS_FRIEND_API(JSBool)
|
2007-03-22 10:30:00 -07:00
|
|
|
js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval)
|
|
|
|
{
|
|
|
|
uintN flags;
|
|
|
|
|
|
|
|
/* Fast path for native iterators */
|
|
|
|
if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass) {
|
2008-07-30 16:32:33 -07:00
|
|
|
flags = JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS));
|
2007-03-22 10:30:00 -07:00
|
|
|
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. */
|
2008-11-13 00:30:20 -08:00
|
|
|
if (!cx->throwing || !js_ValueIsStopIteration(cx->exception))
|
2007-03-22 10:30:00 -07:00
|
|
|
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)
|
|
|
|
{
|
2008-11-13 00:30:20 -08:00
|
|
|
*bp = js_ValueIsStopIteration(v);
|
2007-03-22 10:30:00 -07:00
|
|
|
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,
|
2009-07-22 09:23:21 -07:00
|
|
|
JS_ConvertStub, NULL,
|
2007-03-22 10:30:00 -07:00
|
|
|
NULL, NULL,
|
|
|
|
NULL, NULL,
|
|
|
|
NULL, stopiter_hasInstance,
|
|
|
|
NULL, NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
#if JS_HAS_GENERATORS
|
|
|
|
|
|
|
|
static void
|
|
|
|
generator_finalize(JSContext *cx, JSObject *obj)
|
|
|
|
{
|
2009-08-04 14:06:55 -07:00
|
|
|
JSGenerator *gen = (JSGenerator *) obj->getPrivate();
|
|
|
|
if (!gen)
|
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-08-04 14:06:55 -07:00
|
|
|
/*
|
|
|
|
* gen is open when a script has not called its close method while
|
|
|
|
* explicitly manipulating it.
|
|
|
|
*/
|
|
|
|
JS_ASSERT(gen->state == JSGEN_NEWBORN ||
|
|
|
|
gen->state == JSGEN_CLOSED ||
|
|
|
|
gen->state == JSGEN_OPEN);
|
|
|
|
cx->free(gen);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-04-16 23:53:37 -07:00
|
|
|
static void
|
|
|
|
generator_trace(JSTracer *trc, JSObject *obj)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2009-08-04 14:06:55 -07:00
|
|
|
JSGenerator *gen = (JSGenerator *) obj->getPrivate();
|
2007-08-04 21:54:34 -07:00
|
|
|
if (!gen)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* js_TraceStackFrame does not recursively trace the down-linked frame
|
|
|
|
* chain, so we insist that gen->frame has no parent to trace when the
|
|
|
|
* generator is not running.
|
|
|
|
*/
|
|
|
|
JS_ASSERT_IF(gen->state != JSGEN_RUNNING && gen->state != JSGEN_CLOSING,
|
|
|
|
!gen->frame.down);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FIXME be 390950. Generator's frame is a part of the JS stack when the
|
|
|
|
* generator is running or closing. Thus tracing the frame in this case
|
|
|
|
* here duplicates the work done in js_TraceContext.
|
|
|
|
*/
|
|
|
|
js_TraceStackFrame(trc, &gen->frame);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-04-05 21:17:22 -07:00
|
|
|
JS_FRIEND_DATA(JSClass) js_GeneratorClass = {
|
2007-03-22 10:30:00 -07:00
|
|
|
js_Generator_str,
|
|
|
|
JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS |
|
2007-04-16 23:53:37 -07:00
|
|
|
JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator),
|
2007-03-22 10:30:00 -07:00
|
|
|
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
|
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, generator_finalize,
|
|
|
|
NULL, NULL, NULL, NULL,
|
2007-04-16 23:53:37 -07:00
|
|
|
NULL, NULL, JS_CLASS_TRACE(generator_trace), NULL
|
2007-03-22 10:30:00 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2008-11-13 00:30:20 -08:00
|
|
|
uintN argc, nargs, nslots;
|
2007-03-22 10:30:00 -07:00
|
|
|
JSGenerator *gen;
|
2008-07-20 13:13:17 -07:00
|
|
|
jsval *slots;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-07-14 17:06:09 -07:00
|
|
|
obj = js_NewObject(cx, &js_GeneratorClass, NULL, NULL);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!obj)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Load and compute stack slot counts. */
|
|
|
|
argc = fp->argc;
|
2008-03-29 03:34:29 -07:00
|
|
|
nargs = JS_MAX(argc, fp->fun->nargs);
|
2008-07-20 13:13:17 -07:00
|
|
|
nslots = 2 + nargs + fp->script->nslots;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/* Allocate obj's private data struct. */
|
|
|
|
gen = (JSGenerator *)
|
2009-07-27 21:10:12 -07:00
|
|
|
cx->malloc(sizeof(JSGenerator) + (nslots - 1) * sizeof(jsval));
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!gen)
|
2009-08-04 14:06:55 -07:00
|
|
|
return NULL;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-07-16 13:57:16 -07:00
|
|
|
gen->obj = obj;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
/* Steal away objects reflecting fp and point them at gen->frame. */
|
|
|
|
gen->frame.callobj = fp->callobj;
|
|
|
|
if (fp->callobj) {
|
2009-08-04 14:06:55 -07:00
|
|
|
fp->callobj->setPrivate(&gen->frame);
|
2007-03-22 10:30:00 -07:00
|
|
|
fp->callobj = NULL;
|
|
|
|
}
|
|
|
|
gen->frame.argsobj = fp->argsobj;
|
|
|
|
if (fp->argsobj) {
|
2009-08-04 14:06:55 -07:00
|
|
|
JSVAL_TO_OBJECT(fp->argsobj)->setPrivate(&gen->frame);
|
2007-03-22 10:30:00 -07:00
|
|
|
fp->argsobj = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* These two references can be shared with fp until it goes away. */
|
|
|
|
gen->frame.varobj = fp->varobj;
|
2009-09-11 19:07:00 -07:00
|
|
|
gen->frame.thisv = fp->thisv;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/* Copy call-invariant script and function references. */
|
|
|
|
gen->frame.script = fp->script;
|
|
|
|
gen->frame.fun = fp->fun;
|
|
|
|
|
2008-07-20 13:13:17 -07:00
|
|
|
/* Use slots to carve space out of gen->slots. */
|
|
|
|
slots = gen->slots;
|
2007-03-22 10:30:00 -07:00
|
|
|
gen->arena.next = NULL;
|
2008-07-20 13:13:17 -07:00
|
|
|
gen->arena.base = (jsuword) slots;
|
|
|
|
gen->arena.limit = gen->arena.avail = (jsuword) (slots + nslots);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-20 13:13:17 -07:00
|
|
|
/* Copy rval, argv and vars. */
|
|
|
|
gen->frame.rval = fp->rval;
|
|
|
|
memcpy(slots, fp->argv - 2, (2 + nargs) * sizeof(jsval));
|
|
|
|
gen->frame.argc = nargs;
|
|
|
|
gen->frame.argv = slots + 2;
|
|
|
|
slots += 2 + nargs;
|
|
|
|
memcpy(slots, fp->slots, fp->script->nfixed * sizeof(jsval));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/* Initialize or copy virtual machine state. */
|
|
|
|
gen->frame.down = NULL;
|
|
|
|
gen->frame.annotation = NULL;
|
|
|
|
gen->frame.scopeChain = fp->scopeChain;
|
|
|
|
|
2008-11-13 00:30:20 -08:00
|
|
|
gen->frame.imacpc = NULL;
|
2008-07-20 13:13:17 -07:00
|
|
|
gen->frame.slots = slots;
|
|
|
|
JS_ASSERT(StackBase(fp) == fp->regs->sp);
|
|
|
|
gen->savedRegs.sp = slots + fp->script->nfixed;
|
2008-03-17 01:58:28 -07:00
|
|
|
gen->savedRegs.pc = fp->regs->pc;
|
|
|
|
gen->frame.regs = &gen->savedRegs;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-09-18 00:34:54 -07:00
|
|
|
gen->frame.flags = (fp->flags & ~JSFRAME_ROOTED_ARGV) | JSFRAME_GENERATOR;
|
2007-03-22 10:30:00 -07:00
|
|
|
gen->frame.dormantNext = NULL;
|
2009-09-09 20:21:15 -07:00
|
|
|
|
Bug 480132: Clone lexical blocks only when needed. r=igor
Terminology:
A "script block" is an object of class Block allocated by the byte
compiler and associated with a script. Script blocks are never
modified, and may be used as a prototype for a "closure block":
A "closure block" is an object of class Block that holds variables
that have been closed over (although we actually leave the variables
on the stack until we leave their dynamic scope). A closure block is
a clone of a script block (its prototype is a script block).
Adjust the meanings of fp->blockChain and fp->scopeChain:
fp->blockChain is always the innermost script block in whose static
scope we're executing.
fp->scopeChain is the current scope chain, including 'call' objects
and closure blocks for those function calls and blocks in whose
static scope we are currently executing, and 'with' objects for with
statements; the chain is typically terminated by a global object.
However, as an optimization, the young end of the chain omits block
objects we have not yet needed to clone.
Closures need fully reified scope chains, so have js_GetScopeChain
reify any closure blocks missing from the young end of fp->scopeChain
by cloning script blocks as needed from fp->blockChain. Thus, if we
never actually close over a particular block, we never place a closure
block for it on fp->scopeChain.
Have JSOP_ENTERBLOCK and JSOP_LEAVEBLOCK always keep fp->blockChain
current. When JSOP_LEAVEBLOCK pops a block from fp->blockChain that
has been cloned on fp->scopeChain, pop fp->scopeChain as well.
Remove the JSFRAME_POP_BLOCKS flag, as it is no longer needed.
Ensure that the JIT won't have to create closure blocks or call
js_PutBlockObject; it can't handle those things yet. Note our current
script block when we begin recording. Abort recording if we leave
that block; we can't tell in advance whether it will need to be "put"
in future trace invocations. Leave trace if we call js_GetScopeChain
while in the static scope of lexical blocks. Remove JIT tests based
on JSFRAME_POP_BLOCKS.
Verify that generators capture the correct value for blockChain.
Add a constructor to JSAutoTempValueRooter for rooting JSObject
pointers.
2009-03-16 09:55:06 -07:00
|
|
|
/* JSOP_GENERATOR appears in the prologue, outside all blocks. */
|
|
|
|
JS_ASSERT(!fp->blockChain);
|
2007-03-22 10:30:00 -07:00
|
|
|
gen->frame.blockChain = NULL;
|
|
|
|
|
|
|
|
/* Note that gen is newborn. */
|
|
|
|
gen->state = JSGEN_NEWBORN;
|
|
|
|
|
2009-08-04 14:06:55 -07:00
|
|
|
obj->setPrivate(gen);
|
2007-03-22 10:30:00 -07:00
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
2009-01-30 15:40:05 -08:00
|
|
|
static JS_REQUIRES_STACK JSBool
|
2007-03-22 10:30:00 -07:00
|
|
|
SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
|
2008-03-06 14:47:46 -08:00
|
|
|
JSGenerator *gen, jsval arg)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
JSStackFrame *fp;
|
|
|
|
JSArena *arena;
|
|
|
|
JSBool ok;
|
|
|
|
|
2007-07-16 23:41:20 -07:00
|
|
|
if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
|
|
|
|
js_ReportValueError(cx, JSMSG_NESTING_GENERATOR,
|
|
|
|
JSDVG_SEARCH_STACK, OBJECT_TO_JSVAL(obj),
|
|
|
|
JS_GetFunctionId(gen->frame.fun));
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
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.
|
|
|
|
*/
|
2008-03-17 01:58:28 -07:00
|
|
|
gen->savedRegs.sp[-1] = arg;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
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. */
|
2008-12-09 08:38:32 -08:00
|
|
|
fp = js_GetTopStackFrame(cx);
|
2007-03-22 10:30:00 -07:00
|
|
|
cx->fp = &gen->frame;
|
|
|
|
gen->frame.down = fp;
|
2008-03-06 14:47:46 -08:00
|
|
|
ok = js_Interpret(cx);
|
2007-03-22 10:30:00 -07:00
|
|
|
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;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2008-03-06 14:47:46 -08:00
|
|
|
gen->frame.rval = JSVAL_VOID;
|
2007-03-22 10:30:00 -07:00
|
|
|
gen->state = JSGEN_CLOSED;
|
|
|
|
if (ok) {
|
|
|
|
/* Returned, explicitly or by falling off the end. */
|
2008-03-06 14:47:46 -08:00
|
|
|
if (op == JSGENOP_CLOSE)
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_TRUE;
|
2007-09-30 17:31:22 -07:00
|
|
|
return js_ThrowStopIteration(cx);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-01-03 01:28:40 -08:00
|
|
|
* An error, silent termination by operation callback or an exception.
|
2007-03-22 10:30:00 -07:00
|
|
|
* Propagate the condition to the caller.
|
|
|
|
*/
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
|
2009-01-30 15:40:05 -08:00
|
|
|
static JS_REQUIRES_STACK JSBool
|
2007-07-13 00:28:47 -07:00
|
|
|
CloseGenerator(JSContext *cx, JSObject *obj)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-07-02 05:13:23 -07:00
|
|
|
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_GeneratorClass);
|
2009-08-04 14:06:55 -07:00
|
|
|
|
|
|
|
JSGenerator *gen = (JSGenerator *) obj->getPrivate();
|
2007-07-02 05:13:23 -07:00
|
|
|
if (!gen) {
|
|
|
|
/* Generator prototype object. */
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gen->state == JSGEN_CLOSED)
|
|
|
|
return JS_TRUE;
|
|
|
|
|
2008-03-06 14:47:46 -08:00
|
|
|
return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, JSVAL_VOID);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Common subroutine of generator_(next|send|throw|close) methods.
|
|
|
|
*/
|
|
|
|
static JSBool
|
2008-08-08 09:02:50 -07:00
|
|
|
generator_op(JSContext *cx, JSGeneratorOp op, jsval *vp, uintN argc)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-08-01 21:33:52 -07:00
|
|
|
JSObject *obj;
|
2007-03-22 10:30:00 -07:00
|
|
|
jsval arg;
|
|
|
|
|
2009-01-30 15:40:05 -08:00
|
|
|
js_LeaveTrace(cx);
|
|
|
|
|
2008-02-17 16:12:33 -08:00
|
|
|
obj = JS_THIS_OBJECT(cx, vp);
|
2007-08-01 21:33:52 -07:00
|
|
|
if (!JS_InstanceOf(cx, obj, &js_GeneratorClass, vp + 2))
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_FALSE;
|
|
|
|
|
2009-08-04 14:06:55 -07:00
|
|
|
JSGenerator *gen = (JSGenerator *) obj->getPrivate();
|
|
|
|
if (!gen) {
|
2007-03-22 10:30:00 -07:00
|
|
|
/* This happens when obj is the generator prototype. See bug 352885. */
|
|
|
|
goto closed_generator;
|
|
|
|
}
|
|
|
|
|
2007-07-16 23:41:20 -07:00
|
|
|
if (gen->state == JSGEN_NEWBORN) {
|
2007-03-22 10:30:00 -07:00
|
|
|
switch (op) {
|
|
|
|
case JSGENOP_NEXT:
|
|
|
|
case JSGENOP_THROW:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JSGENOP_SEND:
|
2008-08-08 09:02:50 -07:00
|
|
|
if (argc >= 1 && !JSVAL_IS_VOID(vp[2])) {
|
2007-03-22 10:30:00 -07:00
|
|
|
js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
|
2007-08-01 21:33:52 -07:00
|
|
|
JSDVG_SEARCH_STACK, vp[2], NULL);
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
JS_ASSERT(op == JSGENOP_CLOSE);
|
|
|
|
gen->state = JSGEN_CLOSED;
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
2007-07-16 23:41:20 -07:00
|
|
|
} else if (gen->state == JSGEN_CLOSED) {
|
2007-03-22 10:30:00 -07:00
|
|
|
closed_generator:
|
|
|
|
switch (op) {
|
|
|
|
case JSGENOP_NEXT:
|
|
|
|
case JSGENOP_SEND:
|
2007-09-30 17:31:22 -07:00
|
|
|
return js_ThrowStopIteration(cx);
|
2007-03-22 10:30:00 -07:00
|
|
|
case JSGENOP_THROW:
|
2008-08-08 09:02:50 -07:00
|
|
|
JS_SetPendingException(cx, argc >= 1 ? vp[2] : JSVAL_VOID);
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_FALSE;
|
|
|
|
default:
|
|
|
|
JS_ASSERT(op == JSGENOP_CLOSE);
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-08 09:02:50 -07:00
|
|
|
arg = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && argc != 0)
|
2007-08-01 21:33:52 -07:00
|
|
|
? vp[2]
|
2007-03-22 10:30:00 -07:00
|
|
|
: JSVAL_VOID;
|
2008-03-06 14:47:46 -08:00
|
|
|
if (!SendToGenerator(cx, op, obj, gen, arg))
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_FALSE;
|
2008-03-06 14:47:46 -08:00
|
|
|
*vp = gen->frame.rval;
|
2007-03-22 10:30:00 -07:00
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
2007-08-01 21:33:52 -07:00
|
|
|
generator_send(JSContext *cx, uintN argc, jsval *vp)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-08-08 09:02:50 -07:00
|
|
|
return generator_op(cx, JSGENOP_SEND, vp, argc);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
2007-08-01 21:33:52 -07:00
|
|
|
generator_next(JSContext *cx, uintN argc, jsval *vp)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-08-08 09:02:50 -07:00
|
|
|
return generator_op(cx, JSGENOP_NEXT, vp, argc);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
2007-08-01 21:33:52 -07:00
|
|
|
generator_throw(JSContext *cx, uintN argc, jsval *vp)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-08-08 09:02:50 -07:00
|
|
|
return generator_op(cx, JSGENOP_THROW, vp, argc);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
2007-08-01 21:33:52 -07:00
|
|
|
generator_close(JSContext *cx, uintN argc, jsval *vp)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-08-08 09:02:50 -07:00
|
|
|
return generator_op(cx, JSGENOP_CLOSE, vp, argc);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static JSFunctionSpec generator_methods[] = {
|
2008-08-08 09:02:50 -07:00
|
|
|
JS_FN(js_iterator_str, iterator_self, 0,JSPROP_ROPERM),
|
|
|
|
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),
|
2007-08-01 21:33:52 -07:00
|
|
|
JS_FS_END
|
2007-03-22 10:30:00 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
#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;
|
|
|
|
STOBJ_SET_SLOT(proto, JSSLOT_ITER_STATE, JSVAL_NULL);
|
2009-08-21 09:58:34 -07:00
|
|
|
STOBJ_SET_SLOT(proto, JSSLOT_ITER_FLAGS, JSVAL_ZERO);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#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);
|
|
|
|
}
|