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 "mozilla/Util.h"
45 :
46 : #include "jstypes.h"
47 : #include "jsutil.h"
48 : #include "jsapi.h"
49 : #include "jsarray.h"
50 : #include "jsatom.h"
51 : #include "jsbool.h"
52 : #include "jscntxt.h"
53 : #include "jsversion.h"
54 : #include "jsexn.h"
55 : #include "jsfun.h"
56 : #include "jsgc.h"
57 : #include "jsgcmark.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 "jsproxy.h"
65 : #include "jsscope.h"
66 : #include "jsscript.h"
67 :
68 : #if JS_HAS_XML_SUPPORT
69 : #include "jsxml.h"
70 : #endif
71 :
72 : #include "ds/Sort.h"
73 : #include "frontend/TokenStream.h"
74 : #include "vm/GlobalObject.h"
75 :
76 : #include "jsinferinlines.h"
77 : #include "jsobjinlines.h"
78 :
79 : #include "vm/MethodGuard-inl.h"
80 : #include "vm/Stack-inl.h"
81 : #include "vm/String-inl.h"
82 :
83 : using namespace mozilla;
84 : using namespace js;
85 : using namespace js::gc;
86 :
87 : static void iterator_finalize(JSContext *cx, JSObject *obj);
88 : static void iterator_trace(JSTracer *trc, JSObject *obj);
89 : static JSObject *iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly);
90 :
91 : Class js::IteratorClass = {
92 : "Iterator",
93 : JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
94 : JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
95 : JS_PropertyStub, /* addProperty */
96 : JS_PropertyStub, /* delProperty */
97 : JS_PropertyStub, /* getProperty */
98 : JS_StrictPropertyStub, /* setProperty */
99 : JS_EnumerateStub,
100 : JS_ResolveStub,
101 : JS_ConvertStub,
102 : iterator_finalize,
103 : NULL, /* checkAccess */
104 : NULL, /* call */
105 : NULL, /* construct */
106 : NULL, /* hasInstance */
107 : iterator_trace,
108 : {
109 : NULL, /* equality */
110 : NULL, /* outerObject */
111 : NULL, /* innerObject */
112 : iterator_iterator,
113 : NULL /* unused */
114 : }
115 : };
116 :
117 : Class js::ElementIteratorClass = {
118 : "ElementIterator",
119 : JSCLASS_HAS_RESERVED_SLOTS(ElementIteratorObject::NumSlots),
120 : JS_PropertyStub, /* addProperty */
121 : JS_PropertyStub, /* delProperty */
122 : JS_PropertyStub, /* getProperty */
123 : JS_StrictPropertyStub, /* setProperty */
124 : JS_EnumerateStub,
125 : JS_ResolveStub,
126 : JS_ConvertStub,
127 : NULL, /* finalize */
128 : NULL, /* checkAccess */
129 : NULL, /* call */
130 : NULL, /* construct */
131 : NULL, /* hasInstance */
132 : NULL, /* trace */
133 : {
134 : NULL, /* equality */
135 : NULL, /* outerObject */
136 : NULL, /* innerObject */
137 : iterator_iterator,
138 : NULL /* unused */
139 : }
140 : };
141 :
142 : static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::FINALIZE_OBJECT2;
143 :
144 : void
145 17167 : NativeIterator::mark(JSTracer *trc)
146 : {
147 24812 : for (HeapPtr<JSFlatString> *str = begin(); str < end(); str++)
148 7645 : MarkString(trc, str, "prop");
149 17167 : if (obj)
150 4137 : MarkObject(trc, &obj, "obj");
151 17167 : }
152 :
153 : static void
154 568601 : iterator_finalize(JSContext *cx, JSObject *obj)
155 : {
156 568601 : JS_ASSERT(obj->isIterator());
157 :
158 568601 : NativeIterator *ni = obj->getNativeIterator();
159 568601 : if (ni) {
160 568592 : obj->setPrivate(NULL);
161 568592 : cx->free_(ni);
162 : }
163 568601 : }
164 :
165 : static void
166 17185 : iterator_trace(JSTracer *trc, JSObject *obj)
167 : {
168 17185 : NativeIterator *ni = obj->getNativeIterator();
169 :
170 17185 : if (ni)
171 17167 : ni->mark(trc);
172 17185 : }
173 :
174 : struct IdHashPolicy {
175 : typedef jsid Lookup;
176 75513976 : static HashNumber hash(jsid id) {
177 75513976 : return JSID_BITS(id);
178 : }
179 905679 : static bool match(jsid id1, jsid id2) {
180 905679 : return id1 == id2;
181 : }
182 : };
183 :
184 : typedef HashSet<jsid, IdHashPolicy> IdSet;
185 :
186 : static inline bool
187 72757 : NewKeyValuePair(JSContext *cx, jsid id, const Value &val, Value *rval)
188 : {
189 72757 : Value vec[2] = { IdToValue(id), val };
190 145514 : AutoArrayRooter tvr(cx, ArrayLength(vec), vec);
191 :
192 72757 : JSObject *aobj = NewDenseCopiedArray(cx, 2, vec);
193 72757 : if (!aobj)
194 0 : return false;
195 72757 : rval->setObject(*aobj);
196 72757 : return true;
197 : }
198 :
199 : static inline bool
200 80196792 : Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id,
201 : bool enumerable, unsigned flags, IdSet& ht, AutoIdVector *props)
202 : {
203 80196792 : JS_ASSERT_IF(flags & JSITER_OWNONLY, obj == pobj);
204 :
205 : /*
206 : * We implement __proto__ using a property on |Object.prototype|, but
207 : * because __proto__ is highly deserving of removal, we don't want it to
208 : * show up in property enumeration, even if only for |Object.prototype|
209 : * (think introspection by Prototype-like frameworks that add methods to
210 : * the built-in prototypes). So exclude __proto__ if the object where the
211 : * property was found has no [[Prototype]] and might be |Object.prototype|.
212 : */
213 80196792 : if (JS_UNLIKELY(!pobj->getProto() && JSID_IS_ATOM(id, cx->runtime->atomState.protoAtom)))
214 562041 : return true;
215 :
216 79634751 : if (!(flags & JSITER_OWNONLY) || pobj->isProxy() || pobj->getOps()->enumerate) {
217 : /* If we've already seen this, we definitely won't add it. */
218 75513976 : IdSet::AddPtr p = ht.lookupForAdd(id);
219 75513976 : if (JS_UNLIKELY(!!p))
220 905679 : return true;
221 :
222 : /*
223 : * It's not necessary to add properties to the hash table at the end of
224 : * the prototype chain, but custom enumeration behaviors might return
225 : * duplicated properties, so always add in such cases.
226 : */
227 74608297 : if ((pobj->getProto() || pobj->isProxy() || pobj->getOps()->enumerate) && !ht.add(p, id))
228 0 : return false;
229 : }
230 :
231 78729072 : if (enumerable || (flags & JSITER_HIDDEN))
232 58703445 : return props->append(id);
233 :
234 20025627 : return true;
235 : }
236 :
237 : static bool
238 2455835 : EnumerateNativeProperties(JSContext *cx, JSObject *obj, JSObject *pobj, unsigned flags, IdSet &ht,
239 : AutoIdVector *props)
240 : {
241 4911670 : RootObject objRoot(cx, &obj);
242 4911670 : RootObject pobjRoot(cx, &pobj);
243 :
244 2455835 : size_t initialLength = props->length();
245 :
246 : /* Collect all unique properties from this object's scope. */
247 2455835 : Shape::Range r = pobj->lastProperty()->all();
248 4911670 : Shape::Range::Root root(cx, &r);
249 43071539 : for (; !r.empty(); r.popFront()) {
250 40615704 : const Shape &shape = r.front();
251 :
252 81231390 : if (!JSID_IS_DEFAULT_XML_NAMESPACE(shape.propid()) &&
253 40615686 : !Enumerate(cx, obj, pobj, shape.propid(), shape.enumerable(), flags, ht, props))
254 : {
255 0 : return false;
256 : }
257 : }
258 :
259 2455835 : ::Reverse(props->begin() + initialLength, props->end());
260 2455835 : return true;
261 : }
262 :
263 : static bool
264 113418 : EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, unsigned flags,
265 : IdSet &ht, AutoIdVector *props)
266 : {
267 226836 : if (!Enumerate(cx, obj, pobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), false,
268 113418 : flags, ht, props)) {
269 0 : return false;
270 : }
271 :
272 113418 : if (pobj->getArrayLength() > 0) {
273 105347 : size_t initlen = pobj->getDenseArrayInitializedLength();
274 105347 : const Value *vp = pobj->getDenseArrayElements();
275 889307 : for (size_t i = 0; i < initlen; ++i, ++vp) {
276 783960 : if (!vp->isMagic(JS_ARRAY_HOLE)) {
277 : /* Dense arrays never get so large that i would not fit into an integer id. */
278 765274 : if (!Enumerate(cx, obj, pobj, INT_TO_JSID(i), true, flags, ht, props))
279 0 : return false;
280 : }
281 : }
282 : }
283 :
284 113418 : return true;
285 : }
286 :
287 : #ifdef JS_MORE_DETERMINISTIC
288 :
289 : struct SortComparatorIds
290 : {
291 : JSContext *const cx;
292 :
293 : SortComparatorIds(JSContext *cx)
294 : : cx(cx) {}
295 :
296 : bool operator()(jsid a, jsid b, bool *lessOrEqualp)
297 : {
298 : /* Pick an arbitrary total order on jsids that is stable across executions. */
299 : JSString *astr = IdToString(cx, a);
300 : if (!astr)
301 : return false;
302 : JSString *bstr = IdToString(cx, b);
303 : if (!bstr)
304 : return false;
305 :
306 : int32_t result;
307 : if (!CompareStrings(cx, astr, bstr, &result))
308 : return false;
309 :
310 : *lessOrEqualp = (result <= 0);
311 : return true;
312 : }
313 : };
314 :
315 : #endif /* JS_MORE_DETERMINISTIC */
316 :
317 : static bool
318 1870990 : Snapshot(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector *props)
319 : {
320 3741980 : IdSet ht(cx);
321 1870990 : if (!ht.init(32))
322 0 : return NULL;
323 :
324 3741980 : RootObject objRoot(cx, &obj);
325 3741980 : RootedVarObject pobj(cx);
326 1870990 : pobj = obj;
327 :
328 1267230 : do {
329 2569443 : Class *clasp = pobj->getClass();
330 7480922 : if (pobj->isNative() &&
331 2455974 : !pobj->getOps()->enumerate &&
332 2455505 : !(clasp->flags & JSCLASS_NEW_ENUMERATE)) {
333 2455505 : if (!clasp->enumerate(cx, pobj))
334 0 : return false;
335 2455505 : if (!EnumerateNativeProperties(cx, obj, pobj, flags, ht, props))
336 0 : return false;
337 113938 : } else if (pobj->isDenseArray()) {
338 113418 : if (!EnumerateDenseArrayProperties(cx, obj, pobj, flags, ht, props))
339 0 : return false;
340 : } else {
341 520 : if (pobj->isProxy()) {
342 36 : AutoIdVector proxyProps(cx);
343 18 : if (flags & JSITER_OWNONLY) {
344 18 : if (flags & JSITER_HIDDEN) {
345 9 : if (!Proxy::getOwnPropertyNames(cx, pobj, proxyProps))
346 0 : return false;
347 : } else {
348 9 : if (!Proxy::keys(cx, pobj, proxyProps))
349 0 : return false;
350 : }
351 : } else {
352 0 : if (!Proxy::enumerate(cx, pobj, proxyProps))
353 0 : return false;
354 : }
355 36 : for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
356 18 : if (!Enumerate(cx, obj, pobj, proxyProps[n], true, flags, ht, props))
357 0 : return false;
358 : }
359 : /* Proxy objects enumerate the prototype on their own, so we are done here. */
360 18 : break;
361 : }
362 : Value state;
363 502 : JSIterateOp op = (flags & JSITER_HIDDEN) ? JSENUMERATE_INIT_ALL : JSENUMERATE_INIT;
364 502 : if (!pobj->enumerate(cx, op, &state, NULL))
365 0 : return false;
366 502 : if (state.isMagic(JS_NATIVE_ENUMERATE)) {
367 330 : if (!EnumerateNativeProperties(cx, obj, pobj, flags, ht, props))
368 0 : return false;
369 : } else {
370 38702396 : while (true) {
371 : jsid id;
372 38702568 : if (!pobj->enumerate(cx, JSENUMERATE_NEXT, &state, &id))
373 0 : return false;
374 38702568 : if (state.isNull())
375 172 : break;
376 38702396 : if (!Enumerate(cx, obj, pobj, id, true, flags, ht, props))
377 0 : return false;
378 : }
379 : }
380 : }
381 :
382 2569425 : if ((flags & JSITER_OWNONLY) || pobj->isXML())
383 1302195 : break;
384 1267230 : } while ((pobj = pobj->getProto()) != NULL);
385 :
386 : #ifdef JS_MORE_DETERMINISTIC
387 :
388 : /*
389 : * In some cases the enumeration order for an object depends on the
390 : * execution mode (interpreter vs. JIT), especially for native objects
391 : * with a class enumerate hook (where resolving a property changes the
392 : * resulting enumeration order). These aren't really bugs, but the
393 : * differences can change the generated output and confuse correctness
394 : * fuzzers, so we sort the ids if such a fuzzer is running.
395 : *
396 : * We don't do this in the general case because (a) doing so is slow,
397 : * and (b) it also breaks the web, which expects enumeration order to
398 : * follow the order in which properties are added, in certain cases.
399 : * Since ECMA does not specify an enumeration order for objects, both
400 : * behaviors are technically correct to do.
401 : */
402 :
403 : jsid *ids = props->begin();
404 : size_t n = props->length();
405 :
406 : Vector<jsid> tmp(cx);
407 : if (!tmp.resizeUninitialized(n))
408 : return false;
409 :
410 : if (!MergeSort(ids, n, tmp.begin(), SortComparatorIds(cx)))
411 : return false;
412 :
413 : #endif /* JS_MORE_DETERMINISTIC */
414 :
415 1870990 : return true;
416 : }
417 :
418 : bool
419 2449 : js::VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap)
420 : {
421 : JS_STATIC_ASSERT(sizeof(JSIdArray) > sizeof(jsid));
422 2449 : size_t len = props.length();
423 2449 : size_t idsz = len * sizeof(jsid);
424 2449 : size_t sz = (sizeof(JSIdArray) - sizeof(jsid)) + idsz;
425 2449 : JSIdArray *ida = static_cast<JSIdArray *>(cx->malloc_(sz));
426 2449 : if (!ida)
427 0 : return false;
428 :
429 2449 : ida->length = static_cast<int>(len);
430 2449 : jsid *v = props.begin();
431 5339 : for (int i = 0; i < ida->length; i++)
432 2890 : ida->vector[i].init(v[i]);
433 2449 : *idap = ida;
434 2449 : return true;
435 : }
436 :
437 : JS_FRIEND_API(bool)
438 1305449 : js::GetPropertyNames(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector *props)
439 : {
440 1305449 : return Snapshot(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), props);
441 : }
442 :
443 : size_t sCustomIteratorCount = 0;
444 :
445 : static inline bool
446 571711 : GetCustomIterator(JSContext *cx, JSObject *obj, unsigned flags, Value *vp)
447 : {
448 571711 : JS_CHECK_RECURSION(cx, return false);
449 :
450 : /*
451 : * for-of iteration does not fall back on __iterator__ or property
452 : * enumeration. This is more conservative than the current proposed spec.
453 : */
454 571702 : if (flags == JSITER_FOR_OF) {
455 : js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NOT_ITERABLE,
456 243 : JSDVG_SEARCH_STACK, ObjectValue(*obj), NULL, NULL, NULL);
457 243 : return false;
458 : }
459 :
460 : /* Check whether we have a valid __iterator__ method. */
461 571459 : JSAtom *atom = cx->runtime->atomState.iteratorAtom;
462 571459 : if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, vp))
463 0 : return false;
464 :
465 : /* If there is no custom __iterator__ method, we are done here. */
466 571459 : if (!vp->isObject()) {
467 565541 : vp->setUndefined();
468 565541 : return true;
469 : }
470 :
471 5918 : if (!cx->runningWithTrustedPrincipals())
472 5918 : ++sCustomIteratorCount;
473 :
474 : /* Otherwise call it and return that object. */
475 5918 : Value arg = BooleanValue((flags & JSITER_FOREACH) == 0);
476 5918 : if (!Invoke(cx, ObjectValue(*obj), *vp, 1, &arg, vp))
477 5711 : return false;
478 207 : if (vp->isPrimitive()) {
479 : /*
480 : * We are always coming from js::ValueToIterator, and we are no longer on
481 : * trace, so the object we are iterating over is on top of the stack (-1).
482 : */
483 0 : JSAutoByteString bytes;
484 0 : if (!js_AtomToPrintableString(cx, atom, &bytes))
485 0 : return false;
486 0 : js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
487 0 : -1, ObjectValue(*obj), NULL, bytes.ptr());
488 0 : return false;
489 : }
490 207 : return true;
491 : }
492 :
493 : template <typename T>
494 : static inline bool
495 6915 : Compare(T *a, T *b, size_t c)
496 : {
497 6915 : size_t n = (c + size_t(7)) / size_t(8);
498 6915 : switch (c % 8) {
499 6915 : case 0: do { if (*a++ != *b++) return false;
500 0 : case 7: if (*a++ != *b++) return false;
501 0 : case 6: if (*a++ != *b++) return false;
502 0 : case 5: if (*a++ != *b++) return false;
503 0 : case 4: if (*a++ != *b++) return false;
504 10 : case 3: if (*a++ != *b++) return false;
505 6885 : case 2: if (*a++ != *b++) return false;
506 6915 : case 1: if (*a++ != *b++) return false;
507 : } while (--n > 0);
508 : }
509 6915 : return true;
510 : }
511 :
512 : static inline JSObject *
513 566095 : NewIteratorObject(JSContext *cx, unsigned flags)
514 : {
515 566095 : if (flags & JSITER_ENUMERATE) {
516 1124784 : RootedVarTypeObject type(cx);
517 562392 : type = cx->compartment->getEmptyType(cx);
518 562392 : if (!type)
519 0 : return NULL;
520 :
521 1124784 : RootedVarShape emptyEnumeratorShape(cx);
522 : emptyEnumeratorShape = EmptyShape::getInitialShape(cx, &IteratorClass, NULL, NULL,
523 562392 : ITERATOR_FINALIZE_KIND);
524 562392 : if (!emptyEnumeratorShape)
525 0 : return NULL;
526 :
527 : JSObject *obj = JSObject::create(cx, ITERATOR_FINALIZE_KIND,
528 562392 : emptyEnumeratorShape, type, NULL);
529 562392 : if (!obj)
530 0 : return NULL;
531 :
532 562392 : JS_ASSERT(obj->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);
533 562392 : return obj;
534 : }
535 :
536 3703 : return NewBuiltinClassInstance(cx, &IteratorClass);
537 : }
538 :
539 : NativeIterator *
540 568602 : NativeIterator::allocateIterator(JSContext *cx, uint32_t slength, const AutoIdVector &props)
541 : {
542 568602 : size_t plength = props.length();
543 : NativeIterator *ni = (NativeIterator *)
544 : cx->malloc_(sizeof(NativeIterator)
545 : + plength * sizeof(JSString *)
546 568602 : + slength * sizeof(Shape *));
547 568602 : if (!ni)
548 0 : return NULL;
549 1137204 : AutoValueVector strings(cx);
550 568602 : ni->props_array = ni->props_cursor = (HeapPtr<JSFlatString> *) (ni + 1);
551 568602 : ni->props_end = ni->props_array + plength;
552 568602 : if (plength) {
553 54788870 : for (size_t i = 0; i < plength; i++) {
554 54246212 : JSFlatString *str = IdToString(cx, props[i]);
555 54246212 : if (!str || !strings.append(StringValue(str)))
556 9 : return NULL;
557 54246203 : ni->props_array[i].init(str);
558 : }
559 : }
560 568593 : return ni;
561 : }
562 :
563 : inline void
564 568593 : NativeIterator::init(JSObject *obj, unsigned flags, uint32_t slength, uint32_t key)
565 : {
566 568593 : this->obj.init(obj);
567 568593 : this->flags = flags;
568 568593 : this->shapes_array = (const Shape **) this->props_end;
569 568593 : this->shapes_length = slength;
570 568593 : this->shapes_key = key;
571 568593 : }
572 :
573 : static inline void
574 632105 : RegisterEnumerator(JSContext *cx, JSObject *iterobj, NativeIterator *ni)
575 : {
576 : /* Register non-escaping native enumerators (for-in) with the current context. */
577 632105 : if (ni->flags & JSITER_ENUMERATE) {
578 628402 : ni->next = cx->enumerators;
579 628402 : cx->enumerators = iterobj;
580 :
581 628402 : JS_ASSERT(!(ni->flags & JSITER_ACTIVE));
582 628402 : ni->flags |= JSITER_ACTIVE;
583 : }
584 632105 : }
585 :
586 : static inline bool
587 288674 : VectorToKeyIterator(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector &keys,
588 : uint32_t slength, uint32_t key, Value *vp)
589 : {
590 288674 : JS_ASSERT(!(flags & JSITER_FOREACH));
591 :
592 288674 : if (obj) {
593 288349 : if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx))
594 0 : return false;
595 288349 : types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
596 : }
597 :
598 288674 : JSObject *iterobj = NewIteratorObject(cx, flags);
599 288674 : if (!iterobj)
600 0 : return false;
601 :
602 288674 : NativeIterator *ni = NativeIterator::allocateIterator(cx, slength, keys);
603 288674 : if (!ni)
604 9 : return false;
605 288665 : ni->init(obj, flags, slength, key);
606 :
607 288665 : if (slength) {
608 : /*
609 : * Fill in the shape array from scratch. We can't use the array that was
610 : * computed for the cache lookup earlier, as constructing iterobj could
611 : * have triggered a shape-regenerating GC. Don't bother with regenerating
612 : * the shape key; if such a GC *does* occur, we can only get hits through
613 : * the one-slot lastNativeIterator cache.
614 : */
615 1675 : JSObject *pobj = obj;
616 1675 : size_t ind = 0;
617 3320 : do {
618 3320 : ni->shapes_array[ind++] = pobj->lastProperty();
619 3320 : pobj = pobj->getProto();
620 : } while (pobj);
621 1675 : JS_ASSERT(ind == slength);
622 : }
623 :
624 288665 : iterobj->setNativeIterator(ni);
625 288665 : vp->setObject(*iterobj);
626 :
627 288665 : RegisterEnumerator(cx, iterobj, ni);
628 288665 : return true;
629 : }
630 :
631 : namespace js {
632 :
633 : bool
634 18 : VectorToKeyIterator(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector &props, Value *vp)
635 : {
636 18 : return VectorToKeyIterator(cx, obj, flags, props, 0, 0, vp);
637 : }
638 :
639 : bool
640 277421 : VectorToValueIterator(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector &keys,
641 : Value *vp)
642 : {
643 277421 : JS_ASSERT(flags & JSITER_FOREACH);
644 :
645 277421 : if (obj) {
646 277210 : if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx))
647 0 : return false;
648 277210 : types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
649 : }
650 :
651 277421 : JSObject *iterobj = NewIteratorObject(cx, flags);
652 277421 : if (!iterobj)
653 0 : return false;
654 :
655 277421 : NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, keys);
656 277421 : if (!ni)
657 0 : return false;
658 277421 : ni->init(obj, flags, 0, 0);
659 :
660 277421 : iterobj->setNativeIterator(ni);
661 277421 : vp->setObject(*iterobj);
662 :
663 277421 : RegisterEnumerator(cx, iterobj, ni);
664 277421 : return true;
665 : }
666 :
667 : bool
668 0 : EnumeratedIdVectorToIterator(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector &props, Value *vp)
669 : {
670 0 : if (!(flags & JSITER_FOREACH))
671 0 : return VectorToKeyIterator(cx, obj, flags, props, vp);
672 :
673 0 : return VectorToValueIterator(cx, obj, flags, props, vp);
674 : }
675 :
676 : static inline void
677 66019 : UpdateNativeIterator(NativeIterator *ni, JSObject *obj)
678 : {
679 : // Update the object for which the native iterator is associated, so
680 : // SuppressDeletedPropertyHelper will recognize the iterator as a match.
681 66019 : ni->obj = obj;
682 66019 : }
683 :
684 : bool
685 650037 : GetIterator(JSContext *cx, JSObject *obj, unsigned flags, Value *vp)
686 : {
687 1300074 : Vector<const Shape *, 8> shapes(cx);
688 650037 : uint32_t key = 0;
689 :
690 650037 : bool keysOnly = (flags == JSITER_ENUMERATE);
691 :
692 650037 : if (obj) {
693 : /* Enumerate Iterator.prototype directly. */
694 649501 : if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) {
695 : /*
696 : * Arrays and other classes representing iterable collections have
697 : * the JSCLASS_FOR_OF_ITERATION flag. This flag means that the
698 : * object responds to all other kinds of enumeration (for-in,
699 : * for-each, Object.keys, Object.getOwnPropertyNames, etc.) in the
700 : * default way, ignoring the hook. The hook is used only when
701 : * iterating in the style of a for-of loop.
702 : */
703 124520 : if (!(obj->getClass()->flags & JSCLASS_FOR_OF_ITERATION) || flags == JSITER_FOR_OF) {
704 11690 : JSObject *iterobj = op(cx, obj, !(flags & (JSITER_FOREACH | JSITER_FOR_OF)));
705 11690 : if (!iterobj)
706 0 : return false;
707 11690 : vp->setObject(*iterobj);
708 11690 : types::MarkIteratorUnknown(cx);
709 11690 : return true;
710 : }
711 : }
712 :
713 637811 : if (keysOnly) {
714 : /*
715 : * Check to see if this is the same as the most recent object which
716 : * was iterated over. We don't explicitly check for shapeless
717 : * objects here, as they are not inserted into the cache and
718 : * will result in a miss.
719 : */
720 354584 : JSObject *last = cx->compartment->nativeIterCache.last;
721 354584 : JSObject *proto = obj->getProto();
722 354584 : if (last) {
723 66924 : NativeIterator *lastni = last->getNativeIterator();
724 376519 : if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
725 66149 : obj->isNative() &&
726 66134 : obj->lastProperty() == lastni->shapes_array[0] &&
727 59104 : proto && proto->isNative() &&
728 59104 : proto->lastProperty() == lastni->shapes_array[1] &&
729 59104 : !proto->getProto()) {
730 59104 : vp->setObject(*last);
731 59104 : UpdateNativeIterator(lastni, obj);
732 59104 : RegisterEnumerator(cx, last, lastni);
733 59104 : return true;
734 : }
735 : }
736 :
737 : /*
738 : * The iterator object for JSITER_ENUMERATE never escapes, so we
739 : * don't care for the proper parent/proto to be set. This also
740 : * allows us to re-use a previous iterator object that is not
741 : * currently active.
742 : */
743 295480 : JSObject *pobj = obj;
744 295132 : do {
745 1749793 : if (!pobj->isNative() ||
746 574283 : pobj->hasUncacheableProto() ||
747 296860 : obj->getOps()->enumerate ||
748 296748 : pobj->getClass()->enumerate != JS_EnumerateStub) {
749 286770 : shapes.clear();
750 286770 : goto miss;
751 : }
752 295132 : const Shape *shape = pobj->lastProperty();
753 295132 : key = (key + (key << 16)) ^ (uintptr_t(shape) >> 3);
754 295132 : if (!shapes.append((Shape *) shape))
755 0 : return false;
756 295132 : pobj = pobj->getProto();
757 : } while (pobj);
758 :
759 8710 : JSObject *iterobj = cx->compartment->nativeIterCache.get(key);
760 8710 : if (iterobj) {
761 6975 : NativeIterator *ni = iterobj->getNativeIterator();
762 20805 : if (!(ni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
763 : ni->shapes_key == key &&
764 6915 : ni->shapes_length == shapes.length() &&
765 6915 : Compare(ni->shapes_array, shapes.begin(), ni->shapes_length)) {
766 6915 : vp->setObject(*iterobj);
767 :
768 6915 : UpdateNativeIterator(ni, obj);
769 6915 : RegisterEnumerator(cx, iterobj, ni);
770 6915 : if (shapes.length() == 2)
771 6875 : cx->compartment->nativeIterCache.last = iterobj;
772 6915 : return true;
773 : }
774 : }
775 : }
776 :
777 : miss:
778 571792 : if (obj->isProxy()) {
779 81 : types::MarkIteratorUnknown(cx);
780 81 : return Proxy::iterate(cx, obj, flags, vp);
781 : }
782 571711 : if (!GetCustomIterator(cx, obj, flags, vp))
783 5963 : return false;
784 565748 : if (!vp->isUndefined()) {
785 207 : types::MarkIteratorUnknown(cx);
786 207 : return true;
787 : }
788 : }
789 :
790 : /* NB: for (var p in null) succeeds by iterating over no properties. */
791 :
792 1132154 : AutoIdVector keys(cx);
793 566077 : if (flags & JSITER_FOREACH) {
794 277421 : if (JS_LIKELY(obj != NULL) && !Snapshot(cx, obj, flags, &keys))
795 0 : return false;
796 277421 : JS_ASSERT(shapes.empty());
797 277421 : if (!VectorToValueIterator(cx, obj, flags, keys, vp))
798 0 : return false;
799 : } else {
800 288656 : if (JS_LIKELY(obj != NULL) && !Snapshot(cx, obj, flags, &keys))
801 0 : return false;
802 288656 : if (!VectorToKeyIterator(cx, obj, flags, keys, shapes.length(), key, vp))
803 9 : return false;
804 : }
805 :
806 566068 : JSObject *iterobj = &vp->toObject();
807 :
808 : /* Cache the iterator object if possible. */
809 566068 : if (shapes.length())
810 1675 : cx->compartment->nativeIterCache.set(key, iterobj);
811 :
812 566068 : if (shapes.length() == 2)
813 1505 : cx->compartment->nativeIterCache.last = iterobj;
814 566068 : return true;
815 : }
816 :
817 : }
818 :
819 : static JSObject *
820 10762 : iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly)
821 : {
822 10762 : return obj;
823 : }
824 :
825 : static JSBool
826 9414 : Iterator(JSContext *cx, unsigned argc, Value *vp)
827 : {
828 9414 : Value *argv = JS_ARGV(cx, vp);
829 9414 : bool keyonly = argc >= 2 ? js_ValueToBoolean(argv[1]) : false;
830 9414 : unsigned flags = JSITER_OWNONLY | (keyonly ? 0 : (JSITER_FOREACH | JSITER_KEYVALUE));
831 9414 : *vp = argc >= 1 ? argv[0] : UndefinedValue();
832 9414 : return ValueToIterator(cx, flags, vp);
833 : }
834 :
835 : JSBool
836 7183 : js_ThrowStopIteration(JSContext *cx)
837 : {
838 : Value v;
839 :
840 7183 : JS_ASSERT(!JS_IsExceptionPending(cx));
841 7183 : if (js_FindClassObject(cx, NULL, JSProto_StopIteration, &v))
842 7183 : cx->setPendingException(v);
843 7183 : return JS_FALSE;
844 : }
845 :
846 : static JSBool
847 117 : iterator_next(JSContext *cx, unsigned argc, Value *vp)
848 : {
849 117 : CallArgs args = CallArgsFromVp(argc, vp);
850 :
851 : bool ok;
852 117 : JSObject *obj = NonGenericMethodGuard(cx, args, iterator_next, &IteratorClass, &ok);
853 117 : if (!obj)
854 63 : return ok;
855 :
856 54 : if (!js_IteratorMore(cx, obj, &args.rval()))
857 0 : return false;
858 :
859 54 : if (!args.rval().toBoolean()) {
860 27 : js_ThrowStopIteration(cx);
861 27 : return false;
862 : }
863 :
864 27 : return js_IteratorNext(cx, obj, &args.rval());
865 : }
866 :
867 : #define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
868 :
869 : static JSFunctionSpec iterator_methods[] = {
870 : JS_FN(js_next_str, iterator_next, 0,JSPROP_ROPERM),
871 : JS_FS_END
872 : };
873 :
874 : #if JS_HAS_GENERATORS
875 : static JSBool
876 : CloseGenerator(JSContext *cx, JSObject *genobj);
877 : #endif
878 :
879 : /*
880 : * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
881 : * Otherwise construct the default iterator.
882 : */
883 : JSBool
884 650037 : js::ValueToIterator(JSContext *cx, unsigned flags, Value *vp)
885 : {
886 : /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
887 650037 : JS_ASSERT_IF(flags & JSITER_KEYVALUE, flags & JSITER_FOREACH);
888 :
889 : /*
890 : * Make sure the more/next state machine doesn't get stuck. A value might be
891 : * left in iterValue when a trace is left due to an operation time-out after
892 : * JSOP_MOREITER but before the value is picked up by FOR*.
893 : */
894 650037 : cx->iterValue.setMagic(JS_NO_ITER_VALUE);
895 :
896 : JSObject *obj;
897 650037 : if (vp->isObject()) {
898 : /* Common case. */
899 620498 : obj = &vp->toObject();
900 : } else {
901 : /*
902 : * Enumerating over null and undefined gives an empty enumerator.
903 : * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of
904 : * the first production in 12.6.4 and step 4 of the second production,
905 : * but it's "web JS" compatible. ES5 fixed for-in to match this de-facto
906 : * standard.
907 : */
908 29539 : if ((flags & JSITER_ENUMERATE)) {
909 23693 : if (!js_ValueToObjectOrNull(cx, *vp, &obj))
910 0 : return false;
911 : /* fall through */
912 : } else {
913 5846 : obj = js_ValueToNonNullObject(cx, *vp);
914 5846 : if (!obj)
915 36 : return false;
916 : }
917 : }
918 :
919 650001 : return GetIterator(cx, obj, flags, vp);
920 : }
921 :
922 : bool
923 335584 : js::CloseIterator(JSContext *cx, JSObject *obj)
924 : {
925 335584 : cx->iterValue.setMagic(JS_NO_ITER_VALUE);
926 :
927 335584 : if (obj->isIterator()) {
928 : /* Remove enumerators from the active list, which is a stack. */
929 327309 : NativeIterator *ni = obj->getNativeIterator();
930 :
931 327309 : if (ni->flags & JSITER_ENUMERATE) {
932 323651 : JS_ASSERT(cx->enumerators == obj);
933 323651 : cx->enumerators = ni->next;
934 :
935 323651 : JS_ASSERT(ni->flags & JSITER_ACTIVE);
936 323651 : ni->flags &= ~JSITER_ACTIVE;
937 :
938 : /*
939 : * Reset the enumerator; it may still be in the cached iterators
940 : * for this thread, and can be reused.
941 : */
942 323651 : ni->props_cursor = ni->props_array;
943 : }
944 : }
945 : #if JS_HAS_GENERATORS
946 8275 : else if (obj->isGenerator()) {
947 7167 : return CloseGenerator(cx, obj);
948 : }
949 : #endif
950 328417 : return JS_TRUE;
951 : }
952 :
953 : bool
954 142 : js::UnwindIteratorForException(JSContext *cx, JSObject *obj)
955 : {
956 142 : Value v = cx->getPendingException();
957 142 : cx->clearPendingException();
958 142 : if (!CloseIterator(cx, obj))
959 9 : return false;
960 133 : cx->setPendingException(v);
961 133 : return true;
962 : }
963 :
964 : void
965 0 : js::UnwindIteratorForUncatchableException(JSContext *cx, JSObject *obj)
966 : {
967 0 : if (obj->isIterator()) {
968 0 : NativeIterator *ni = obj->getNativeIterator();
969 0 : if (ni->flags & JSITER_ENUMERATE) {
970 0 : JS_ASSERT(cx->enumerators == obj);
971 0 : cx->enumerators = ni->next;
972 : }
973 : }
974 0 : }
975 :
976 : /*
977 : * Suppress enumeration of deleted properties. This function must be called
978 : * when a property is deleted and there might be active enumerators.
979 : *
980 : * We maintain a list of active non-escaping for-in enumerators. To suppress
981 : * a property, we check whether each active enumerator contains the (obj, id)
982 : * pair and has not yet enumerated |id|. If so, and |id| is the next property,
983 : * we simply advance the cursor. Otherwise, we delete |id| from the list.
984 : *
985 : * We do not suppress enumeration of a property deleted along an object's
986 : * prototype chain. Only direct deletions on the object are handled.
987 : *
988 : * This function can suppress multiple properties at once. The |predicate|
989 : * argument is an object which can be called on an id and returns true or
990 : * false. It also must have a method |matchesAtMostOne| which allows us to
991 : * stop searching after the first deletion if true.
992 : */
993 : template<typename StringPredicate>
994 : static bool
995 1713335 : SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, StringPredicate predicate)
996 : {
997 1713335 : JSObject *iterobj = cx->enumerators;
998 3511104 : while (iterobj) {
999 : again:
1000 84434 : NativeIterator *ni = iterobj->getNativeIterator();
1001 : /* This only works for identified surpressed keys, not values. */
1002 84434 : if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) {
1003 : /* Check whether id is still to come. */
1004 3854 : HeapPtr<JSFlatString> *props_cursor = ni->current();
1005 3854 : HeapPtr<JSFlatString> *props_end = ni->end();
1006 23777 : for (HeapPtr<JSFlatString> *idp = props_cursor; idp < props_end; ++idp) {
1007 20167 : if (predicate(*idp)) {
1008 : /*
1009 : * Check whether another property along the prototype chain
1010 : * became visible as a result of this deletion.
1011 : */
1012 244 : if (obj->getProto()) {
1013 244 : JSObject *proto = obj->getProto();
1014 : JSObject *obj2;
1015 : JSProperty *prop;
1016 : jsid id;
1017 244 : if (!ValueToId(cx, StringValue(*idp), &id))
1018 0 : return false;
1019 244 : id = js_CheckForStringIndex(id);
1020 244 : if (!proto->lookupGeneric(cx, id, &obj2, &prop))
1021 0 : return false;
1022 244 : if (prop) {
1023 : unsigned attrs;
1024 0 : if (obj2->isNative())
1025 0 : attrs = ((Shape *) prop)->attributes();
1026 0 : else if (!obj2->getGenericAttributes(cx, id, &attrs))
1027 0 : return false;
1028 :
1029 0 : if (attrs & JSPROP_ENUMERATE)
1030 0 : continue;
1031 : }
1032 : }
1033 :
1034 : /*
1035 : * If lookupProperty or getAttributes above removed a property from
1036 : * ni, start over.
1037 : */
1038 244 : if (props_end != ni->props_end || props_cursor != ni->props_cursor)
1039 : goto again;
1040 :
1041 : /*
1042 : * No property along the prototype chain stepped in to take the
1043 : * property's place, so go ahead and delete id from the list.
1044 : * If it is the next property to be enumerated, just skip it.
1045 : */
1046 244 : if (idp == props_cursor) {
1047 127 : ni->incCursor();
1048 : } else {
1049 162 : for (HeapPtr<JSFlatString> *p = idp; p + 1 != props_end; p++)
1050 45 : *p = *(p + 1);
1051 117 : ni->props_end = ni->end() - 1;
1052 :
1053 : /*
1054 : * Invoke the write barrier on this element, since it's
1055 : * no longer going to be marked.
1056 : */
1057 117 : ni->props_end->HeapPtr<JSFlatString>::~HeapPtr();
1058 : }
1059 :
1060 : /* Don't reuse modified native iterators. */
1061 244 : ni->flags |= JSITER_UNREUSABLE;
1062 :
1063 244 : if (predicate.matchesAtMostOne())
1064 244 : break;
1065 : }
1066 : }
1067 : }
1068 84434 : iterobj = ni->next;
1069 : }
1070 1713335 : return true;
1071 : }
1072 :
1073 : class SingleStringPredicate {
1074 : JSFlatString *str;
1075 : public:
1076 1713283 : SingleStringPredicate(JSFlatString *str) : str(str) {}
1077 :
1078 20167 : bool operator()(JSFlatString *str) { return EqualStrings(str, this->str); }
1079 244 : bool matchesAtMostOne() { return true; }
1080 : };
1081 :
1082 : bool
1083 1713283 : js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id)
1084 : {
1085 1713283 : JSFlatString *str = IdToString(cx, id);
1086 1713283 : if (!str)
1087 0 : return false;
1088 1713283 : return SuppressDeletedPropertyHelper(cx, obj, SingleStringPredicate(str));
1089 : }
1090 :
1091 : bool
1092 1612389 : js_SuppressDeletedElement(JSContext *cx, JSObject *obj, uint32_t index)
1093 : {
1094 : jsid id;
1095 1612389 : if (!IndexToId(cx, index, &id))
1096 0 : return false;
1097 1612389 : return js_SuppressDeletedProperty(cx, obj, id);
1098 : }
1099 :
1100 : class IndexRangePredicate {
1101 : uint32_t begin, end;
1102 :
1103 : public:
1104 52 : IndexRangePredicate(uint32_t begin, uint32_t end) : begin(begin), end(end) {}
1105 :
1106 0 : bool operator()(JSFlatString *str) {
1107 : uint32_t index;
1108 0 : return str->isIndex(&index) && begin <= index && index < end;
1109 : }
1110 :
1111 0 : bool matchesAtMostOne() { return false; }
1112 : };
1113 :
1114 : bool
1115 52 : js_SuppressDeletedElements(JSContext *cx, JSObject *obj, uint32_t begin, uint32_t end)
1116 : {
1117 52 : return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end));
1118 : }
1119 :
1120 : const uint32_t CLOSED_INDEX = UINT32_MAX;
1121 :
1122 : JSObject *
1123 927 : ElementIteratorObject::create(JSContext *cx, JSObject *obj)
1124 : {
1125 927 : JS_ASSERT(obj);
1126 927 : JSObject *iterobj = NewObjectWithGivenProto(cx, &ElementIteratorClass, NULL, obj);
1127 927 : if (iterobj) {
1128 927 : iterobj->setReservedSlot(TargetSlot, ObjectValue(*obj));
1129 927 : iterobj->setReservedSlot(IndexSlot, Int32Value(0));
1130 : }
1131 927 : return iterobj;
1132 : }
1133 :
1134 : inline uint32_t
1135 3942 : ElementIteratorObject::getIndex() const
1136 : {
1137 3942 : return uint32_t(getReservedSlot(IndexSlot).toInt32());
1138 : }
1139 :
1140 : inline JSObject *
1141 3942 : ElementIteratorObject::getTargetObject() const
1142 : {
1143 3942 : return &getReservedSlot(TargetSlot).toObject();
1144 : }
1145 :
1146 : inline void
1147 3942 : ElementIteratorObject::setIndex(uint32_t index)
1148 : {
1149 3942 : setReservedSlot(IndexSlot, Int32Value(int32_t(index)));
1150 3942 : }
1151 :
1152 : bool
1153 3942 : ElementIteratorObject::iteratorNext(JSContext *cx, Value *vp)
1154 : {
1155 : uint32_t i, length;
1156 3942 : JSObject *obj = getTargetObject();
1157 3942 : if (!js_GetLengthProperty(cx, obj, &length))
1158 0 : goto error;
1159 :
1160 3942 : i = getIndex();
1161 3942 : if (i >= length) {
1162 738 : setIndex(CLOSED_INDEX);
1163 738 : vp->setMagic(JS_NO_ITER_VALUE);
1164 738 : return true;
1165 : }
1166 :
1167 3204 : JS_ASSERT(i + 1 > i);
1168 3204 : if (!obj->getElement(cx, obj, i, vp))
1169 0 : goto error;
1170 :
1171 : /* On success, bump the index. */
1172 3204 : setIndex(i + 1);
1173 3204 : return true;
1174 :
1175 : error:
1176 0 : setIndex(CLOSED_INDEX);
1177 0 : return false;
1178 : }
1179 :
1180 : inline js::ElementIteratorObject *
1181 3942 : JSObject::asElementIterator()
1182 : {
1183 3942 : JS_ASSERT(isElementIterator());
1184 3942 : return static_cast<js::ElementIteratorObject *>(this);
1185 : }
1186 :
1187 : JSBool
1188 13094970 : js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval)
1189 : {
1190 : /* Fast path for native iterators */
1191 13094970 : NativeIterator *ni = NULL;
1192 13094970 : if (iterobj->isIterator()) {
1193 : /* Key iterators are handled by fast-paths. */
1194 13064000 : ni = iterobj->getNativeIterator();
1195 13064000 : bool more = ni->props_cursor < ni->props_end;
1196 13064000 : if (ni->isKeyIter() || !more) {
1197 256338 : rval->setBoolean(more);
1198 256338 : return true;
1199 : }
1200 : }
1201 :
1202 : /* We might still have a pending value. */
1203 12838632 : if (!cx->iterValue.isMagic(JS_NO_ITER_VALUE)) {
1204 0 : rval->setBoolean(true);
1205 0 : return true;
1206 : }
1207 :
1208 : /* We're reentering below and can call anything. */
1209 12838632 : JS_CHECK_RECURSION(cx, return false);
1210 :
1211 : /* Fetch and cache the next value from the iterator. */
1212 12838631 : if (ni) {
1213 12807662 : JS_ASSERT(!ni->isKeyIter());
1214 : jsid id;
1215 12807662 : if (!ValueToId(cx, StringValue(*ni->current()), &id))
1216 0 : return false;
1217 12807662 : id = js_CheckForStringIndex(id);
1218 12807662 : ni->incCursor();
1219 12807662 : if (!ni->obj->getGeneric(cx, id, rval))
1220 18 : return false;
1221 12807644 : if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *rval, rval))
1222 0 : return false;
1223 30969 : } else if (iterobj->isElementIterator()) {
1224 : /*
1225 : * Like native iterators, element iterators do not have a .next
1226 : * method, so this fast path is necessary for correctness.
1227 : */
1228 3942 : if (!iterobj->asElementIterator()->iteratorNext(cx, rval))
1229 0 : return false;
1230 3942 : if (rval->isMagic(JS_NO_ITER_VALUE)) {
1231 738 : cx->iterValue.setMagic(JS_NO_ITER_VALUE);
1232 738 : rval->setBoolean(false);
1233 738 : return true;
1234 : }
1235 27027 : } else if (iterobj->isProxy()) {
1236 54 : if (!Proxy::iteratorNext(cx, iterobj, rval))
1237 0 : return false;
1238 54 : if (rval->isMagic(JS_NO_ITER_VALUE)) {
1239 9 : rval->setBoolean(false);
1240 9 : return true;
1241 : }
1242 : } else {
1243 : /* Call the iterator object's .next method. */
1244 26973 : jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom);
1245 26973 : if (!js_GetMethod(cx, iterobj, id, JSGET_METHOD_BARRIER, rval))
1246 0 : return false;
1247 26973 : if (!Invoke(cx, ObjectValue(*iterobj), *rval, 0, NULL, rval)) {
1248 : /* Check for StopIteration. */
1249 7266 : if (!cx->isExceptionPending() || !IsStopIteration(cx->getPendingException()))
1250 22 : return false;
1251 :
1252 7244 : cx->clearPendingException();
1253 7244 : cx->iterValue.setMagic(JS_NO_ITER_VALUE);
1254 7244 : rval->setBoolean(false);
1255 7244 : return true;
1256 : }
1257 : }
1258 :
1259 : /* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */
1260 12830600 : JS_ASSERT(!rval->isMagic(JS_NO_ITER_VALUE));
1261 12830600 : cx->iterValue = *rval;
1262 12830600 : rval->setBoolean(true);
1263 12830600 : return true;
1264 : }
1265 :
1266 : JSBool
1267 12830555 : js_IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
1268 : {
1269 : /* Fast path for native iterators */
1270 12830555 : if (iterobj->isIterator()) {
1271 : /*
1272 : * Implement next directly as all the methods of the native iterator are
1273 : * read-only and permanent.
1274 : */
1275 12807644 : NativeIterator *ni = iterobj->getNativeIterator();
1276 12807644 : if (ni->isKeyIter()) {
1277 0 : JS_ASSERT(ni->props_cursor < ni->props_end);
1278 0 : *rval = StringValue(*ni->current());
1279 0 : ni->incCursor();
1280 :
1281 0 : if (rval->isString())
1282 0 : return true;
1283 :
1284 : JSString *str;
1285 : int i;
1286 0 : if (rval->isInt32() && StaticStrings::hasInt(i = rval->toInt32())) {
1287 0 : str = cx->runtime->staticStrings.getInt(i);
1288 : } else {
1289 0 : str = ToString(cx, *rval);
1290 0 : if (!str)
1291 0 : return false;
1292 : }
1293 :
1294 0 : rval->setString(str);
1295 0 : return true;
1296 : }
1297 : }
1298 :
1299 12830555 : JS_ASSERT(!cx->iterValue.isMagic(JS_NO_ITER_VALUE));
1300 12830555 : *rval = cx->iterValue;
1301 12830555 : cx->iterValue.setMagic(JS_NO_ITER_VALUE);
1302 :
1303 12830555 : return true;
1304 : }
1305 :
1306 : static JSBool
1307 63 : stopiter_hasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
1308 : {
1309 63 : *bp = IsStopIteration(*v);
1310 63 : return JS_TRUE;
1311 : }
1312 :
1313 : Class js::StopIterationClass = {
1314 : js_StopIteration_str,
1315 : JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration) |
1316 : JSCLASS_FREEZE_PROTO,
1317 : JS_PropertyStub, /* addProperty */
1318 : JS_PropertyStub, /* delProperty */
1319 : JS_PropertyStub, /* getProperty */
1320 : JS_StrictPropertyStub, /* setProperty */
1321 : JS_EnumerateStub,
1322 : JS_ResolveStub,
1323 : JS_ConvertStub,
1324 : NULL, /* finalize */
1325 : NULL, /* checkAccess */
1326 : NULL, /* call */
1327 : NULL, /* construct */
1328 : stopiter_hasInstance
1329 : };
1330 :
1331 : #if JS_HAS_GENERATORS
1332 :
1333 : static void
1334 14769 : generator_finalize(JSContext *cx, JSObject *obj)
1335 : {
1336 14769 : JSGenerator *gen = (JSGenerator *) obj->getPrivate();
1337 14769 : if (!gen)
1338 2506 : return;
1339 :
1340 : /*
1341 : * gen is open when a script has not called its close method while
1342 : * explicitly manipulating it.
1343 : */
1344 0 : JS_ASSERT(gen->state == JSGEN_NEWBORN ||
1345 : gen->state == JSGEN_CLOSED ||
1346 12263 : gen->state == JSGEN_OPEN);
1347 12263 : cx->free_(gen);
1348 : }
1349 :
1350 : static void
1351 2880 : MarkGenerator(JSTracer *trc, JSGenerator *gen)
1352 : {
1353 2880 : StackFrame *fp = gen->floatingFrame();
1354 :
1355 : /*
1356 : * MarkGenerator should only be called when regs is based on the floating frame.
1357 : * See calls to RebaseRegsFromTo.
1358 : */
1359 2880 : JS_ASSERT(size_t(gen->regs.sp - fp->slots()) <= fp->numSlots());
1360 :
1361 : /*
1362 : * Currently, generators are not mjitted. Still, (overflow) args can be
1363 : * pushed by the mjit and need to be conservatively marked. Technically, the
1364 : * formal args and generator slots are safe for exact marking, but since the
1365 : * plan is to eventually mjit generators, it makes sense to future-proof
1366 : * this code and save someone an hour later.
1367 : */
1368 2880 : MarkValueRange(trc, (HeapValue *)fp->formalArgsEnd() - gen->floatingStack,
1369 5760 : gen->floatingStack, "Generator Floating Args");
1370 2880 : fp->mark(trc);
1371 2880 : MarkValueRange(trc, gen->regs.sp - fp->slots(),
1372 5760 : (HeapValue *)fp->slots(), "Generator Floating Stack");
1373 2880 : }
1374 :
1375 : static void
1376 30834 : GeneratorWriteBarrierPre(JSContext *cx, JSGenerator *gen)
1377 : {
1378 30834 : JSCompartment *comp = cx->compartment;
1379 30834 : if (comp->needsBarrier())
1380 18 : MarkGenerator(comp->barrierTracer(), gen);
1381 30834 : }
1382 :
1383 : static void
1384 241970 : generator_trace(JSTracer *trc, JSObject *obj)
1385 : {
1386 241970 : JSGenerator *gen = (JSGenerator *) obj->getPrivate();
1387 241970 : if (!gen)
1388 21185 : return;
1389 :
1390 : /*
1391 : * Do not mark if the generator is running; the contents may be trash and
1392 : * will be replaced when the generator stops.
1393 : */
1394 220785 : if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING)
1395 217923 : return;
1396 :
1397 2862 : JS_ASSERT(gen->liveFrame() == gen->floatingFrame());
1398 2862 : MarkGenerator(trc, gen);
1399 : }
1400 :
1401 : Class js::GeneratorClass = {
1402 : "Generator",
1403 : JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
1404 : JS_PropertyStub, /* addProperty */
1405 : JS_PropertyStub, /* delProperty */
1406 : JS_PropertyStub, /* getProperty */
1407 : JS_StrictPropertyStub, /* setProperty */
1408 : JS_EnumerateStub,
1409 : JS_ResolveStub,
1410 : JS_ConvertStub,
1411 : generator_finalize,
1412 : NULL, /* checkAccess */
1413 : NULL, /* call */
1414 : NULL, /* construct */
1415 : NULL, /* hasInstance */
1416 : generator_trace,
1417 : {
1418 : NULL, /* equality */
1419 : NULL, /* outerObject */
1420 : NULL, /* innerObject */
1421 : iterator_iterator,
1422 : NULL /* unused */
1423 : }
1424 : };
1425 :
1426 : /*
1427 : * Called from the JSOP_GENERATOR case in the interpreter, with fp referring
1428 : * to the frame by which the generator function was activated. Create a new
1429 : * JSGenerator object, which contains its own StackFrame that we populate
1430 : * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return
1431 : * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
1432 : * if they are non-null.
1433 : */
1434 : JSObject *
1435 12263 : js_NewGenerator(JSContext *cx)
1436 : {
1437 12263 : FrameRegs &stackRegs = cx->regs();
1438 12263 : StackFrame *stackfp = stackRegs.fp();
1439 12263 : JS_ASSERT(stackfp->base() == cx->regs().sp);
1440 12263 : JS_ASSERT(stackfp->actualArgs() <= stackfp->formalArgs());
1441 :
1442 12263 : GlobalObject *global = &stackfp->scopeChain().global();
1443 12263 : JSObject *proto = global->getOrCreateGeneratorPrototype(cx);
1444 12263 : if (!proto)
1445 0 : return NULL;
1446 12263 : JSObject *obj = NewObjectWithGivenProto(cx, &GeneratorClass, proto, global);
1447 12263 : if (!obj)
1448 0 : return NULL;
1449 :
1450 : /* Load and compute stack slot counts. */
1451 12263 : Value *stackvp = stackfp->actualArgs() - 2;
1452 12263 : unsigned vplen = stackfp->formalArgsEnd() - stackvp;
1453 :
1454 : /* Compute JSGenerator size. */
1455 : unsigned nbytes = sizeof(JSGenerator) +
1456 : (-1 + /* one Value included in JSGenerator */
1457 : vplen +
1458 : VALUES_PER_STACK_FRAME +
1459 12263 : stackfp->numSlots()) * sizeof(HeapValue);
1460 :
1461 12263 : JS_ASSERT(nbytes % sizeof(Value) == 0);
1462 : JS_STATIC_ASSERT(sizeof(StackFrame) % sizeof(HeapValue) == 0);
1463 :
1464 12263 : JSGenerator *gen = (JSGenerator *) cx->malloc_(nbytes);
1465 12263 : if (!gen)
1466 0 : return NULL;
1467 12263 : SetValueRangeToUndefined((Value *)gen, nbytes / sizeof(Value));
1468 :
1469 : /* Cut up floatingStack space. */
1470 12263 : HeapValue *genvp = gen->floatingStack;
1471 12263 : StackFrame *genfp = reinterpret_cast<StackFrame *>(genvp + vplen);
1472 :
1473 : /* Initialize JSGenerator. */
1474 12263 : gen->obj.init(obj);
1475 12263 : gen->state = JSGEN_NEWBORN;
1476 12263 : gen->enumerators = NULL;
1477 12263 : gen->floating = genfp;
1478 :
1479 : /* Copy from the stack to the generator's floating frame. */
1480 12263 : gen->regs.rebaseFromTo(stackRegs, *genfp);
1481 : genfp->stealFrameAndSlots<HeapValue, Value, StackFrame::DoPostBarrier>(
1482 12263 : genfp, genvp, stackfp, stackvp, stackRegs.sp);
1483 12263 : genfp->initFloatingGenerator();
1484 :
1485 12263 : obj->setPrivate(gen);
1486 12263 : return obj;
1487 : }
1488 :
1489 : JSGenerator *
1490 33338 : js_FloatingFrameToGenerator(StackFrame *fp)
1491 : {
1492 33338 : JS_ASSERT(fp->isGeneratorFrame() && fp->isFloatingGenerator());
1493 33338 : char *floatingStackp = (char *)(fp->actualArgs() - 2);
1494 33338 : char *p = floatingStackp - offsetof(JSGenerator, floatingStack);
1495 33338 : return reinterpret_cast<JSGenerator *>(p);
1496 : }
1497 :
1498 : typedef enum JSGeneratorOp {
1499 : JSGENOP_NEXT,
1500 : JSGENOP_SEND,
1501 : JSGENOP_THROW,
1502 : JSGENOP_CLOSE
1503 : } JSGeneratorOp;
1504 :
1505 : /*
1506 : * Start newborn or restart yielding generator and perform the requested
1507 : * operation inside its frame.
1508 : */
1509 : static JSBool
1510 30834 : SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
1511 : JSGenerator *gen, const Value &arg)
1512 : {
1513 30834 : if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
1514 0 : js_ReportValueError(cx, JSMSG_NESTING_GENERATOR,
1515 : JSDVG_SEARCH_STACK, ObjectOrNullValue(obj),
1516 0 : JS_GetFunctionId(gen->floatingFrame()->fun()));
1517 0 : return JS_FALSE;
1518 : }
1519 :
1520 : /* Check for OOM errors here, where we can fail easily. */
1521 30834 : if (!cx->ensureGeneratorStackSpace())
1522 0 : return JS_FALSE;
1523 :
1524 : /*
1525 : * Write barrier is needed since the generator stack can be updated,
1526 : * and it's not barriered in any other way. We need to do it before
1527 : * gen->state changes, which can cause us to trace the generator
1528 : * differently.
1529 : *
1530 : * We could optimize this by setting a bit on the generator to signify
1531 : * that it has been marked. If this bit has already been set, there is no
1532 : * need to mark again. The bit would have to be reset before the next GC,
1533 : * or else some kind of epoch scheme would have to be used.
1534 : */
1535 30834 : GeneratorWriteBarrierPre(cx, gen);
1536 :
1537 30834 : JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
1538 30834 : switch (op) {
1539 : case JSGENOP_NEXT:
1540 : case JSGENOP_SEND:
1541 29329 : if (gen->state == JSGEN_OPEN) {
1542 : /*
1543 : * Store the argument to send as the result of the yield
1544 : * expression.
1545 : */
1546 21495 : gen->regs.sp[-1] = arg;
1547 : }
1548 29329 : gen->state = JSGEN_RUNNING;
1549 29329 : break;
1550 :
1551 : case JSGENOP_THROW:
1552 0 : cx->setPendingException(arg);
1553 0 : gen->state = JSGEN_RUNNING;
1554 0 : break;
1555 :
1556 : default:
1557 1505 : JS_ASSERT(op == JSGENOP_CLOSE);
1558 1505 : cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
1559 1505 : gen->state = JSGEN_CLOSING;
1560 1505 : break;
1561 : }
1562 :
1563 30834 : StackFrame *genfp = gen->floatingFrame();
1564 :
1565 : JSBool ok;
1566 : {
1567 61668 : GeneratorFrameGuard gfg;
1568 30834 : if (!cx->stack.pushGeneratorFrame(cx, gen, &gfg)) {
1569 0 : gen->state = JSGEN_CLOSED;
1570 0 : return JS_FALSE;
1571 : }
1572 :
1573 30834 : StackFrame *fp = gfg.fp();
1574 30834 : gen->regs = cx->regs();
1575 30834 : JS_ASSERT(gen->liveFrame() == fp);
1576 :
1577 30834 : cx->enterGenerator(gen); /* OOM check above. */
1578 30834 : JSObject *enumerators = cx->enumerators;
1579 30834 : cx->enumerators = gen->enumerators;
1580 :
1581 30834 : ok = RunScript(cx, fp->script(), fp);
1582 :
1583 30834 : gen->enumerators = cx->enumerators;
1584 30834 : cx->enumerators = enumerators;
1585 61668 : cx->leaveGenerator(gen);
1586 : }
1587 :
1588 30834 : if (gen->floatingFrame()->isYielding()) {
1589 : /* Yield cannot fail, throw or be called on closing. */
1590 23131 : JS_ASSERT(ok);
1591 23131 : JS_ASSERT(!cx->isExceptionPending());
1592 23131 : JS_ASSERT(gen->state == JSGEN_RUNNING);
1593 23131 : JS_ASSERT(op != JSGENOP_CLOSE);
1594 23131 : genfp->clearYielding();
1595 23131 : gen->state = JSGEN_OPEN;
1596 23131 : return JS_TRUE;
1597 : }
1598 :
1599 7703 : genfp->clearReturnValue();
1600 7703 : gen->state = JSGEN_CLOSED;
1601 7703 : if (ok) {
1602 : /* Returned, explicitly or by falling off the end. */
1603 7661 : if (op == JSGENOP_CLOSE)
1604 1496 : return JS_TRUE;
1605 6165 : return js_ThrowStopIteration(cx);
1606 : }
1607 :
1608 : /*
1609 : * An error, silent termination by operation callback or an exception.
1610 : * Propagate the condition to the caller.
1611 : */
1612 42 : return JS_FALSE;
1613 : }
1614 :
1615 : static JSBool
1616 7167 : CloseGenerator(JSContext *cx, JSObject *obj)
1617 : {
1618 7167 : JS_ASSERT(obj->isGenerator());
1619 :
1620 7167 : JSGenerator *gen = (JSGenerator *) obj->getPrivate();
1621 7167 : if (!gen) {
1622 : /* Generator prototype object. */
1623 0 : return JS_TRUE;
1624 : }
1625 :
1626 7167 : if (gen->state == JSGEN_CLOSED)
1627 7085 : return JS_TRUE;
1628 :
1629 82 : return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, UndefinedValue());
1630 : }
1631 :
1632 : /*
1633 : * Common subroutine of generator_(next|send|throw|close) methods.
1634 : */
1635 : static JSBool
1636 31818 : generator_op(JSContext *cx, Native native, JSGeneratorOp op, Value *vp, unsigned argc)
1637 : {
1638 31818 : CallArgs args = CallArgsFromVp(argc, vp);
1639 :
1640 : bool ok;
1641 31818 : JSObject *obj = NonGenericMethodGuard(cx, args, native, &GeneratorClass, &ok);
1642 31818 : if (!obj)
1643 63 : return ok;
1644 :
1645 31755 : JSGenerator *gen = (JSGenerator *) obj->getPrivate();
1646 31755 : if (!gen) {
1647 : /* This happens when obj is the generator prototype. See bug 352885. */
1648 0 : goto closed_generator;
1649 : }
1650 :
1651 31755 : if (gen->state == JSGEN_NEWBORN) {
1652 7834 : switch (op) {
1653 : case JSGENOP_NEXT:
1654 : case JSGENOP_THROW:
1655 7834 : break;
1656 :
1657 : case JSGENOP_SEND:
1658 0 : if (args.hasDefined(0)) {
1659 0 : js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
1660 0 : JSDVG_SEARCH_STACK, args[0], NULL);
1661 0 : return false;
1662 : }
1663 0 : break;
1664 :
1665 : default:
1666 0 : JS_ASSERT(op == JSGENOP_CLOSE);
1667 0 : gen->state = JSGEN_CLOSED;
1668 0 : args.rval().setUndefined();
1669 0 : return true;
1670 : }
1671 23921 : } else if (gen->state == JSGEN_CLOSED) {
1672 : closed_generator:
1673 1003 : switch (op) {
1674 : case JSGENOP_NEXT:
1675 : case JSGENOP_SEND:
1676 990 : return js_ThrowStopIteration(cx);
1677 : case JSGENOP_THROW:
1678 0 : cx->setPendingException(args.length() >= 1 ? args[0] : UndefinedValue());
1679 0 : return false;
1680 : default:
1681 13 : JS_ASSERT(op == JSGENOP_CLOSE);
1682 13 : args.rval().setUndefined();
1683 13 : return true;
1684 : }
1685 : }
1686 :
1687 30752 : bool undef = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && args.length() != 0);
1688 30752 : if (!SendToGenerator(cx, op, obj, gen, undef ? args[0] : UndefinedValue()))
1689 6198 : return false;
1690 :
1691 24554 : args.rval() = gen->floatingFrame()->returnValue();
1692 24554 : return true;
1693 : }
1694 :
1695 : static JSBool
1696 1012 : generator_send(JSContext *cx, unsigned argc, Value *vp)
1697 : {
1698 1012 : return generator_op(cx, generator_send, JSGENOP_SEND, vp, argc);
1699 : }
1700 :
1701 : static JSBool
1702 29370 : generator_next(JSContext *cx, unsigned argc, Value *vp)
1703 : {
1704 29370 : return generator_op(cx, generator_next, JSGENOP_NEXT, vp, argc);
1705 : }
1706 :
1707 : static JSBool
1708 0 : generator_throw(JSContext *cx, unsigned argc, Value *vp)
1709 : {
1710 0 : return generator_op(cx, generator_throw, JSGENOP_THROW, vp, argc);
1711 : }
1712 :
1713 : static JSBool
1714 1436 : generator_close(JSContext *cx, unsigned argc, Value *vp)
1715 : {
1716 1436 : return generator_op(cx, generator_close, JSGENOP_CLOSE, vp, argc);
1717 : }
1718 :
1719 : static JSFunctionSpec generator_methods[] = {
1720 : JS_FN(js_next_str, generator_next, 0,JSPROP_ROPERM),
1721 : JS_FN(js_send_str, generator_send, 1,JSPROP_ROPERM),
1722 : JS_FN(js_throw_str, generator_throw, 1,JSPROP_ROPERM),
1723 : JS_FN(js_close_str, generator_close, 0,JSPROP_ROPERM),
1724 : JS_FS_END
1725 : };
1726 :
1727 : #endif /* JS_HAS_GENERATORS */
1728 :
1729 : static bool
1730 2507 : InitIteratorClass(JSContext *cx, GlobalObject *global)
1731 : {
1732 2507 : JSObject *iteratorProto = global->createBlankPrototype(cx, &IteratorClass);
1733 2507 : if (!iteratorProto)
1734 0 : return false;
1735 :
1736 5014 : AutoIdVector blank(cx);
1737 2507 : NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, blank);
1738 2507 : if (!ni)
1739 0 : return false;
1740 2507 : ni->init(NULL, 0 /* flags */, 0, 0);
1741 :
1742 2507 : iteratorProto->setNativeIterator(ni);
1743 :
1744 : JSFunction *ctor = global->createConstructor(cx, Iterator, &IteratorClass,
1745 2507 : CLASS_ATOM(cx, Iterator), 2);
1746 2507 : if (!ctor)
1747 0 : return false;
1748 :
1749 2507 : if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto))
1750 0 : return false;
1751 :
1752 2507 : if (!DefinePropertiesAndBrand(cx, iteratorProto, NULL, iterator_methods))
1753 0 : return false;
1754 :
1755 2507 : return DefineConstructorAndPrototype(cx, global, JSProto_Iterator, ctor, iteratorProto);
1756 : }
1757 :
1758 : bool
1759 2507 : GlobalObject::initGeneratorClass(JSContext *cx)
1760 : {
1761 : #if JS_HAS_GENERATORS
1762 2507 : JSObject *proto = createBlankPrototype(cx, &GeneratorClass);
1763 2507 : if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, generator_methods))
1764 0 : return false;
1765 2507 : setReservedSlot(GENERATOR_PROTO, ObjectValue(*proto));
1766 : #endif
1767 2507 : return true;
1768 : }
1769 :
1770 : static JSObject *
1771 2507 : InitStopIterationClass(JSContext *cx, GlobalObject *global)
1772 : {
1773 2507 : JSObject *proto = global->createBlankPrototype(cx, &StopIterationClass);
1774 2507 : if (!proto || !proto->freeze(cx))
1775 0 : return NULL;
1776 :
1777 : /* This should use a non-JSProtoKey'd slot, but this is easier for now. */
1778 2507 : if (!DefineConstructorAndPrototype(cx, global, JSProto_StopIteration, proto, proto))
1779 0 : return NULL;
1780 :
1781 2507 : MarkStandardClassInitializedNoProto(global, &StopIterationClass);
1782 :
1783 2507 : return proto;
1784 : }
1785 :
1786 : JSObject *
1787 4678 : js_InitIteratorClasses(JSContext *cx, JSObject *obj)
1788 : {
1789 4678 : JS_ASSERT(obj->isNative());
1790 :
1791 4678 : GlobalObject *global = &obj->asGlobal();
1792 :
1793 : /*
1794 : * Bail if Iterator has already been initialized. We test for Iterator
1795 : * rather than for StopIteration because if js_InitIteratorClasses recurs,
1796 : * as happens when the StopIteration object is frozen, initializing the
1797 : * Iterator class a second time will assert.
1798 : */
1799 : JSObject *iter;
1800 4678 : if (!js_GetClassObject(cx, global, JSProto_Iterator, &iter))
1801 0 : return NULL;
1802 4678 : if (iter)
1803 2171 : return iter;
1804 :
1805 2507 : if (!InitIteratorClass(cx, global) || !global->initGeneratorClass(cx))
1806 0 : return NULL;
1807 2507 : return InitStopIterationClass(cx, global);
1808 : }
|