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

Annotation of /trunk/js/jsiter.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (hide annotations)
Sun Jan 10 07:23:34 2010 UTC (12 years, 5 months ago) by siliconforks
File size: 31650 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

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

  ViewVC Help
Powered by ViewVC 1.1.24