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

Contents of /trunk/js/jsiter.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

1 /* -*- 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 #include "jsstaticcheck.h"
68 #include "jstracer.h"
69
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 static JS_REQUIRES_STACK JSBool
81 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 js_EnumerateXMLValues(cx, iterable, JSENUMERATE_DESTROY, &state,
109 NULL, NULL);
110 } 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 ? js_EnumerateXMLValues(cx, obj, JSENUMERATE_INIT, &state, NULL, NULL)
147 :
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 if (JS_IsConstructing(cx)) {
178 /* 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 ? js_EnumerateXMLValues(cx, iterable, JSENUMERATE_NEXT, &state,
236 &id, rval)
237 :
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 if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), false, vp))
379 goto bad;
380 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 js_LeaveTrace(cx);
401 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 js_ReportValueError(cx, JSMSG_BAD_ITERATOR_RETURN,
408 JSDVG_SEARCH_STACK, *vp, NULL);
409 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 JS_FRIEND_API(JSBool) JS_FASTCALL
425 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 JS_ASSERT_NOT_ON_TRACE(cx);
440 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 if (!js_EnumerateXMLValues(cx, obj, JSENUMERATE_NEXT, &state,
479 &id, rval)) {
480 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 if (!cx->throwing || !js_ValueIsStopIteration(cx->exception))
602 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 *bp = js_ValueIsStopIteration(v);
619 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 JS_FRIEND_DATA(JSClass) js_GeneratorClass = {
680 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 uintN argc, nargs, nslots;
702 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 gen->frame.imacpc = NULL;
764 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 /* JSOP_GENERATOR appears in the prologue, outside all blocks. */
777 JS_ASSERT(!fp->blockChain);
778 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 static JS_REQUIRES_STACK JSBool
806 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 fp = js_GetTopStackFrame(cx);
855 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 static JS_REQUIRES_STACK JSBool
896 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 js_LeaveTrace(cx);
924
925 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