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

Contents of /trunk/js/jsiter.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

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 <string.h> /* for memcpy */
45 #include "jstypes.h"
46 #include "jsstdint.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 "jsbuiltins.h"
54 #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 #include "jsstaticcheck.h"
69 #include "jstracer.h"
70
71 #if JS_HAS_XML_SUPPORT
72 #include "jsxml.h"
73 #endif
74
75 JS_STATIC_ASSERT(JSSLOT_ITER_FLAGS < JS_INITIAL_NSLOTS);
76
77 #if JS_HAS_GENERATORS
78
79 static JS_REQUIRES_STACK JSBool
80 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 iterable = iterobj->getParent();
103 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 js_EnumerateXMLValues(cx, iterable, JSENUMERATE_DESTROY, &state,
108 NULL, NULL);
109 } else
110 #endif
111 iterable->enumerate(cx, JSENUMERATE_DESTROY, &state, NULL);
112 }
113 STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, JSVAL_NULL);
114 }
115
116 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 JSClass js_IteratorClass = {
135 "Iterator",
136 JSCLASS_HAS_RESERVED_SLOTS(2) | /* slots for state and flags */
137 JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
138 JSCLASS_MARK_IS_TRACE,
139 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
140 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
141 NULL, NULL, NULL, NULL,
142 NULL, NULL, JS_CLASS_TRACE(iterator_trace), NULL
143 };
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 ? js_EnumerateXMLValues(cx, obj, JSENUMERATE_INIT, &state, NULL, NULL)
166 :
167 #endif
168 obj->enumerate(cx, JSENUMERATE_INIT, &state, NULL);
169 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 if (JS_IsConstructing(cx)) {
197 /* 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 ? js_EnumerateXMLValues(cx, iterable, JSENUMERATE_NEXT, &state,
255 &id, rval)
256 :
257 #endif
258 iterable->enumerate(cx, JSENUMERATE_NEXT, &state, &id);
259 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 !iterable->getProperty(cx, id, rval)) {
270 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 if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), false, vp))
398 goto bad;
399 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 iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL);
410 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 js_LeaveTrace(cx);
420 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 js_ReportValueError(cx, JSMSG_BAD_ITERATOR_RETURN,
427 JSDVG_SEARCH_STACK, *vp, NULL);
428 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 JS_FRIEND_API(JSBool) JS_FASTCALL
444 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 JS_ASSERT_NOT_ON_TRACE(cx);
459 if (!CloseGenerator(cx, obj))
460 return JS_FALSE;
461 }
462 #endif
463 return JS_TRUE;
464 }
465 JS_DEFINE_CALLINFO_2(FRIEND, BOOL, js_CloseIterator, CONTEXT, JSVAL, 0, 0)
466
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 if (!js_EnumerateXMLValues(cx, obj, JSENUMERATE_NEXT, &state,
499 &id, rval)) {
500 return JS_FALSE;
501 }
502 } else {
503 if (!obj->enumerate(cx, JSENUMERATE_NEXT, &state, &id))
504 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 if (!obj->enumerate(cx, JSENUMERATE_NEXT, &state, &id))
514 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 if (!obj->enumerate(cx, JSENUMERATE_INIT, &state, NULL))
535 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 if (!origobj->lookupProperty(cx, id, &obj2, &prop))
546 return JS_FALSE;
547 if (!prop)
548 goto restart;
549 obj2->dropProperty(cx, prop);
550
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 * means that all lookupProperty implementations must return an
555 * 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 if (!origobj->getProperty(cx, id, rval))
573 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 if (!cx->throwing || !js_ValueIsStopIteration(cx->exception))
622 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 *bp = js_ValueIsStopIteration(v);
639 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 JS_ConvertStub, NULL,
649 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 JSGenerator *gen = (JSGenerator *) obj->getPrivate();
661 if (!gen)
662 return;
663
664 /*
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 }
673
674 static void
675 generator_trace(JSTracer *trc, JSObject *obj)
676 {
677 JSGenerator *gen = (JSGenerator *) obj->getPrivate();
678 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 JS_FRIEND_DATA(JSClass) js_GeneratorClass = {
698 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 uintN argc, nargs, nslots;
720 JSGenerator *gen;
721 jsval *slots;
722
723 obj = js_NewObject(cx, &js_GeneratorClass, NULL, NULL);
724 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 cx->malloc(sizeof(JSGenerator) + (nslots - 1) * sizeof(jsval));
735 if (!gen)
736 return NULL;
737
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 fp->callobj->setPrivate(&gen->frame);
744 fp->callobj = NULL;
745 }
746 gen->frame.argsobj = fp->argsobj;
747 if (fp->argsobj) {
748 JSVAL_TO_OBJECT(fp->argsobj)->setPrivate(&gen->frame);
749 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 gen->frame.imacpc = NULL;
780 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 /* JSOP_GENERATOR appears in the prologue, outside all blocks. */
792 JS_ASSERT(!fp->blockChain);
793 gen->frame.blockChain = NULL;
794
795 /* Note that gen is newborn. */
796 gen->state = JSGEN_NEWBORN;
797
798 obj->setPrivate(gen);
799 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 static JS_REQUIRES_STACK JSBool
814 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 fp = js_GetTopStackFrame(cx);
863 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 static JS_REQUIRES_STACK JSBool
904 CloseGenerator(JSContext *cx, JSObject *obj)
905 {
906 JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_GeneratorClass);
907
908 JSGenerator *gen = (JSGenerator *) obj->getPrivate();
909 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 js_LeaveTrace(cx);
930
931 obj = JS_THIS_OBJECT(cx, vp);
932 if (!JS_InstanceOf(cx, obj, &js_GeneratorClass, vp + 2))
933 return JS_FALSE;
934
935 JSGenerator *gen = (JSGenerator *) obj->getPrivate();
936 if (!gen) {
937 /* 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 STOBJ_SET_SLOT(proto, JSSLOT_ITER_FLAGS, JSVAL_ZERO);
1036
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