/[jscoverage]/trunk/js/jsiter.cpp
ViewVC logotype

Annotation of /trunk/js/jsiter.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 460 - (hide annotations)
Sat Sep 26 23:15:22 2009 UTC (12 years, 9 months ago) by siliconforks
File size: 31297 byte(s)
Upgrade to SpiderMonkey from Firefox 3.5.3.

1 siliconforks 332 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2     * vim: set ts=8 sw=4 et tw=78:
3     *
4     * ***** BEGIN LICENSE BLOCK *****
5     * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6     *
7     * The contents of this file are subject to the Mozilla Public License Version
8     * 1.1 (the "License"); you may not use this file except in compliance with
9     * the License. You may obtain a copy of the License at
10     * http://www.mozilla.org/MPL/
11     *
12     * Software distributed under the License is distributed on an "AS IS" basis,
13     * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14     * for the specific language governing rights and limitations under the
15     * License.
16     *
17     * The Original Code is Mozilla Communicator client code, released
18     * March 31, 1998.
19     *
20     * The Initial Developer of the Original Code is
21     * Netscape Communications Corporation.
22     * Portions created by the Initial Developer are Copyright (C) 1998
23     * the Initial Developer. All Rights Reserved.
24     *
25     * Contributor(s):
26     *
27     * Alternatively, the contents of this file may be used under the terms of
28     * either of the GNU General Public License Version 2 or later (the "GPL"),
29     * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30     * in which case the provisions of the GPL or the LGPL are applicable instead
31     * of those above. If you wish to allow use of your version of this file only
32     * under the terms of either the GPL or the LGPL, and not to allow others to
33     * use your version of this file under the terms of the MPL, indicate your
34     * decision by deleting the provisions above and replace them with the notice
35     * and other provisions required by the GPL or the LGPL. If you do not delete
36     * the provisions above, a recipient may use your version of this file under
37     * the terms of any one of the MPL, the GPL or the LGPL.
38     *
39     * ***** END LICENSE BLOCK ***** */
40    
41     /*
42     * JavaScript iterators.
43     */
44     #include "jsstddef.h"
45     #include <string.h> /* for memcpy */
46     #include "jstypes.h"
47     #include "jsutil.h"
48     #include "jsarena.h"
49     #include "jsapi.h"
50     #include "jsarray.h"
51     #include "jsatom.h"
52     #include "jsbool.h"
53     #include "jscntxt.h"
54     #include "jsversion.h"
55     #include "jsexn.h"
56     #include "jsfun.h"
57     #include "jsgc.h"
58     #include "jsinterp.h"
59     #include "jsiter.h"
60     #include "jslock.h"
61     #include "jsnum.h"
62     #include "jsobj.h"
63     #include "jsopcode.h"
64     #include "jsscan.h"
65     #include "jsscope.h"
66     #include "jsscript.h"
67 siliconforks 460 #include "jsstaticcheck.h"
68     #include "jstracer.h"
69 siliconforks 332
70     #if JS_HAS_XML_SUPPORT
71     #include "jsxml.h"
72     #endif
73    
74     #if JSSLOT_ITER_FLAGS >= JS_INITIAL_NSLOTS
75     #error JS_INITIAL_NSLOTS must be greater than JSSLOT_ITER_FLAGS.
76     #endif
77    
78     #if JS_HAS_GENERATORS
79    
80 siliconforks 460 static JS_REQUIRES_STACK JSBool
81 siliconforks 332 CloseGenerator(JSContext *cx, JSObject *genobj);
82    
83     #endif
84    
85     /*
86     * Shared code to close iterator's state either through an explicit call or
87     * when GC detects that the iterator is no longer reachable.
88     */
89     void
90     js_CloseNativeIterator(JSContext *cx, JSObject *iterobj)
91     {
92     jsval state;
93     JSObject *iterable;
94    
95     JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass);
96    
97     /* Avoid double work if js_CloseNativeIterator was called on obj. */
98     state = STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE);
99     if (JSVAL_IS_NULL(state))
100     return;
101    
102     /* Protect against failure to fully initialize obj. */
103     iterable = STOBJ_GET_PARENT(iterobj);
104     if (iterable) {
105     #if JS_HAS_XML_SUPPORT
106     uintN flags = JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS));
107     if ((flags & JSITER_FOREACH) && OBJECT_IS_XML(cx, iterable)) {
108 siliconforks 460 js_EnumerateXMLValues(cx, iterable, JSENUMERATE_DESTROY, &state,
109     NULL, NULL);
110 siliconforks 332 } else
111     #endif
112     OBJ_ENUMERATE(cx, iterable, JSENUMERATE_DESTROY, &state, NULL);
113     }
114     STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, JSVAL_NULL);
115     }
116    
117     JSClass js_IteratorClass = {
118     "Iterator",
119     JSCLASS_HAS_RESERVED_SLOTS(2) | /* slots for state and flags */
120     JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
121     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
122     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
123     JSCLASS_NO_OPTIONAL_MEMBERS
124     };
125    
126     static JSBool
127     InitNativeIterator(JSContext *cx, JSObject *iterobj, JSObject *obj, uintN flags)
128     {
129     jsval state;
130     JSBool ok;
131    
132     JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass);
133    
134     /* Initialize iterobj in case of enumerate hook failure. */
135     STOBJ_SET_PARENT(iterobj, obj);
136     STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, JSVAL_NULL);
137     STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_FLAGS, INT_TO_JSVAL(flags));
138     if (!js_RegisterCloseableIterator(cx, iterobj))
139     return JS_FALSE;
140     if (!obj)
141     return JS_TRUE;
142    
143     ok =
144     #if JS_HAS_XML_SUPPORT
145     ((flags & JSITER_FOREACH) && OBJECT_IS_XML(cx, obj))
146 siliconforks 460 ? js_EnumerateXMLValues(cx, obj, JSENUMERATE_INIT, &state, NULL, NULL)
147 siliconforks 332 :
148     #endif
149     OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL);
150     if (!ok)
151     return JS_FALSE;
152    
153     STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state);
154     if (flags & JSITER_ENUMERATE) {
155     /*
156     * The enumerating iterator needs the original object to suppress
157     * enumeration of deleted or shadowed prototype properties. Since the
158     * enumerator never escapes to scripts, we use the prototype slot to
159     * store the original object.
160     */
161     JS_ASSERT(obj != iterobj);
162     STOBJ_SET_PROTO(iterobj, obj);
163     }
164     return JS_TRUE;
165     }
166    
167     static JSBool
168     Iterator(JSContext *cx, JSObject *iterobj, uintN argc, jsval *argv, jsval *rval)
169     {
170     JSBool keyonly;
171     uintN flags;
172     JSObject *obj;
173    
174     keyonly = js_ValueToBoolean(argv[1]);
175     flags = keyonly ? 0 : JSITER_FOREACH;
176    
177 siliconforks 460 if (JS_IsConstructing(cx)) {
178 siliconforks 332 /* XXX work around old valueOf call hidden beneath js_ValueToObject */
179     if (!JSVAL_IS_PRIMITIVE(argv[0])) {
180     obj = JSVAL_TO_OBJECT(argv[0]);
181     } else {
182     obj = js_ValueToNonNullObject(cx, argv[0]);
183     if (!obj)
184     return JS_FALSE;
185     argv[0] = OBJECT_TO_JSVAL(obj);
186     }
187     return InitNativeIterator(cx, iterobj, obj, flags);
188     }
189    
190     *rval = argv[0];
191     return js_ValueToIterator(cx, flags, rval);
192     }
193    
194     static JSBool
195     NewKeyValuePair(JSContext *cx, jsid key, jsval val, jsval *rval)
196     {
197     jsval vec[2];
198     JSTempValueRooter tvr;
199     JSObject *aobj;
200    
201     vec[0] = ID_TO_VALUE(key);
202     vec[1] = val;
203    
204     JS_PUSH_TEMP_ROOT(cx, 2, vec, &tvr);
205     aobj = js_NewArrayObject(cx, 2, vec);
206     *rval = OBJECT_TO_JSVAL(aobj);
207     JS_POP_TEMP_ROOT(cx, &tvr);
208    
209     return aobj != NULL;
210     }
211    
212     static JSBool
213     IteratorNextImpl(JSContext *cx, JSObject *obj, jsval *rval)
214     {
215     JSObject *iterable;
216     jsval state;
217     uintN flags;
218     JSBool foreach, ok;
219     jsid id;
220    
221     JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_IteratorClass);
222    
223     iterable = OBJ_GET_PARENT(cx, obj);
224     JS_ASSERT(iterable);
225     state = STOBJ_GET_SLOT(obj, JSSLOT_ITER_STATE);
226     if (JSVAL_IS_NULL(state))
227     goto stop;
228    
229     flags = JSVAL_TO_INT(STOBJ_GET_SLOT(obj, JSSLOT_ITER_FLAGS));
230     JS_ASSERT(!(flags & JSITER_ENUMERATE));
231     foreach = (flags & JSITER_FOREACH) != 0;
232     ok =
233     #if JS_HAS_XML_SUPPORT
234     (foreach && OBJECT_IS_XML(cx, iterable))
235 siliconforks 460 ? js_EnumerateXMLValues(cx, iterable, JSENUMERATE_NEXT, &state,
236     &id, rval)
237 siliconforks 332 :
238     #endif
239     OBJ_ENUMERATE(cx, iterable, JSENUMERATE_NEXT, &state, &id);
240     if (!ok)
241     return JS_FALSE;
242    
243     STOBJ_SET_SLOT(obj, JSSLOT_ITER_STATE, state);
244     if (JSVAL_IS_NULL(state))
245     goto stop;
246    
247     if (foreach) {
248     #if JS_HAS_XML_SUPPORT
249     if (!OBJECT_IS_XML(cx, iterable) &&
250     !OBJ_GET_PROPERTY(cx, iterable, id, rval)) {
251     return JS_FALSE;
252     }
253     #endif
254     if (!NewKeyValuePair(cx, id, *rval, rval))
255     return JS_FALSE;
256     } else {
257     *rval = ID_TO_VALUE(id);
258     }
259     return JS_TRUE;
260    
261     stop:
262     JS_ASSERT(STOBJ_GET_SLOT(obj, JSSLOT_ITER_STATE) == JSVAL_NULL);
263     *rval = JSVAL_HOLE;
264     return JS_TRUE;
265     }
266    
267     JSBool
268     js_ThrowStopIteration(JSContext *cx)
269     {
270     jsval v;
271    
272     JS_ASSERT(!JS_IsExceptionPending(cx));
273     if (js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_StopIteration), &v))
274     JS_SetPendingException(cx, v);
275     return JS_FALSE;
276     }
277    
278     static JSBool
279     iterator_next(JSContext *cx, uintN argc, jsval *vp)
280     {
281     JSObject *obj;
282    
283     obj = JS_THIS_OBJECT(cx, vp);
284     if (!JS_InstanceOf(cx, obj, &js_IteratorClass, vp + 2))
285     return JS_FALSE;
286    
287     if (!IteratorNextImpl(cx, obj, vp))
288     return JS_FALSE;
289    
290     if (*vp == JSVAL_HOLE) {
291     *vp = JSVAL_NULL;
292     js_ThrowStopIteration(cx);
293     return JS_FALSE;
294     }
295     return JS_TRUE;
296     }
297    
298     static JSBool
299     iterator_self(JSContext *cx, uintN argc, jsval *vp)
300     {
301     *vp = JS_THIS(cx, vp);
302     return !JSVAL_IS_NULL(*vp);
303     }
304    
305     #define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
306    
307     static JSFunctionSpec iterator_methods[] = {
308     JS_FN(js_iterator_str, iterator_self, 0,JSPROP_ROPERM),
309     JS_FN(js_next_str, iterator_next, 0,JSPROP_ROPERM),
310     JS_FS_END
311     };
312    
313     uintN
314     js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj)
315     {
316     if (OBJ_GET_CLASS(cx, iterobj) != &js_IteratorClass)
317     return 0;
318     return JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS));
319     }
320    
321     /*
322     * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
323     * Otherwise construct the default iterator.
324     */
325     JS_FRIEND_API(JSBool)
326     js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp)
327     {
328     JSObject *obj;
329     JSTempValueRooter tvr;
330     JSAtom *atom;
331     JSClass *clasp;
332     JSExtendedClass *xclasp;
333     JSBool ok;
334     JSObject *iterobj;
335     jsval arg;
336    
337     JS_ASSERT(!(flags & ~(JSITER_ENUMERATE |
338     JSITER_FOREACH |
339     JSITER_KEYVALUE)));
340    
341     /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
342     JS_ASSERT(!(flags & JSITER_KEYVALUE) || (flags & JSITER_FOREACH));
343    
344     /* XXX work around old valueOf call hidden beneath js_ValueToObject */
345     if (!JSVAL_IS_PRIMITIVE(*vp)) {
346     obj = JSVAL_TO_OBJECT(*vp);
347     } else {
348     /*
349     * Enumerating over null and undefined gives an empty enumerator.
350     * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of
351     * the first production in 12.6.4 and step 4 of the second production,
352     * but it's "web JS" compatible.
353     */
354     if ((flags & JSITER_ENUMERATE)) {
355     if (!js_ValueToObject(cx, *vp, &obj))
356     return JS_FALSE;
357     if (!obj)
358     goto default_iter;
359     } else {
360     obj = js_ValueToNonNullObject(cx, *vp);
361     if (!obj)
362     return JS_FALSE;
363     }
364     }
365    
366     JS_ASSERT(obj);
367     JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
368    
369     clasp = OBJ_GET_CLASS(cx, obj);
370     if ((clasp->flags & JSCLASS_IS_EXTENDED) &&
371     (xclasp = (JSExtendedClass *) clasp)->iteratorObject) {
372     iterobj = xclasp->iteratorObject(cx, obj, !(flags & JSITER_FOREACH));
373     if (!iterobj)
374     goto bad;
375     *vp = OBJECT_TO_JSVAL(iterobj);
376     } else {
377     atom = cx->runtime->atomState.iteratorAtom;
378 siliconforks 460 if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), false, vp))
379     goto bad;
380 siliconforks 332 if (JSVAL_IS_VOID(*vp)) {
381     default_iter:
382     /*
383     * Fail over to the default enumerating native iterator.
384     *
385     * Create iterobj with a NULL parent to ensure that we use the
386     * correct scope chain to lookup the iterator's constructor. Since
387     * we use the parent slot to keep track of the iterable, we must
388     * fix it up after.
389     */
390     iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL, 0);
391     if (!iterobj)
392     goto bad;
393    
394     /* Store in *vp to protect it from GC (callers must root vp). */
395     *vp = OBJECT_TO_JSVAL(iterobj);
396    
397     if (!InitNativeIterator(cx, iterobj, obj, flags))
398     goto bad;
399     } else {
400 siliconforks 460 js_LeaveTrace(cx);
401 siliconforks 332 arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0);
402     if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg,
403     vp)) {
404     goto bad;
405     }
406     if (JSVAL_IS_PRIMITIVE(*vp)) {
407 siliconforks 460 js_ReportValueError(cx, JSMSG_BAD_ITERATOR_RETURN,
408     JSDVG_SEARCH_STACK, *vp, NULL);
409 siliconforks 332 goto bad;
410     }
411     }
412     }
413    
414     ok = JS_TRUE;
415     out:
416     if (obj)
417     JS_POP_TEMP_ROOT(cx, &tvr);
418     return ok;
419     bad:
420     ok = JS_FALSE;
421     goto out;
422     }
423    
424 siliconforks 399 JS_FRIEND_API(JSBool) JS_FASTCALL
425 siliconforks 332 js_CloseIterator(JSContext *cx, jsval v)
426     {
427     JSObject *obj;
428     JSClass *clasp;
429    
430     JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
431     obj = JSVAL_TO_OBJECT(v);
432     clasp = OBJ_GET_CLASS(cx, obj);
433    
434     if (clasp == &js_IteratorClass) {
435     js_CloseNativeIterator(cx, obj);
436     }
437     #if JS_HAS_GENERATORS
438     else if (clasp == &js_GeneratorClass) {
439 siliconforks 460 JS_ASSERT_NOT_ON_TRACE(cx);
440 siliconforks 332 if (!CloseGenerator(cx, obj))
441     return JS_FALSE;
442     }
443     #endif
444     return JS_TRUE;
445     }
446    
447     static JSBool
448     CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval)
449     {
450     JSObject *obj, *origobj;
451     jsval state;
452     JSBool foreach;
453     jsid id;
454     JSObject *obj2;
455     JSBool cond;
456     JSClass *clasp;
457     JSExtendedClass *xclasp;
458     JSProperty *prop;
459     JSString *str;
460    
461     JS_ASSERT(flags & JSITER_ENUMERATE);
462     JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass);
463    
464     obj = STOBJ_GET_PARENT(iterobj);
465     origobj = STOBJ_GET_PROTO(iterobj);
466     state = STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE);
467     if (JSVAL_IS_NULL(state))
468     goto stop;
469    
470     foreach = (flags & JSITER_FOREACH) != 0;
471     #if JS_HAS_XML_SUPPORT
472     /*
473     * Treat an XML object specially only when it starts the prototype chain.
474     * Otherwise we need to do the usual deleted and shadowed property checks.
475     */
476     if (obj == origobj && OBJECT_IS_XML(cx, obj)) {
477     if (foreach) {
478 siliconforks 460 if (!js_EnumerateXMLValues(cx, obj, JSENUMERATE_NEXT, &state,
479     &id, rval)) {
480 siliconforks 332 return JS_FALSE;
481     }
482     } else {
483     if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id))
484     return JS_FALSE;
485     }
486     STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state);
487     if (JSVAL_IS_NULL(state))
488     goto stop;
489     } else
490     #endif
491     {
492     restart:
493     if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id))
494     return JS_FALSE;
495    
496     STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state);
497     if (JSVAL_IS_NULL(state)) {
498     #if JS_HAS_XML_SUPPORT
499     if (OBJECT_IS_XML(cx, obj)) {
500     /*
501     * We just finished enumerating an XML obj that is present on
502     * the prototype chain of a non-XML origobj. Stop further
503     * prototype chain searches because XML objects don't
504     * enumerate prototypes.
505     */
506     JS_ASSERT(origobj != obj);
507     JS_ASSERT(!OBJECT_IS_XML(cx, origobj));
508     } else
509     #endif
510     {
511     obj = OBJ_GET_PROTO(cx, obj);
512     if (obj) {
513     STOBJ_SET_PARENT(iterobj, obj);
514     if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL))
515     return JS_FALSE;
516     STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state);
517     if (!JSVAL_IS_NULL(state))
518     goto restart;
519     }
520     }
521     goto stop;
522     }
523    
524     /* Skip properties not in obj when looking from origobj. */
525     if (!OBJ_LOOKUP_PROPERTY(cx, origobj, id, &obj2, &prop))
526     return JS_FALSE;
527     if (!prop)
528     goto restart;
529     OBJ_DROP_PROPERTY(cx, obj2, prop);
530    
531     /*
532     * If the id was found in a prototype object or an unrelated object
533     * (specifically, not in an inner object for obj), skip it. This step
534     * means that all OBJ_LOOKUP_PROPERTY implementations must return an
535     * object further along on the prototype chain, or else possibly an
536     * object returned by the JSExtendedClass.outerObject optional hook.
537     */
538     if (obj != obj2) {
539     cond = JS_FALSE;
540     clasp = OBJ_GET_CLASS(cx, obj2);
541     if (clasp->flags & JSCLASS_IS_EXTENDED) {
542     xclasp = (JSExtendedClass *) clasp;
543     cond = xclasp->outerObject &&
544     xclasp->outerObject(cx, obj2) == obj;
545     }
546     if (!cond)
547     goto restart;
548     }
549    
550     if (foreach) {
551     /* Get property querying the original object. */
552     if (!OBJ_GET_PROPERTY(cx, origobj, id, rval))
553     return JS_FALSE;
554     }
555     }
556    
557     if (foreach) {
558     if (flags & JSITER_KEYVALUE) {
559     if (!NewKeyValuePair(cx, id, *rval, rval))
560     return JS_FALSE;
561     }
562     } else {
563     /* Make rval a string for uniformity and compatibility. */
564     str = js_ValueToString(cx, ID_TO_VALUE(id));
565     if (!str)
566     return JS_FALSE;
567     *rval = STRING_TO_JSVAL(str);
568     }
569     return JS_TRUE;
570    
571     stop:
572     JS_ASSERT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE) == JSVAL_NULL);
573     *rval = JSVAL_HOLE;
574     return JS_TRUE;
575     }
576    
577     JS_FRIEND_API(JSBool)
578     js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval)
579     {
580     uintN flags;
581    
582     /* Fast path for native iterators */
583     if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass) {
584     flags = JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS));
585     if (flags & JSITER_ENUMERATE)
586     return CallEnumeratorNext(cx, iterobj, flags, rval);
587    
588     /*
589     * Call next directly as all the methods of the native iterator are
590     * read-only and permanent.
591     */
592     if (!IteratorNextImpl(cx, iterobj, rval))
593     return JS_FALSE;
594     } else {
595     jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom);
596    
597     if (!JS_GetMethodById(cx, iterobj, id, &iterobj, rval))
598     return JS_FALSE;
599     if (!js_InternalCall(cx, iterobj, *rval, 0, NULL, rval)) {
600     /* Check for StopIteration. */
601 siliconforks 399 if (!cx->throwing || !js_ValueIsStopIteration(cx->exception))
602 siliconforks 332 return JS_FALSE;
603    
604     /* Inline JS_ClearPendingException(cx). */
605     cx->throwing = JS_FALSE;
606     cx->exception = JSVAL_VOID;
607     *rval = JSVAL_HOLE;
608     return JS_TRUE;
609     }
610     }
611    
612     return JS_TRUE;
613     }
614    
615     static JSBool
616     stopiter_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
617     {
618 siliconforks 399 *bp = js_ValueIsStopIteration(v);
619 siliconforks 332 return JS_TRUE;
620     }
621    
622     JSClass js_StopIterationClass = {
623     js_StopIteration_str,
624     JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
625     JS_PropertyStub, JS_PropertyStub,
626     JS_PropertyStub, JS_PropertyStub,
627     JS_EnumerateStub, JS_ResolveStub,
628     JS_ConvertStub, JS_FinalizeStub,
629     NULL, NULL,
630     NULL, NULL,
631     NULL, stopiter_hasInstance,
632     NULL, NULL
633     };
634    
635     #if JS_HAS_GENERATORS
636    
637     static void
638     generator_finalize(JSContext *cx, JSObject *obj)
639     {
640     JSGenerator *gen;
641    
642     gen = (JSGenerator *) JS_GetPrivate(cx, obj);
643     if (gen) {
644     /*
645     * gen can be open on shutdown when close hooks are ignored or when
646     * the embedding cancels scheduled close hooks.
647     */
648     JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_CLOSED ||
649     gen->state == JSGEN_OPEN);
650     JS_free(cx, gen);
651     }
652     }
653    
654     static void
655     generator_trace(JSTracer *trc, JSObject *obj)
656     {
657     JSGenerator *gen;
658    
659     gen = (JSGenerator *) JS_GetPrivate(trc->context, obj);
660     if (!gen)
661     return;
662    
663     /*
664     * js_TraceStackFrame does not recursively trace the down-linked frame
665     * chain, so we insist that gen->frame has no parent to trace when the
666     * generator is not running.
667     */
668     JS_ASSERT_IF(gen->state != JSGEN_RUNNING && gen->state != JSGEN_CLOSING,
669     !gen->frame.down);
670    
671     /*
672     * FIXME be 390950. Generator's frame is a part of the JS stack when the
673     * generator is running or closing. Thus tracing the frame in this case
674     * here duplicates the work done in js_TraceContext.
675     */
676     js_TraceStackFrame(trc, &gen->frame);
677     }
678    
679 siliconforks 460 JS_FRIEND_DATA(JSClass) js_GeneratorClass = {
680 siliconforks 332 js_Generator_str,
681     JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS |
682     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator),
683     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
684     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, generator_finalize,
685     NULL, NULL, NULL, NULL,
686     NULL, NULL, JS_CLASS_TRACE(generator_trace), NULL
687     };
688    
689     /*
690     * Called from the JSOP_GENERATOR case in the interpreter, with fp referring
691     * to the frame by which the generator function was activated. Create a new
692     * JSGenerator object, which contains its own JSStackFrame that we populate
693     * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return
694     * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
695     * if they are non-null.
696     */
697     JSObject *
698     js_NewGenerator(JSContext *cx, JSStackFrame *fp)
699     {
700     JSObject *obj;
701 siliconforks 399 uintN argc, nargs, nslots;
702 siliconforks 332 JSGenerator *gen;
703     jsval *slots;
704    
705     /* After the following return, failing control flow must goto bad. */
706     obj = js_NewObject(cx, &js_GeneratorClass, NULL, NULL, 0);
707     if (!obj)
708     return NULL;
709    
710     /* Load and compute stack slot counts. */
711     argc = fp->argc;
712     nargs = JS_MAX(argc, fp->fun->nargs);
713     nslots = 2 + nargs + fp->script->nslots;
714    
715     /* Allocate obj's private data struct. */
716     gen = (JSGenerator *)
717     JS_malloc(cx, sizeof(JSGenerator) + (nslots - 1) * sizeof(jsval));
718     if (!gen)
719     goto bad;
720    
721     gen->obj = obj;
722    
723     /* Steal away objects reflecting fp and point them at gen->frame. */
724     gen->frame.callobj = fp->callobj;
725     if (fp->callobj) {
726     JS_SetPrivate(cx, fp->callobj, &gen->frame);
727     fp->callobj = NULL;
728     }
729     gen->frame.argsobj = fp->argsobj;
730     if (fp->argsobj) {
731     JS_SetPrivate(cx, fp->argsobj, &gen->frame);
732     fp->argsobj = NULL;
733     }
734    
735     /* These two references can be shared with fp until it goes away. */
736     gen->frame.varobj = fp->varobj;
737     gen->frame.thisp = fp->thisp;
738    
739     /* Copy call-invariant script and function references. */
740     gen->frame.script = fp->script;
741     gen->frame.callee = fp->callee;
742     gen->frame.fun = fp->fun;
743    
744     /* Use slots to carve space out of gen->slots. */
745     slots = gen->slots;
746     gen->arena.next = NULL;
747     gen->arena.base = (jsuword) slots;
748     gen->arena.limit = gen->arena.avail = (jsuword) (slots + nslots);
749    
750     /* Copy rval, argv and vars. */
751     gen->frame.rval = fp->rval;
752     memcpy(slots, fp->argv - 2, (2 + nargs) * sizeof(jsval));
753     gen->frame.argc = nargs;
754     gen->frame.argv = slots + 2;
755     slots += 2 + nargs;
756     memcpy(slots, fp->slots, fp->script->nfixed * sizeof(jsval));
757    
758     /* Initialize or copy virtual machine state. */
759     gen->frame.down = NULL;
760     gen->frame.annotation = NULL;
761     gen->frame.scopeChain = fp->scopeChain;
762    
763 siliconforks 399 gen->frame.imacpc = NULL;
764 siliconforks 332 gen->frame.slots = slots;
765     JS_ASSERT(StackBase(fp) == fp->regs->sp);
766     gen->savedRegs.sp = slots + fp->script->nfixed;
767     gen->savedRegs.pc = fp->regs->pc;
768     gen->frame.regs = &gen->savedRegs;
769    
770     /* Copy remaining state (XXX sharp* and xml* should be local vars). */
771     gen->frame.sharpDepth = 0;
772     gen->frame.sharpArray = NULL;
773     gen->frame.flags = (fp->flags & ~JSFRAME_ROOTED_ARGV) | JSFRAME_GENERATOR;
774     gen->frame.dormantNext = NULL;
775     gen->frame.xmlNamespace = NULL;
776 siliconforks 460 /* JSOP_GENERATOR appears in the prologue, outside all blocks. */
777     JS_ASSERT(!fp->blockChain);
778 siliconforks 332 gen->frame.blockChain = NULL;
779    
780     /* Note that gen is newborn. */
781     gen->state = JSGEN_NEWBORN;
782    
783     if (!JS_SetPrivate(cx, obj, gen)) {
784     JS_free(cx, gen);
785     goto bad;
786     }
787     return obj;
788    
789     bad:
790     cx->weakRoots.newborn[GCX_OBJECT] = NULL;
791     return NULL;
792     }
793    
794     typedef enum JSGeneratorOp {
795     JSGENOP_NEXT,
796     JSGENOP_SEND,
797     JSGENOP_THROW,
798     JSGENOP_CLOSE
799     } JSGeneratorOp;
800    
801     /*
802     * Start newborn or restart yielding generator and perform the requested
803     * operation inside its frame.
804     */
805 siliconforks 460 static JS_REQUIRES_STACK JSBool
806 siliconforks 332 SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
807     JSGenerator *gen, jsval arg)
808     {
809     JSStackFrame *fp;
810     JSArena *arena;
811     JSBool ok;
812    
813     if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
814     js_ReportValueError(cx, JSMSG_NESTING_GENERATOR,
815     JSDVG_SEARCH_STACK, OBJECT_TO_JSVAL(obj),
816     JS_GetFunctionId(gen->frame.fun));
817     return JS_FALSE;
818     }
819    
820     JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
821     switch (op) {
822     case JSGENOP_NEXT:
823     case JSGENOP_SEND:
824     if (gen->state == JSGEN_OPEN) {
825     /*
826     * Store the argument to send as the result of the yield
827     * expression.
828     */
829     gen->savedRegs.sp[-1] = arg;
830     }
831     gen->state = JSGEN_RUNNING;
832     break;
833    
834     case JSGENOP_THROW:
835     JS_SetPendingException(cx, arg);
836     gen->state = JSGEN_RUNNING;
837     break;
838    
839     default:
840     JS_ASSERT(op == JSGENOP_CLOSE);
841     JS_SetPendingException(cx, JSVAL_ARETURN);
842     gen->state = JSGEN_CLOSING;
843     break;
844     }
845    
846     /* Extend the current stack pool with gen->arena. */
847     arena = cx->stackPool.current;
848     JS_ASSERT(!arena->next);
849     JS_ASSERT(!gen->arena.next);
850     JS_ASSERT(cx->stackPool.current != &gen->arena);
851     cx->stackPool.current = arena->next = &gen->arena;
852    
853     /* Push gen->frame around the interpreter activation. */
854 siliconforks 460 fp = js_GetTopStackFrame(cx);
855 siliconforks 332 cx->fp = &gen->frame;
856     gen->frame.down = fp;
857     ok = js_Interpret(cx);
858     cx->fp = fp;
859     gen->frame.down = NULL;
860    
861     /* Retract the stack pool and sanitize gen->arena. */
862     JS_ASSERT(!gen->arena.next);
863     JS_ASSERT(arena->next == &gen->arena);
864     JS_ASSERT(cx->stackPool.current == &gen->arena);
865     cx->stackPool.current = arena;
866     arena->next = NULL;
867    
868     if (gen->frame.flags & JSFRAME_YIELDING) {
869     /* Yield cannot fail, throw or be called on closing. */
870     JS_ASSERT(ok);
871     JS_ASSERT(!cx->throwing);
872     JS_ASSERT(gen->state == JSGEN_RUNNING);
873     JS_ASSERT(op != JSGENOP_CLOSE);
874     gen->frame.flags &= ~JSFRAME_YIELDING;
875     gen->state = JSGEN_OPEN;
876     return JS_TRUE;
877     }
878    
879     gen->frame.rval = JSVAL_VOID;
880     gen->state = JSGEN_CLOSED;
881     if (ok) {
882     /* Returned, explicitly or by falling off the end. */
883     if (op == JSGENOP_CLOSE)
884     return JS_TRUE;
885     return js_ThrowStopIteration(cx);
886     }
887    
888     /*
889     * An error, silent termination by operation callback or an exception.
890     * Propagate the condition to the caller.
891     */
892     return JS_FALSE;
893     }
894    
895 siliconforks 460 static JS_REQUIRES_STACK JSBool
896 siliconforks 332 CloseGenerator(JSContext *cx, JSObject *obj)
897     {
898     JSGenerator *gen;
899    
900     JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_GeneratorClass);
901     gen = (JSGenerator *) JS_GetPrivate(cx, obj);
902     if (!gen) {
903     /* Generator prototype object. */
904     return JS_TRUE;
905     }
906    
907     if (gen->state == JSGEN_CLOSED)
908     return JS_TRUE;
909    
910     return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, JSVAL_VOID);
911     }
912    
913     /*
914     * Common subroutine of generator_(next|send|throw|close) methods.
915     */
916     static JSBool
917     generator_op(JSContext *cx, JSGeneratorOp op, jsval *vp, uintN argc)
918     {
919     JSObject *obj;
920     JSGenerator *gen;
921     jsval arg;
922    
923 siliconforks 460 js_LeaveTrace(cx);
924    
925 siliconforks 332 obj = JS_THIS_OBJECT(cx, vp);
926     if (!JS_InstanceOf(cx, obj, &js_GeneratorClass, vp + 2))
927     return JS_FALSE;
928    
929     gen = (JSGenerator *) JS_GetPrivate(cx, obj);
930     if (gen == NULL) {
931     /* This happens when obj is the generator prototype. See bug 352885. */
932     goto closed_generator;
933     }
934    
935     if (gen->state == JSGEN_NEWBORN) {
936     switch (op) {
937     case JSGENOP_NEXT:
938     case JSGENOP_THROW:
939     break;
940    
941     case JSGENOP_SEND:
942     if (argc >= 1 && !JSVAL_IS_VOID(vp[2])) {
943     js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
944     JSDVG_SEARCH_STACK, vp[2], NULL);
945     return JS_FALSE;
946     }
947     break;
948    
949     default:
950     JS_ASSERT(op == JSGENOP_CLOSE);
951     gen->state = JSGEN_CLOSED;
952     return JS_TRUE;
953     }
954     } else if (gen->state == JSGEN_CLOSED) {
955     closed_generator:
956     switch (op) {
957     case JSGENOP_NEXT:
958     case JSGENOP_SEND:
959     return js_ThrowStopIteration(cx);
960     case JSGENOP_THROW:
961     JS_SetPendingException(cx, argc >= 1 ? vp[2] : JSVAL_VOID);
962     return JS_FALSE;
963     default:
964     JS_ASSERT(op == JSGENOP_CLOSE);
965     return JS_TRUE;
966     }
967     }
968    
969     arg = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && argc != 0)
970     ? vp[2]
971     : JSVAL_VOID;
972     if (!SendToGenerator(cx, op, obj, gen, arg))
973     return JS_FALSE;
974     *vp = gen->frame.rval;
975     return JS_TRUE;
976     }
977    
978     static JSBool
979     generator_send(JSContext *cx, uintN argc, jsval *vp)
980     {
981     return generator_op(cx, JSGENOP_SEND, vp, argc);
982     }
983    
984     static JSBool
985     generator_next(JSContext *cx, uintN argc, jsval *vp)
986     {
987     return generator_op(cx, JSGENOP_NEXT, vp, argc);
988     }
989    
990     static JSBool
991     generator_throw(JSContext *cx, uintN argc, jsval *vp)
992     {
993     return generator_op(cx, JSGENOP_THROW, vp, argc);
994     }
995    
996     static JSBool
997     generator_close(JSContext *cx, uintN argc, jsval *vp)
998     {
999     return generator_op(cx, JSGENOP_CLOSE, vp, argc);
1000     }
1001    
1002     static JSFunctionSpec generator_methods[] = {
1003     JS_FN(js_iterator_str, iterator_self, 0,JSPROP_ROPERM),
1004     JS_FN(js_next_str, generator_next, 0,JSPROP_ROPERM),
1005     JS_FN(js_send_str, generator_send, 1,JSPROP_ROPERM),
1006     JS_FN(js_throw_str, generator_throw, 1,JSPROP_ROPERM),
1007     JS_FN(js_close_str, generator_close, 0,JSPROP_ROPERM),
1008     JS_FS_END
1009     };
1010    
1011     #endif /* JS_HAS_GENERATORS */
1012    
1013     JSObject *
1014     js_InitIteratorClasses(JSContext *cx, JSObject *obj)
1015     {
1016     JSObject *proto, *stop;
1017    
1018     /* Idempotency required: we initialize several things, possibly lazily. */
1019     if (!js_GetClassObject(cx, obj, JSProto_StopIteration, &stop))
1020     return NULL;
1021     if (stop)
1022     return stop;
1023    
1024     proto = JS_InitClass(cx, obj, NULL, &js_IteratorClass, Iterator, 2,
1025     NULL, iterator_methods, NULL, NULL);
1026     if (!proto)
1027     return NULL;
1028     STOBJ_SET_SLOT(proto, JSSLOT_ITER_STATE, JSVAL_NULL);
1029    
1030     #if JS_HAS_GENERATORS
1031     /* Initialize the generator internals if configured. */
1032     if (!JS_InitClass(cx, obj, NULL, &js_GeneratorClass, NULL, 0,
1033     NULL, generator_methods, NULL, NULL)) {
1034     return NULL;
1035     }
1036     #endif
1037    
1038     return JS_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0,
1039     NULL, NULL, NULL, NULL);
1040     }

  ViewVC Help
Powered by ViewVC 1.1.24