js/src/jsiter.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /* JavaScript iterators. */
michael@0 8
michael@0 9 #include "jsiter.h"
michael@0 10
michael@0 11 #include "mozilla/ArrayUtils.h"
michael@0 12 #include "mozilla/MemoryReporting.h"
michael@0 13 #include "mozilla/PodOperations.h"
michael@0 14
michael@0 15 #include "jsarray.h"
michael@0 16 #include "jsatom.h"
michael@0 17 #include "jscntxt.h"
michael@0 18 #include "jsgc.h"
michael@0 19 #include "jsobj.h"
michael@0 20 #include "jsopcode.h"
michael@0 21 #include "jsproxy.h"
michael@0 22 #include "jsscript.h"
michael@0 23 #include "jstypes.h"
michael@0 24 #include "jsutil.h"
michael@0 25
michael@0 26 #include "ds/Sort.h"
michael@0 27 #include "gc/Marking.h"
michael@0 28 #include "vm/GeneratorObject.h"
michael@0 29 #include "vm/GlobalObject.h"
michael@0 30 #include "vm/Interpreter.h"
michael@0 31 #include "vm/Shape.h"
michael@0 32 #include "vm/StopIterationObject.h"
michael@0 33
michael@0 34 #include "jsinferinlines.h"
michael@0 35 #include "jsobjinlines.h"
michael@0 36 #include "jsscriptinlines.h"
michael@0 37
michael@0 38 #include "vm/Stack-inl.h"
michael@0 39 #include "vm/String-inl.h"
michael@0 40
michael@0 41 using namespace js;
michael@0 42 using namespace js::gc;
michael@0 43 using JS::ForOfIterator;
michael@0 44
michael@0 45 using mozilla::ArrayLength;
michael@0 46 #ifdef JS_MORE_DETERMINISTIC
michael@0 47 using mozilla::PodCopy;
michael@0 48 #endif
michael@0 49 using mozilla::PodZero;
michael@0 50
michael@0 51 typedef Rooted<PropertyIteratorObject*> RootedPropertyIteratorObject;
michael@0 52
michael@0 53 static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::FINALIZE_OBJECT2_BACKGROUND;
michael@0 54
michael@0 55 void
michael@0 56 NativeIterator::mark(JSTracer *trc)
michael@0 57 {
michael@0 58 for (HeapPtr<JSFlatString> *str = begin(); str < end(); str++)
michael@0 59 MarkString(trc, str, "prop");
michael@0 60 if (obj)
michael@0 61 MarkObject(trc, &obj, "obj");
michael@0 62
michael@0 63 // The SuppressDeletedPropertyHelper loop can GC, so make sure that if the
michael@0 64 // GC removes any elements from the list, it won't remove this one.
michael@0 65 if (iterObj_)
michael@0 66 MarkObjectUnbarriered(trc, &iterObj_, "iterObj");
michael@0 67 }
michael@0 68
michael@0 69 struct IdHashPolicy {
michael@0 70 typedef jsid Lookup;
michael@0 71 static HashNumber hash(jsid id) {
michael@0 72 return JSID_BITS(id);
michael@0 73 }
michael@0 74 static bool match(jsid id1, jsid id2) {
michael@0 75 return id1 == id2;
michael@0 76 }
michael@0 77 };
michael@0 78
michael@0 79 typedef HashSet<jsid, IdHashPolicy> IdSet;
michael@0 80
michael@0 81 static inline bool
michael@0 82 NewKeyValuePair(JSContext *cx, jsid id, const Value &val, MutableHandleValue rval)
michael@0 83 {
michael@0 84 JS::AutoValueArray<2> vec(cx);
michael@0 85 vec[0].set(IdToValue(id));
michael@0 86 vec[1].set(val);
michael@0 87
michael@0 88 JSObject *aobj = NewDenseCopiedArray(cx, 2, vec.begin());
michael@0 89 if (!aobj)
michael@0 90 return false;
michael@0 91 rval.setObject(*aobj);
michael@0 92 return true;
michael@0 93 }
michael@0 94
michael@0 95 static inline bool
michael@0 96 Enumerate(JSContext *cx, HandleObject pobj, jsid id,
michael@0 97 bool enumerable, unsigned flags, IdSet& ht, AutoIdVector *props)
michael@0 98 {
michael@0 99 /*
michael@0 100 * We implement __proto__ using a property on |Object.prototype|, but
michael@0 101 * because __proto__ is highly deserving of removal, we don't want it to
michael@0 102 * show up in property enumeration, even if only for |Object.prototype|
michael@0 103 * (think introspection by Prototype-like frameworks that add methods to
michael@0 104 * the built-in prototypes). So exclude __proto__ if the object where the
michael@0 105 * property was found has no [[Prototype]] and might be |Object.prototype|.
michael@0 106 */
michael@0 107 if (MOZ_UNLIKELY(!pobj->getTaggedProto().isObject() && JSID_IS_ATOM(id, cx->names().proto)))
michael@0 108 return true;
michael@0 109
michael@0 110 if (!(flags & JSITER_OWNONLY) || pobj->is<ProxyObject>() || pobj->getOps()->enumerate) {
michael@0 111 /* If we've already seen this, we definitely won't add it. */
michael@0 112 IdSet::AddPtr p = ht.lookupForAdd(id);
michael@0 113 if (MOZ_UNLIKELY(!!p))
michael@0 114 return true;
michael@0 115
michael@0 116 /*
michael@0 117 * It's not necessary to add properties to the hash table at the end of
michael@0 118 * the prototype chain, but custom enumeration behaviors might return
michael@0 119 * duplicated properties, so always add in such cases.
michael@0 120 */
michael@0 121 if ((pobj->is<ProxyObject>() || pobj->getProto() || pobj->getOps()->enumerate) && !ht.add(p, id))
michael@0 122 return false;
michael@0 123 }
michael@0 124
michael@0 125 if (enumerable || (flags & JSITER_HIDDEN))
michael@0 126 return props->append(id);
michael@0 127
michael@0 128 return true;
michael@0 129 }
michael@0 130
michael@0 131 static bool
michael@0 132 EnumerateNativeProperties(JSContext *cx, HandleObject pobj, unsigned flags, IdSet &ht,
michael@0 133 AutoIdVector *props)
michael@0 134 {
michael@0 135 /* Collect any dense elements from this object. */
michael@0 136 size_t initlen = pobj->getDenseInitializedLength();
michael@0 137 const Value *vp = pobj->getDenseElements();
michael@0 138 for (size_t i = 0; i < initlen; ++i, ++vp) {
michael@0 139 if (!vp->isMagic(JS_ELEMENTS_HOLE)) {
michael@0 140 /* Dense arrays never get so large that i would not fit into an integer id. */
michael@0 141 if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props))
michael@0 142 return false;
michael@0 143 }
michael@0 144 }
michael@0 145
michael@0 146 /* Collect any typed array elements from this object. */
michael@0 147 if (pobj->is<TypedArrayObject>()) {
michael@0 148 size_t len = pobj->as<TypedArrayObject>().length();
michael@0 149 for (size_t i = 0; i < len; i++) {
michael@0 150 if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props))
michael@0 151 return false;
michael@0 152 }
michael@0 153 }
michael@0 154
michael@0 155 size_t initialLength = props->length();
michael@0 156
michael@0 157 /* Collect all unique properties from this object's scope. */
michael@0 158 Shape::Range<NoGC> r(pobj->lastProperty());
michael@0 159 for (; !r.empty(); r.popFront()) {
michael@0 160 Shape &shape = r.front();
michael@0 161
michael@0 162 if (!Enumerate(cx, pobj, shape.propid(), shape.enumerable(), flags, ht, props))
michael@0 163 return false;
michael@0 164 }
michael@0 165
michael@0 166 ::Reverse(props->begin() + initialLength, props->end());
michael@0 167 return true;
michael@0 168 }
michael@0 169
michael@0 170 #ifdef JS_MORE_DETERMINISTIC
michael@0 171
michael@0 172 struct SortComparatorIds
michael@0 173 {
michael@0 174 JSContext *const cx;
michael@0 175
michael@0 176 SortComparatorIds(JSContext *cx)
michael@0 177 : cx(cx) {}
michael@0 178
michael@0 179 bool operator()(jsid a, jsid b, bool *lessOrEqualp)
michael@0 180 {
michael@0 181 /* Pick an arbitrary total order on jsids that is stable across executions. */
michael@0 182 RootedString astr(cx, IdToString(cx, a));
michael@0 183 if (!astr)
michael@0 184 return false;
michael@0 185 RootedString bstr(cx, IdToString(cx, b));
michael@0 186 if (!bstr)
michael@0 187 return false;
michael@0 188
michael@0 189 int32_t result;
michael@0 190 if (!CompareStrings(cx, astr, bstr, &result))
michael@0 191 return false;
michael@0 192
michael@0 193 *lessOrEqualp = (result <= 0);
michael@0 194 return true;
michael@0 195 }
michael@0 196 };
michael@0 197
michael@0 198 #endif /* JS_MORE_DETERMINISTIC */
michael@0 199
michael@0 200 static bool
michael@0 201 Snapshot(JSContext *cx, JSObject *pobj_, unsigned flags, AutoIdVector *props)
michael@0 202 {
michael@0 203 IdSet ht(cx);
michael@0 204 if (!ht.init(32))
michael@0 205 return false;
michael@0 206
michael@0 207 RootedObject pobj(cx, pobj_);
michael@0 208
michael@0 209 do {
michael@0 210 const Class *clasp = pobj->getClass();
michael@0 211 if (pobj->isNative() &&
michael@0 212 !pobj->getOps()->enumerate &&
michael@0 213 !(clasp->flags & JSCLASS_NEW_ENUMERATE)) {
michael@0 214 if (!clasp->enumerate(cx, pobj))
michael@0 215 return false;
michael@0 216 if (!EnumerateNativeProperties(cx, pobj, flags, ht, props))
michael@0 217 return false;
michael@0 218 } else {
michael@0 219 if (pobj->is<ProxyObject>()) {
michael@0 220 AutoIdVector proxyProps(cx);
michael@0 221 if (flags & JSITER_OWNONLY) {
michael@0 222 if (flags & JSITER_HIDDEN) {
michael@0 223 if (!Proxy::getOwnPropertyNames(cx, pobj, proxyProps))
michael@0 224 return false;
michael@0 225 } else {
michael@0 226 if (!Proxy::keys(cx, pobj, proxyProps))
michael@0 227 return false;
michael@0 228 }
michael@0 229 } else {
michael@0 230 if (!Proxy::enumerate(cx, pobj, proxyProps))
michael@0 231 return false;
michael@0 232 }
michael@0 233 for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
michael@0 234 if (!Enumerate(cx, pobj, proxyProps[n], true, flags, ht, props))
michael@0 235 return false;
michael@0 236 }
michael@0 237 /* Proxy objects enumerate the prototype on their own, so we are done here. */
michael@0 238 break;
michael@0 239 }
michael@0 240 RootedValue state(cx);
michael@0 241 RootedId id(cx);
michael@0 242 JSIterateOp op = (flags & JSITER_HIDDEN) ? JSENUMERATE_INIT_ALL : JSENUMERATE_INIT;
michael@0 243 if (!JSObject::enumerate(cx, pobj, op, &state, &id))
michael@0 244 return false;
michael@0 245 if (state.isMagic(JS_NATIVE_ENUMERATE)) {
michael@0 246 if (!EnumerateNativeProperties(cx, pobj, flags, ht, props))
michael@0 247 return false;
michael@0 248 } else {
michael@0 249 while (true) {
michael@0 250 RootedId id(cx);
michael@0 251 if (!JSObject::enumerate(cx, pobj, JSENUMERATE_NEXT, &state, &id))
michael@0 252 return false;
michael@0 253 if (state.isNull())
michael@0 254 break;
michael@0 255 if (!Enumerate(cx, pobj, id, true, flags, ht, props))
michael@0 256 return false;
michael@0 257 }
michael@0 258 }
michael@0 259 }
michael@0 260
michael@0 261 if (flags & JSITER_OWNONLY)
michael@0 262 break;
michael@0 263
michael@0 264 } while ((pobj = pobj->getProto()) != nullptr);
michael@0 265
michael@0 266 #ifdef JS_MORE_DETERMINISTIC
michael@0 267
michael@0 268 /*
michael@0 269 * In some cases the enumeration order for an object depends on the
michael@0 270 * execution mode (interpreter vs. JIT), especially for native objects
michael@0 271 * with a class enumerate hook (where resolving a property changes the
michael@0 272 * resulting enumeration order). These aren't really bugs, but the
michael@0 273 * differences can change the generated output and confuse correctness
michael@0 274 * fuzzers, so we sort the ids if such a fuzzer is running.
michael@0 275 *
michael@0 276 * We don't do this in the general case because (a) doing so is slow,
michael@0 277 * and (b) it also breaks the web, which expects enumeration order to
michael@0 278 * follow the order in which properties are added, in certain cases.
michael@0 279 * Since ECMA does not specify an enumeration order for objects, both
michael@0 280 * behaviors are technically correct to do.
michael@0 281 */
michael@0 282
michael@0 283 jsid *ids = props->begin();
michael@0 284 size_t n = props->length();
michael@0 285
michael@0 286 AutoIdVector tmp(cx);
michael@0 287 if (!tmp.resize(n))
michael@0 288 return false;
michael@0 289 PodCopy(tmp.begin(), ids, n);
michael@0 290
michael@0 291 if (!MergeSort(ids, n, tmp.begin(), SortComparatorIds(cx)))
michael@0 292 return false;
michael@0 293
michael@0 294 #endif /* JS_MORE_DETERMINISTIC */
michael@0 295
michael@0 296 return true;
michael@0 297 }
michael@0 298
michael@0 299 bool
michael@0 300 js::VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap)
michael@0 301 {
michael@0 302 JS_STATIC_ASSERT(sizeof(JSIdArray) > sizeof(jsid));
michael@0 303 size_t len = props.length();
michael@0 304 size_t idsz = len * sizeof(jsid);
michael@0 305 size_t sz = (sizeof(JSIdArray) - sizeof(jsid)) + idsz;
michael@0 306 JSIdArray *ida = static_cast<JSIdArray *>(cx->malloc_(sz));
michael@0 307 if (!ida)
michael@0 308 return false;
michael@0 309
michael@0 310 ida->length = static_cast<int>(len);
michael@0 311 jsid *v = props.begin();
michael@0 312 for (int i = 0; i < ida->length; i++)
michael@0 313 ida->vector[i].init(v[i]);
michael@0 314 *idap = ida;
michael@0 315 return true;
michael@0 316 }
michael@0 317
michael@0 318 JS_FRIEND_API(bool)
michael@0 319 js::GetPropertyNames(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector *props)
michael@0 320 {
michael@0 321 return Snapshot(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), props);
michael@0 322 }
michael@0 323
michael@0 324 size_t sCustomIteratorCount = 0;
michael@0 325
michael@0 326 static inline bool
michael@0 327 GetCustomIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleValue vp)
michael@0 328 {
michael@0 329 JS_CHECK_RECURSION(cx, return false);
michael@0 330
michael@0 331 /* Check whether we have a valid __iterator__ method. */
michael@0 332 HandlePropertyName name = cx->names().iteratorIntrinsic;
michael@0 333 if (!JSObject::getProperty(cx, obj, obj, name, vp))
michael@0 334 return false;
michael@0 335
michael@0 336 /* If there is no custom __iterator__ method, we are done here. */
michael@0 337 if (!vp.isObject()) {
michael@0 338 vp.setUndefined();
michael@0 339 return true;
michael@0 340 }
michael@0 341
michael@0 342 if (!cx->runningWithTrustedPrincipals())
michael@0 343 ++sCustomIteratorCount;
michael@0 344
michael@0 345 /* Otherwise call it and return that object. */
michael@0 346 Value arg = BooleanValue((flags & JSITER_FOREACH) == 0);
michael@0 347 if (!Invoke(cx, ObjectValue(*obj), vp, 1, &arg, vp))
michael@0 348 return false;
michael@0 349 if (vp.isPrimitive()) {
michael@0 350 /*
michael@0 351 * We are always coming from js::ValueToIterator, and we are no longer on
michael@0 352 * trace, so the object we are iterating over is on top of the stack (-1).
michael@0 353 */
michael@0 354 JSAutoByteString bytes;
michael@0 355 if (!AtomToPrintableString(cx, name, &bytes))
michael@0 356 return false;
michael@0 357 RootedValue val(cx, ObjectValue(*obj));
michael@0 358 js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
michael@0 359 -1, val, js::NullPtr(), bytes.ptr());
michael@0 360 return false;
michael@0 361 }
michael@0 362 return true;
michael@0 363 }
michael@0 364
michael@0 365 template <typename T>
michael@0 366 static inline bool
michael@0 367 Compare(T *a, T *b, size_t c)
michael@0 368 {
michael@0 369 size_t n = (c + size_t(7)) / size_t(8);
michael@0 370 switch (c % 8) {
michael@0 371 case 0: do { if (*a++ != *b++) return false;
michael@0 372 case 7: if (*a++ != *b++) return false;
michael@0 373 case 6: if (*a++ != *b++) return false;
michael@0 374 case 5: if (*a++ != *b++) return false;
michael@0 375 case 4: if (*a++ != *b++) return false;
michael@0 376 case 3: if (*a++ != *b++) return false;
michael@0 377 case 2: if (*a++ != *b++) return false;
michael@0 378 case 1: if (*a++ != *b++) return false;
michael@0 379 } while (--n > 0);
michael@0 380 }
michael@0 381 return true;
michael@0 382 }
michael@0 383
michael@0 384 static inline PropertyIteratorObject *
michael@0 385 NewPropertyIteratorObject(JSContext *cx, unsigned flags)
michael@0 386 {
michael@0 387 if (flags & JSITER_ENUMERATE) {
michael@0 388 RootedTypeObject type(cx, cx->getNewType(&PropertyIteratorObject::class_, nullptr));
michael@0 389 if (!type)
michael@0 390 return nullptr;
michael@0 391
michael@0 392 JSObject *metadata = nullptr;
michael@0 393 if (!NewObjectMetadata(cx, &metadata))
michael@0 394 return nullptr;
michael@0 395
michael@0 396 const Class *clasp = &PropertyIteratorObject::class_;
michael@0 397 RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, nullptr, nullptr, metadata,
michael@0 398 ITERATOR_FINALIZE_KIND));
michael@0 399 if (!shape)
michael@0 400 return nullptr;
michael@0 401
michael@0 402 JSObject *obj = JSObject::create(cx, ITERATOR_FINALIZE_KIND,
michael@0 403 GetInitialHeap(GenericObject, clasp), shape, type);
michael@0 404 if (!obj)
michael@0 405 return nullptr;
michael@0 406
michael@0 407 JS_ASSERT(obj->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);
michael@0 408 return &obj->as<PropertyIteratorObject>();
michael@0 409 }
michael@0 410
michael@0 411 JSObject *obj = NewBuiltinClassInstance(cx, &PropertyIteratorObject::class_);
michael@0 412 if (!obj)
michael@0 413 return nullptr;
michael@0 414
michael@0 415 return &obj->as<PropertyIteratorObject>();
michael@0 416 }
michael@0 417
michael@0 418 NativeIterator *
michael@0 419 NativeIterator::allocateIterator(JSContext *cx, uint32_t slength, const AutoIdVector &props)
michael@0 420 {
michael@0 421 size_t plength = props.length();
michael@0 422 NativeIterator *ni = (NativeIterator *)
michael@0 423 cx->malloc_(sizeof(NativeIterator)
michael@0 424 + plength * sizeof(JSString *)
michael@0 425 + slength * sizeof(Shape *));
michael@0 426 if (!ni)
michael@0 427 return nullptr;
michael@0 428 AutoValueVector strings(cx);
michael@0 429 ni->props_array = ni->props_cursor = (HeapPtr<JSFlatString> *) (ni + 1);
michael@0 430 ni->props_end = ni->props_array + plength;
michael@0 431 if (plength) {
michael@0 432 for (size_t i = 0; i < plength; i++) {
michael@0 433 JSFlatString *str = IdToString(cx, props[i]);
michael@0 434 if (!str || !strings.append(StringValue(str)))
michael@0 435 return nullptr;
michael@0 436 ni->props_array[i].init(str);
michael@0 437 }
michael@0 438 }
michael@0 439 ni->next_ = nullptr;
michael@0 440 ni->prev_ = nullptr;
michael@0 441 return ni;
michael@0 442 }
michael@0 443
michael@0 444 NativeIterator *
michael@0 445 NativeIterator::allocateSentinel(JSContext *cx)
michael@0 446 {
michael@0 447 NativeIterator *ni = (NativeIterator *)js_malloc(sizeof(NativeIterator));
michael@0 448 if (!ni)
michael@0 449 return nullptr;
michael@0 450
michael@0 451 PodZero(ni);
michael@0 452
michael@0 453 ni->next_ = ni;
michael@0 454 ni->prev_ = ni;
michael@0 455 return ni;
michael@0 456 }
michael@0 457
michael@0 458 inline void
michael@0 459 NativeIterator::init(JSObject *obj, JSObject *iterObj, unsigned flags, uint32_t slength, uint32_t key)
michael@0 460 {
michael@0 461 this->obj.init(obj);
michael@0 462 this->iterObj_ = iterObj;
michael@0 463 this->flags = flags;
michael@0 464 this->shapes_array = (Shape **) this->props_end;
michael@0 465 this->shapes_length = slength;
michael@0 466 this->shapes_key = key;
michael@0 467 }
michael@0 468
michael@0 469 static inline void
michael@0 470 RegisterEnumerator(JSContext *cx, PropertyIteratorObject *iterobj, NativeIterator *ni)
michael@0 471 {
michael@0 472 /* Register non-escaping native enumerators (for-in) with the current context. */
michael@0 473 if (ni->flags & JSITER_ENUMERATE) {
michael@0 474 ni->link(cx->compartment()->enumerators);
michael@0 475
michael@0 476 JS_ASSERT(!(ni->flags & JSITER_ACTIVE));
michael@0 477 ni->flags |= JSITER_ACTIVE;
michael@0 478 }
michael@0 479 }
michael@0 480
michael@0 481 static inline bool
michael@0 482 VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &keys,
michael@0 483 uint32_t slength, uint32_t key, MutableHandleValue vp)
michael@0 484 {
michael@0 485 JS_ASSERT(!(flags & JSITER_FOREACH));
michael@0 486
michael@0 487 if (obj) {
michael@0 488 if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx))
michael@0 489 return false;
michael@0 490 types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
michael@0 491 }
michael@0 492
michael@0 493 Rooted<PropertyIteratorObject *> iterobj(cx, NewPropertyIteratorObject(cx, flags));
michael@0 494 if (!iterobj)
michael@0 495 return false;
michael@0 496
michael@0 497 NativeIterator *ni = NativeIterator::allocateIterator(cx, slength, keys);
michael@0 498 if (!ni)
michael@0 499 return false;
michael@0 500 ni->init(obj, iterobj, flags, slength, key);
michael@0 501
michael@0 502 if (slength) {
michael@0 503 /*
michael@0 504 * Fill in the shape array from scratch. We can't use the array that was
michael@0 505 * computed for the cache lookup earlier, as constructing iterobj could
michael@0 506 * have triggered a shape-regenerating GC. Don't bother with regenerating
michael@0 507 * the shape key; if such a GC *does* occur, we can only get hits through
michael@0 508 * the one-slot lastNativeIterator cache.
michael@0 509 */
michael@0 510 JSObject *pobj = obj;
michael@0 511 size_t ind = 0;
michael@0 512 do {
michael@0 513 ni->shapes_array[ind++] = pobj->lastProperty();
michael@0 514 pobj = pobj->getProto();
michael@0 515 } while (pobj);
michael@0 516 JS_ASSERT(ind == slength);
michael@0 517 }
michael@0 518
michael@0 519 iterobj->setNativeIterator(ni);
michael@0 520 vp.setObject(*iterobj);
michael@0 521
michael@0 522 RegisterEnumerator(cx, iterobj, ni);
michael@0 523 return true;
michael@0 524 }
michael@0 525
michael@0 526 bool
michael@0 527 js::VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &props,
michael@0 528 MutableHandleValue vp)
michael@0 529 {
michael@0 530 return VectorToKeyIterator(cx, obj, flags, props, 0, 0, vp);
michael@0 531 }
michael@0 532
michael@0 533 bool
michael@0 534 js::VectorToValueIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &keys,
michael@0 535 MutableHandleValue vp)
michael@0 536 {
michael@0 537 JS_ASSERT(flags & JSITER_FOREACH);
michael@0 538
michael@0 539 if (obj) {
michael@0 540 if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx))
michael@0 541 return false;
michael@0 542 types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
michael@0 543 }
michael@0 544
michael@0 545 Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx, flags));
michael@0 546 if (!iterobj)
michael@0 547 return false;
michael@0 548
michael@0 549 NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, keys);
michael@0 550 if (!ni)
michael@0 551 return false;
michael@0 552 ni->init(obj, iterobj, flags, 0, 0);
michael@0 553
michael@0 554 iterobj->setNativeIterator(ni);
michael@0 555 vp.setObject(*iterobj);
michael@0 556
michael@0 557 RegisterEnumerator(cx, iterobj, ni);
michael@0 558 return true;
michael@0 559 }
michael@0 560
michael@0 561 bool
michael@0 562 js::EnumeratedIdVectorToIterator(JSContext *cx, HandleObject obj, unsigned flags,
michael@0 563 AutoIdVector &props, MutableHandleValue vp)
michael@0 564 {
michael@0 565 if (!(flags & JSITER_FOREACH))
michael@0 566 return VectorToKeyIterator(cx, obj, flags, props, vp);
michael@0 567
michael@0 568 return VectorToValueIterator(cx, obj, flags, props, vp);
michael@0 569 }
michael@0 570
michael@0 571 static inline void
michael@0 572 UpdateNativeIterator(NativeIterator *ni, JSObject *obj)
michael@0 573 {
michael@0 574 // Update the object for which the native iterator is associated, so
michael@0 575 // SuppressDeletedPropertyHelper will recognize the iterator as a match.
michael@0 576 ni->obj = obj;
michael@0 577 }
michael@0 578
michael@0 579 bool
michael@0 580 js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleValue vp)
michael@0 581 {
michael@0 582 Vector<Shape *, 8> shapes(cx);
michael@0 583 uint32_t key = 0;
michael@0 584
michael@0 585 bool keysOnly = (flags == JSITER_ENUMERATE);
michael@0 586
michael@0 587 if (obj) {
michael@0 588 if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) {
michael@0 589 JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH));
michael@0 590 if (!iterobj)
michael@0 591 return false;
michael@0 592 vp.setObject(*iterobj);
michael@0 593 return true;
michael@0 594 }
michael@0 595
michael@0 596 if (keysOnly) {
michael@0 597 /*
michael@0 598 * Check to see if this is the same as the most recent object which
michael@0 599 * was iterated over. We don't explicitly check for shapeless
michael@0 600 * objects here, as they are not inserted into the cache and
michael@0 601 * will result in a miss.
michael@0 602 */
michael@0 603 PropertyIteratorObject *last = cx->runtime()->nativeIterCache.last;
michael@0 604 if (last) {
michael@0 605 NativeIterator *lastni = last->getNativeIterator();
michael@0 606 if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
michael@0 607 obj->isNative() &&
michael@0 608 obj->hasEmptyElements() &&
michael@0 609 obj->lastProperty() == lastni->shapes_array[0])
michael@0 610 {
michael@0 611 JSObject *proto = obj->getProto();
michael@0 612 if (proto->isNative() &&
michael@0 613 proto->hasEmptyElements() &&
michael@0 614 proto->lastProperty() == lastni->shapes_array[1] &&
michael@0 615 !proto->getProto())
michael@0 616 {
michael@0 617 vp.setObject(*last);
michael@0 618 UpdateNativeIterator(lastni, obj);
michael@0 619 RegisterEnumerator(cx, last, lastni);
michael@0 620 return true;
michael@0 621 }
michael@0 622 }
michael@0 623 }
michael@0 624
michael@0 625 /*
michael@0 626 * The iterator object for JSITER_ENUMERATE never escapes, so we
michael@0 627 * don't care for the proper parent/proto to be set. This also
michael@0 628 * allows us to re-use a previous iterator object that is not
michael@0 629 * currently active.
michael@0 630 */
michael@0 631 {
michael@0 632 JSObject *pobj = obj;
michael@0 633 do {
michael@0 634 if (!pobj->isNative() ||
michael@0 635 !pobj->hasEmptyElements() ||
michael@0 636 pobj->is<TypedArrayObject>() ||
michael@0 637 pobj->hasUncacheableProto() ||
michael@0 638 pobj->getOps()->enumerate ||
michael@0 639 pobj->getClass()->enumerate != JS_EnumerateStub ||
michael@0 640 pobj->nativeContainsPure(cx->names().iteratorIntrinsic))
michael@0 641 {
michael@0 642 shapes.clear();
michael@0 643 goto miss;
michael@0 644 }
michael@0 645 Shape *shape = pobj->lastProperty();
michael@0 646 key = (key + (key << 16)) ^ (uintptr_t(shape) >> 3);
michael@0 647 if (!shapes.append(shape))
michael@0 648 return false;
michael@0 649 pobj = pobj->getProto();
michael@0 650 } while (pobj);
michael@0 651 }
michael@0 652
michael@0 653 PropertyIteratorObject *iterobj = cx->runtime()->nativeIterCache.get(key);
michael@0 654 if (iterobj) {
michael@0 655 NativeIterator *ni = iterobj->getNativeIterator();
michael@0 656 if (!(ni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
michael@0 657 ni->shapes_key == key &&
michael@0 658 ni->shapes_length == shapes.length() &&
michael@0 659 Compare(ni->shapes_array, shapes.begin(), ni->shapes_length)) {
michael@0 660 vp.setObject(*iterobj);
michael@0 661
michael@0 662 UpdateNativeIterator(ni, obj);
michael@0 663 RegisterEnumerator(cx, iterobj, ni);
michael@0 664 if (shapes.length() == 2)
michael@0 665 cx->runtime()->nativeIterCache.last = iterobj;
michael@0 666 return true;
michael@0 667 }
michael@0 668 }
michael@0 669 }
michael@0 670
michael@0 671 miss:
michael@0 672 if (obj->is<ProxyObject>())
michael@0 673 return Proxy::iterate(cx, obj, flags, vp);
michael@0 674
michael@0 675 if (!GetCustomIterator(cx, obj, flags, vp))
michael@0 676 return false;
michael@0 677 if (!vp.isUndefined())
michael@0 678 return true;
michael@0 679 }
michael@0 680
michael@0 681 /* NB: for (var p in null) succeeds by iterating over no properties. */
michael@0 682
michael@0 683 AutoIdVector keys(cx);
michael@0 684 if (flags & JSITER_FOREACH) {
michael@0 685 if (MOZ_LIKELY(obj != nullptr) && !Snapshot(cx, obj, flags, &keys))
michael@0 686 return false;
michael@0 687 JS_ASSERT(shapes.empty());
michael@0 688 if (!VectorToValueIterator(cx, obj, flags, keys, vp))
michael@0 689 return false;
michael@0 690 } else {
michael@0 691 if (MOZ_LIKELY(obj != nullptr) && !Snapshot(cx, obj, flags, &keys))
michael@0 692 return false;
michael@0 693 if (!VectorToKeyIterator(cx, obj, flags, keys, shapes.length(), key, vp))
michael@0 694 return false;
michael@0 695 }
michael@0 696
michael@0 697 PropertyIteratorObject *iterobj = &vp.toObject().as<PropertyIteratorObject>();
michael@0 698
michael@0 699 /* Cache the iterator object if possible. */
michael@0 700 if (shapes.length())
michael@0 701 cx->runtime()->nativeIterCache.set(key, iterobj);
michael@0 702
michael@0 703 if (shapes.length() == 2)
michael@0 704 cx->runtime()->nativeIterCache.last = iterobj;
michael@0 705 return true;
michael@0 706 }
michael@0 707
michael@0 708 JSObject *
michael@0 709 js::GetIteratorObject(JSContext *cx, HandleObject obj, uint32_t flags)
michael@0 710 {
michael@0 711 RootedValue value(cx);
michael@0 712 if (!GetIterator(cx, obj, flags, &value))
michael@0 713 return nullptr;
michael@0 714 return &value.toObject();
michael@0 715 }
michael@0 716
michael@0 717 JSObject *
michael@0 718 js::CreateItrResultObject(JSContext *cx, HandleValue value, bool done)
michael@0 719 {
michael@0 720 // FIXME: We can cache the iterator result object shape somewhere.
michael@0 721 AssertHeapIsIdle(cx);
michael@0 722
michael@0 723 RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx));
michael@0 724 if (!proto)
michael@0 725 return nullptr;
michael@0 726
michael@0 727 RootedObject obj(cx, NewObjectWithGivenProto(cx, &JSObject::class_, proto, cx->global()));
michael@0 728 if (!obj)
michael@0 729 return nullptr;
michael@0 730
michael@0 731 if (!JSObject::defineProperty(cx, obj, cx->names().value, value))
michael@0 732 return nullptr;
michael@0 733
michael@0 734 RootedValue doneBool(cx, BooleanValue(done));
michael@0 735 if (!JSObject::defineProperty(cx, obj, cx->names().done, doneBool))
michael@0 736 return nullptr;
michael@0 737
michael@0 738 return obj;
michael@0 739 }
michael@0 740
michael@0 741 bool
michael@0 742 js_ThrowStopIteration(JSContext *cx)
michael@0 743 {
michael@0 744 JS_ASSERT(!JS_IsExceptionPending(cx));
michael@0 745
michael@0 746 // StopIteration isn't a constructor, but it's stored in GlobalObject
michael@0 747 // as one, out of laziness. Hence the GetBuiltinConstructor call here.
michael@0 748 RootedObject ctor(cx);
michael@0 749 if (GetBuiltinConstructor(cx, JSProto_StopIteration, &ctor))
michael@0 750 cx->setPendingException(ObjectValue(*ctor));
michael@0 751 return false;
michael@0 752 }
michael@0 753
michael@0 754 /*** Iterator objects ****************************************************************************/
michael@0 755
michael@0 756 bool
michael@0 757 js::IteratorConstructor(JSContext *cx, unsigned argc, Value *vp)
michael@0 758 {
michael@0 759 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 760 if (args.length() == 0) {
michael@0 761 js_ReportMissingArg(cx, args.calleev(), 0);
michael@0 762 return false;
michael@0 763 }
michael@0 764
michael@0 765 bool keyonly = false;
michael@0 766 if (args.length() >= 2)
michael@0 767 keyonly = ToBoolean(args[1]);
michael@0 768 unsigned flags = JSITER_OWNONLY | (keyonly ? 0 : (JSITER_FOREACH | JSITER_KEYVALUE));
michael@0 769
michael@0 770 if (!ValueToIterator(cx, flags, args[0]))
michael@0 771 return false;
michael@0 772 args.rval().set(args[0]);
michael@0 773 return true;
michael@0 774 }
michael@0 775
michael@0 776 MOZ_ALWAYS_INLINE bool
michael@0 777 IsIterator(HandleValue v)
michael@0 778 {
michael@0 779 return v.isObject() && v.toObject().hasClass(&PropertyIteratorObject::class_);
michael@0 780 }
michael@0 781
michael@0 782 MOZ_ALWAYS_INLINE bool
michael@0 783 iterator_next_impl(JSContext *cx, CallArgs args)
michael@0 784 {
michael@0 785 JS_ASSERT(IsIterator(args.thisv()));
michael@0 786
michael@0 787 RootedObject thisObj(cx, &args.thisv().toObject());
michael@0 788
michael@0 789 if (!js_IteratorMore(cx, thisObj, args.rval()))
michael@0 790 return false;
michael@0 791
michael@0 792 if (!args.rval().toBoolean()) {
michael@0 793 js_ThrowStopIteration(cx);
michael@0 794 return false;
michael@0 795 }
michael@0 796
michael@0 797 return js_IteratorNext(cx, thisObj, args.rval());
michael@0 798 }
michael@0 799
michael@0 800 static bool
michael@0 801 iterator_next(JSContext *cx, unsigned argc, Value *vp)
michael@0 802 {
michael@0 803 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 804 return CallNonGenericMethod<IsIterator, iterator_next_impl>(cx, args);
michael@0 805 }
michael@0 806
michael@0 807 static const JSFunctionSpec iterator_methods[] = {
michael@0 808 JS_SELF_HOSTED_FN("@@iterator", "LegacyIteratorShim", 0, 0),
michael@0 809 JS_FN("next", iterator_next, 0, 0),
michael@0 810 JS_FS_END
michael@0 811 };
michael@0 812
michael@0 813 static JSObject *
michael@0 814 iterator_iteratorObject(JSContext *cx, HandleObject obj, bool keysonly)
michael@0 815 {
michael@0 816 return obj;
michael@0 817 }
michael@0 818
michael@0 819 size_t
michael@0 820 PropertyIteratorObject::sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const
michael@0 821 {
michael@0 822 return mallocSizeOf(getPrivate());
michael@0 823 }
michael@0 824
michael@0 825 void
michael@0 826 PropertyIteratorObject::trace(JSTracer *trc, JSObject *obj)
michael@0 827 {
michael@0 828 if (NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator())
michael@0 829 ni->mark(trc);
michael@0 830 }
michael@0 831
michael@0 832 void
michael@0 833 PropertyIteratorObject::finalize(FreeOp *fop, JSObject *obj)
michael@0 834 {
michael@0 835 if (NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator())
michael@0 836 fop->free_(ni);
michael@0 837 }
michael@0 838
michael@0 839 const Class PropertyIteratorObject::class_ = {
michael@0 840 "Iterator",
michael@0 841 JSCLASS_IMPLEMENTS_BARRIERS |
michael@0 842 JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
michael@0 843 JSCLASS_HAS_PRIVATE |
michael@0 844 JSCLASS_BACKGROUND_FINALIZE,
michael@0 845 JS_PropertyStub, /* addProperty */
michael@0 846 JS_DeletePropertyStub, /* delProperty */
michael@0 847 JS_PropertyStub, /* getProperty */
michael@0 848 JS_StrictPropertyStub, /* setProperty */
michael@0 849 JS_EnumerateStub,
michael@0 850 JS_ResolveStub,
michael@0 851 JS_ConvertStub,
michael@0 852 finalize,
michael@0 853 nullptr, /* call */
michael@0 854 nullptr, /* hasInstance */
michael@0 855 nullptr, /* construct */
michael@0 856 trace,
michael@0 857 JS_NULL_CLASS_SPEC,
michael@0 858 {
michael@0 859 nullptr, /* outerObject */
michael@0 860 nullptr, /* innerObject */
michael@0 861 iterator_iteratorObject,
michael@0 862 }
michael@0 863 };
michael@0 864
michael@0 865 enum {
michael@0 866 ArrayIteratorSlotIteratedObject,
michael@0 867 ArrayIteratorSlotNextIndex,
michael@0 868 ArrayIteratorSlotItemKind,
michael@0 869 ArrayIteratorSlotCount
michael@0 870 };
michael@0 871
michael@0 872 const Class ArrayIteratorObject::class_ = {
michael@0 873 "Array Iterator",
michael@0 874 JSCLASS_IMPLEMENTS_BARRIERS |
michael@0 875 JSCLASS_HAS_RESERVED_SLOTS(ArrayIteratorSlotCount),
michael@0 876 JS_PropertyStub, /* addProperty */
michael@0 877 JS_DeletePropertyStub, /* delProperty */
michael@0 878 JS_PropertyStub, /* getProperty */
michael@0 879 JS_StrictPropertyStub, /* setProperty */
michael@0 880 JS_EnumerateStub,
michael@0 881 JS_ResolveStub,
michael@0 882 JS_ConvertStub,
michael@0 883 nullptr /* finalize */
michael@0 884 };
michael@0 885
michael@0 886 static const JSFunctionSpec array_iterator_methods[] = {
michael@0 887 JS_SELF_HOSTED_FN("@@iterator", "ArrayIteratorIdentity", 0, 0),
michael@0 888 JS_SELF_HOSTED_FN("next", "ArrayIteratorNext", 0, 0),
michael@0 889 JS_FS_END
michael@0 890 };
michael@0 891
michael@0 892 static const Class StringIteratorPrototypeClass = {
michael@0 893 "String Iterator",
michael@0 894 JSCLASS_IMPLEMENTS_BARRIERS,
michael@0 895 JS_PropertyStub, /* addProperty */
michael@0 896 JS_DeletePropertyStub, /* delProperty */
michael@0 897 JS_PropertyStub, /* getProperty */
michael@0 898 JS_StrictPropertyStub, /* setProperty */
michael@0 899 JS_EnumerateStub,
michael@0 900 JS_ResolveStub,
michael@0 901 JS_ConvertStub,
michael@0 902 nullptr /* finalize */
michael@0 903 };
michael@0 904
michael@0 905 enum {
michael@0 906 StringIteratorSlotIteratedObject,
michael@0 907 StringIteratorSlotNextIndex,
michael@0 908 StringIteratorSlotCount
michael@0 909 };
michael@0 910
michael@0 911 const Class StringIteratorObject::class_ = {
michael@0 912 "String Iterator",
michael@0 913 JSCLASS_IMPLEMENTS_BARRIERS |
michael@0 914 JSCLASS_HAS_RESERVED_SLOTS(StringIteratorSlotCount),
michael@0 915 JS_PropertyStub, /* addProperty */
michael@0 916 JS_DeletePropertyStub, /* delProperty */
michael@0 917 JS_PropertyStub, /* getProperty */
michael@0 918 JS_StrictPropertyStub, /* setProperty */
michael@0 919 JS_EnumerateStub,
michael@0 920 JS_ResolveStub,
michael@0 921 JS_ConvertStub,
michael@0 922 nullptr /* finalize */
michael@0 923 };
michael@0 924
michael@0 925 static const JSFunctionSpec string_iterator_methods[] = {
michael@0 926 JS_SELF_HOSTED_FN("@@iterator", "StringIteratorIdentity", 0, 0),
michael@0 927 JS_SELF_HOSTED_FN("next", "StringIteratorNext", 0, 0),
michael@0 928 JS_FS_END
michael@0 929 };
michael@0 930
michael@0 931 static bool
michael@0 932 CloseLegacyGenerator(JSContext *cx, HandleObject genobj);
michael@0 933
michael@0 934 bool
michael@0 935 js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp)
michael@0 936 {
michael@0 937 /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
michael@0 938 JS_ASSERT_IF(flags & JSITER_KEYVALUE, flags & JSITER_FOREACH);
michael@0 939
michael@0 940 /*
michael@0 941 * Make sure the more/next state machine doesn't get stuck. A value might
michael@0 942 * be left in iterValue when a trace is left due to an interrupt after
michael@0 943 * JSOP_MOREITER but before the value is picked up by FOR*.
michael@0 944 */
michael@0 945 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
michael@0 946
michael@0 947 RootedObject obj(cx);
michael@0 948 if (vp.isObject()) {
michael@0 949 /* Common case. */
michael@0 950 obj = &vp.toObject();
michael@0 951 } else {
michael@0 952 /*
michael@0 953 * Enumerating over null and undefined gives an empty enumerator, so
michael@0 954 * that |for (var p in <null or undefined>) <loop>;| never executes
michael@0 955 * <loop>, per ES5 12.6.4.
michael@0 956 */
michael@0 957 if (!(flags & JSITER_ENUMERATE) || !vp.isNullOrUndefined()) {
michael@0 958 obj = ToObject(cx, vp);
michael@0 959 if (!obj)
michael@0 960 return false;
michael@0 961 }
michael@0 962 }
michael@0 963
michael@0 964 return GetIterator(cx, obj, flags, vp);
michael@0 965 }
michael@0 966
michael@0 967 bool
michael@0 968 js::CloseIterator(JSContext *cx, HandleObject obj)
michael@0 969 {
michael@0 970 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
michael@0 971
michael@0 972 if (obj->is<PropertyIteratorObject>()) {
michael@0 973 /* Remove enumerators from the active list, which is a stack. */
michael@0 974 NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator();
michael@0 975
michael@0 976 if (ni->flags & JSITER_ENUMERATE) {
michael@0 977 ni->unlink();
michael@0 978
michael@0 979 JS_ASSERT(ni->flags & JSITER_ACTIVE);
michael@0 980 ni->flags &= ~JSITER_ACTIVE;
michael@0 981
michael@0 982 /*
michael@0 983 * Reset the enumerator; it may still be in the cached iterators
michael@0 984 * for this thread, and can be reused.
michael@0 985 */
michael@0 986 ni->props_cursor = ni->props_array;
michael@0 987 }
michael@0 988 } else if (obj->is<LegacyGeneratorObject>()) {
michael@0 989 return CloseLegacyGenerator(cx, obj);
michael@0 990 }
michael@0 991 return true;
michael@0 992 }
michael@0 993
michael@0 994 bool
michael@0 995 js::UnwindIteratorForException(JSContext *cx, HandleObject obj)
michael@0 996 {
michael@0 997 RootedValue v(cx);
michael@0 998 bool getOk = cx->getPendingException(&v);
michael@0 999 cx->clearPendingException();
michael@0 1000 if (!CloseIterator(cx, obj))
michael@0 1001 return false;
michael@0 1002 if (!getOk)
michael@0 1003 return false;
michael@0 1004 cx->setPendingException(v);
michael@0 1005 return true;
michael@0 1006 }
michael@0 1007
michael@0 1008 void
michael@0 1009 js::UnwindIteratorForUncatchableException(JSContext *cx, JSObject *obj)
michael@0 1010 {
michael@0 1011 if (obj->is<PropertyIteratorObject>()) {
michael@0 1012 NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator();
michael@0 1013 if (ni->flags & JSITER_ENUMERATE)
michael@0 1014 ni->unlink();
michael@0 1015 }
michael@0 1016 }
michael@0 1017
michael@0 1018 /*
michael@0 1019 * Suppress enumeration of deleted properties. This function must be called
michael@0 1020 * when a property is deleted and there might be active enumerators.
michael@0 1021 *
michael@0 1022 * We maintain a list of active non-escaping for-in enumerators. To suppress
michael@0 1023 * a property, we check whether each active enumerator contains the (obj, id)
michael@0 1024 * pair and has not yet enumerated |id|. If so, and |id| is the next property,
michael@0 1025 * we simply advance the cursor. Otherwise, we delete |id| from the list.
michael@0 1026 *
michael@0 1027 * We do not suppress enumeration of a property deleted along an object's
michael@0 1028 * prototype chain. Only direct deletions on the object are handled.
michael@0 1029 *
michael@0 1030 * This function can suppress multiple properties at once. The |predicate|
michael@0 1031 * argument is an object which can be called on an id and returns true or
michael@0 1032 * false. It also must have a method |matchesAtMostOne| which allows us to
michael@0 1033 * stop searching after the first deletion if true.
michael@0 1034 */
michael@0 1035 template<typename StringPredicate>
michael@0 1036 static bool
michael@0 1037 SuppressDeletedPropertyHelper(JSContext *cx, HandleObject obj, StringPredicate predicate)
michael@0 1038 {
michael@0 1039 NativeIterator *enumeratorList = cx->compartment()->enumerators;
michael@0 1040 NativeIterator *ni = enumeratorList->next();
michael@0 1041
michael@0 1042 while (ni != enumeratorList) {
michael@0 1043 again:
michael@0 1044 /* This only works for identified suppressed keys, not values. */
michael@0 1045 if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) {
michael@0 1046 /* Check whether id is still to come. */
michael@0 1047 HeapPtr<JSFlatString> *props_cursor = ni->current();
michael@0 1048 HeapPtr<JSFlatString> *props_end = ni->end();
michael@0 1049 for (HeapPtr<JSFlatString> *idp = props_cursor; idp < props_end; ++idp) {
michael@0 1050 if (predicate(*idp)) {
michael@0 1051 /*
michael@0 1052 * Check whether another property along the prototype chain
michael@0 1053 * became visible as a result of this deletion.
michael@0 1054 */
michael@0 1055 RootedObject proto(cx);
michael@0 1056 if (!JSObject::getProto(cx, obj, &proto))
michael@0 1057 return false;
michael@0 1058 if (proto) {
michael@0 1059 RootedObject obj2(cx);
michael@0 1060 RootedShape prop(cx);
michael@0 1061 RootedId id(cx);
michael@0 1062 RootedValue idv(cx, StringValue(*idp));
michael@0 1063 if (!ValueToId<CanGC>(cx, idv, &id))
michael@0 1064 return false;
michael@0 1065 if (!JSObject::lookupGeneric(cx, proto, id, &obj2, &prop))
michael@0 1066 return false;
michael@0 1067 if (prop) {
michael@0 1068 unsigned attrs;
michael@0 1069 if (obj2->isNative())
michael@0 1070 attrs = GetShapeAttributes(obj2, prop);
michael@0 1071 else if (!JSObject::getGenericAttributes(cx, obj2, id, &attrs))
michael@0 1072 return false;
michael@0 1073
michael@0 1074 if (attrs & JSPROP_ENUMERATE)
michael@0 1075 continue;
michael@0 1076 }
michael@0 1077 }
michael@0 1078
michael@0 1079 /*
michael@0 1080 * If lookupProperty or getAttributes above removed a property from
michael@0 1081 * ni, start over.
michael@0 1082 */
michael@0 1083 if (props_end != ni->props_end || props_cursor != ni->props_cursor)
michael@0 1084 goto again;
michael@0 1085
michael@0 1086 /*
michael@0 1087 * No property along the prototype chain stepped in to take the
michael@0 1088 * property's place, so go ahead and delete id from the list.
michael@0 1089 * If it is the next property to be enumerated, just skip it.
michael@0 1090 */
michael@0 1091 if (idp == props_cursor) {
michael@0 1092 ni->incCursor();
michael@0 1093 } else {
michael@0 1094 for (HeapPtr<JSFlatString> *p = idp; p + 1 != props_end; p++)
michael@0 1095 *p = *(p + 1);
michael@0 1096 ni->props_end = ni->end() - 1;
michael@0 1097
michael@0 1098 /*
michael@0 1099 * This invokes the pre barrier on this element, since
michael@0 1100 * it's no longer going to be marked, and ensures that
michael@0 1101 * any existing remembered set entry will be dropped.
michael@0 1102 */
michael@0 1103 *ni->props_end = nullptr;
michael@0 1104 }
michael@0 1105
michael@0 1106 /* Don't reuse modified native iterators. */
michael@0 1107 ni->flags |= JSITER_UNREUSABLE;
michael@0 1108
michael@0 1109 if (predicate.matchesAtMostOne())
michael@0 1110 break;
michael@0 1111 }
michael@0 1112 }
michael@0 1113 }
michael@0 1114 ni = ni->next();
michael@0 1115 }
michael@0 1116 return true;
michael@0 1117 }
michael@0 1118
michael@0 1119 namespace {
michael@0 1120
michael@0 1121 class SingleStringPredicate {
michael@0 1122 Handle<JSFlatString*> str;
michael@0 1123 public:
michael@0 1124 SingleStringPredicate(Handle<JSFlatString*> str) : str(str) {}
michael@0 1125
michael@0 1126 bool operator()(JSFlatString *str) { return EqualStrings(str, this->str); }
michael@0 1127 bool matchesAtMostOne() { return true; }
michael@0 1128 };
michael@0 1129
michael@0 1130 } /* anonymous namespace */
michael@0 1131
michael@0 1132 bool
michael@0 1133 js_SuppressDeletedProperty(JSContext *cx, HandleObject obj, jsid id)
michael@0 1134 {
michael@0 1135 Rooted<JSFlatString*> str(cx, IdToString(cx, id));
michael@0 1136 if (!str)
michael@0 1137 return false;
michael@0 1138 return SuppressDeletedPropertyHelper(cx, obj, SingleStringPredicate(str));
michael@0 1139 }
michael@0 1140
michael@0 1141 bool
michael@0 1142 js_SuppressDeletedElement(JSContext *cx, HandleObject obj, uint32_t index)
michael@0 1143 {
michael@0 1144 RootedId id(cx);
michael@0 1145 if (!IndexToId(cx, index, &id))
michael@0 1146 return false;
michael@0 1147 return js_SuppressDeletedProperty(cx, obj, id);
michael@0 1148 }
michael@0 1149
michael@0 1150 namespace {
michael@0 1151
michael@0 1152 class IndexRangePredicate {
michael@0 1153 uint32_t begin, end;
michael@0 1154
michael@0 1155 public:
michael@0 1156 IndexRangePredicate(uint32_t begin, uint32_t end) : begin(begin), end(end) {}
michael@0 1157
michael@0 1158 bool operator()(JSFlatString *str) {
michael@0 1159 uint32_t index;
michael@0 1160 return str->isIndex(&index) && begin <= index && index < end;
michael@0 1161 }
michael@0 1162
michael@0 1163 bool matchesAtMostOne() { return false; }
michael@0 1164 };
michael@0 1165
michael@0 1166 } /* anonymous namespace */
michael@0 1167
michael@0 1168 bool
michael@0 1169 js_SuppressDeletedElements(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end)
michael@0 1170 {
michael@0 1171 return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end));
michael@0 1172 }
michael@0 1173
michael@0 1174 bool
michael@0 1175 js_IteratorMore(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
michael@0 1176 {
michael@0 1177 /* Fast path for native iterators */
michael@0 1178 NativeIterator *ni = nullptr;
michael@0 1179 if (iterobj->is<PropertyIteratorObject>()) {
michael@0 1180 /* Key iterators are handled by fast-paths. */
michael@0 1181 ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
michael@0 1182 bool more = ni->props_cursor < ni->props_end;
michael@0 1183 if (ni->isKeyIter() || !more) {
michael@0 1184 rval.setBoolean(more);
michael@0 1185 return true;
michael@0 1186 }
michael@0 1187 }
michael@0 1188
michael@0 1189 /* We might still have a pending value. */
michael@0 1190 if (!cx->iterValue.isMagic(JS_NO_ITER_VALUE)) {
michael@0 1191 rval.setBoolean(true);
michael@0 1192 return true;
michael@0 1193 }
michael@0 1194
michael@0 1195 /* We're reentering below and can call anything. */
michael@0 1196 JS_CHECK_RECURSION(cx, return false);
michael@0 1197
michael@0 1198 /* Fetch and cache the next value from the iterator. */
michael@0 1199 if (ni) {
michael@0 1200 JS_ASSERT(!ni->isKeyIter());
michael@0 1201 RootedId id(cx);
michael@0 1202 RootedValue current(cx, StringValue(*ni->current()));
michael@0 1203 if (!ValueToId<CanGC>(cx, current, &id))
michael@0 1204 return false;
michael@0 1205 ni->incCursor();
michael@0 1206 RootedObject obj(cx, ni->obj);
michael@0 1207 if (!JSObject::getGeneric(cx, obj, obj, id, rval))
michael@0 1208 return false;
michael@0 1209 if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, rval, rval))
michael@0 1210 return false;
michael@0 1211 } else {
michael@0 1212 /* Call the iterator object's .next method. */
michael@0 1213 if (!JSObject::getProperty(cx, iterobj, iterobj, cx->names().next, rval))
michael@0 1214 return false;
michael@0 1215 if (!Invoke(cx, ObjectValue(*iterobj), rval, 0, nullptr, rval)) {
michael@0 1216 /* Check for StopIteration. */
michael@0 1217 if (!cx->isExceptionPending())
michael@0 1218 return false;
michael@0 1219 RootedValue exception(cx);
michael@0 1220 if (!cx->getPendingException(&exception))
michael@0 1221 return false;
michael@0 1222 if (!JS_IsStopIteration(exception))
michael@0 1223 return false;
michael@0 1224
michael@0 1225 cx->clearPendingException();
michael@0 1226 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
michael@0 1227 rval.setBoolean(false);
michael@0 1228 return true;
michael@0 1229 }
michael@0 1230 }
michael@0 1231
michael@0 1232 /* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */
michael@0 1233 JS_ASSERT(!rval.isMagic(JS_NO_ITER_VALUE));
michael@0 1234 cx->iterValue = rval;
michael@0 1235 rval.setBoolean(true);
michael@0 1236 return true;
michael@0 1237 }
michael@0 1238
michael@0 1239 bool
michael@0 1240 js_IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
michael@0 1241 {
michael@0 1242 /* Fast path for native iterators */
michael@0 1243 if (iterobj->is<PropertyIteratorObject>()) {
michael@0 1244 /*
michael@0 1245 * Implement next directly as all the methods of the native iterator are
michael@0 1246 * read-only and permanent.
michael@0 1247 */
michael@0 1248 NativeIterator *ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
michael@0 1249 if (ni->isKeyIter()) {
michael@0 1250 JS_ASSERT(ni->props_cursor < ni->props_end);
michael@0 1251 rval.setString(*ni->current());
michael@0 1252 ni->incCursor();
michael@0 1253 return true;
michael@0 1254 }
michael@0 1255 }
michael@0 1256
michael@0 1257 JS_ASSERT(!cx->iterValue.isMagic(JS_NO_ITER_VALUE));
michael@0 1258 rval.set(cx->iterValue);
michael@0 1259 cx->iterValue.setMagic(JS_NO_ITER_VALUE);
michael@0 1260
michael@0 1261 return true;
michael@0 1262 }
michael@0 1263
michael@0 1264 static bool
michael@0 1265 stopiter_hasInstance(JSContext *cx, HandleObject obj, MutableHandleValue v, bool *bp)
michael@0 1266 {
michael@0 1267 *bp = JS_IsStopIteration(v);
michael@0 1268 return true;
michael@0 1269 }
michael@0 1270
michael@0 1271 const Class StopIterationObject::class_ = {
michael@0 1272 "StopIteration",
michael@0 1273 JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
michael@0 1274 JS_PropertyStub, /* addProperty */
michael@0 1275 JS_DeletePropertyStub, /* delProperty */
michael@0 1276 JS_PropertyStub, /* getProperty */
michael@0 1277 JS_StrictPropertyStub, /* setProperty */
michael@0 1278 JS_EnumerateStub,
michael@0 1279 JS_ResolveStub,
michael@0 1280 JS_ConvertStub,
michael@0 1281 nullptr, /* finalize */
michael@0 1282 nullptr, /* call */
michael@0 1283 stopiter_hasInstance,
michael@0 1284 nullptr /* construct */
michael@0 1285 };
michael@0 1286
michael@0 1287 bool
michael@0 1288 ForOfIterator::init(HandleValue iterable, NonIterableBehavior nonIterableBehavior)
michael@0 1289 {
michael@0 1290 JSContext *cx = cx_;
michael@0 1291 RootedObject iterableObj(cx, ToObject(cx, iterable));
michael@0 1292 if (!iterableObj)
michael@0 1293 return false;
michael@0 1294
michael@0 1295 JS_ASSERT(index == NOT_ARRAY);
michael@0 1296
michael@0 1297 // Check the PIC first for a match.
michael@0 1298 if (iterableObj->is<ArrayObject>()) {
michael@0 1299 ForOfPIC::Chain *stubChain = ForOfPIC::getOrCreate(cx);
michael@0 1300 if (!stubChain)
michael@0 1301 return false;
michael@0 1302
michael@0 1303 bool optimized;
michael@0 1304 if (!stubChain->tryOptimizeArray(cx, iterableObj, &optimized))
michael@0 1305 return false;
michael@0 1306
michael@0 1307 if (optimized) {
michael@0 1308 // Got optimized stub. Array is optimizable.
michael@0 1309 index = 0;
michael@0 1310 iterator = iterableObj;
michael@0 1311 return true;
michael@0 1312 }
michael@0 1313 }
michael@0 1314
michael@0 1315 JS_ASSERT(index == NOT_ARRAY);
michael@0 1316
michael@0 1317 // The iterator is the result of calling obj[@@iterator]().
michael@0 1318 InvokeArgs args(cx);
michael@0 1319 if (!args.init(0))
michael@0 1320 return false;
michael@0 1321 args.setThis(ObjectValue(*iterableObj));
michael@0 1322
michael@0 1323 RootedValue callee(cx);
michael@0 1324 if (!JSObject::getProperty(cx, iterableObj, iterableObj, cx->names().std_iterator, &callee))
michael@0 1325 return false;
michael@0 1326
michael@0 1327 // Throw if obj[@@iterator] isn't callable if we were asked to do so.
michael@0 1328 // js::Invoke is about to check for this kind of error anyway, but it would
michael@0 1329 // throw an inscrutable error message about |method| rather than this nice
michael@0 1330 // one about |obj|.
michael@0 1331 if (!callee.isObject() || !callee.toObject().isCallable()) {
michael@0 1332 if (nonIterableBehavior == AllowNonIterable)
michael@0 1333 return true;
michael@0 1334 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, iterable, NullPtr());
michael@0 1335 if (!bytes)
michael@0 1336 return false;
michael@0 1337 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, bytes);
michael@0 1338 js_free(bytes);
michael@0 1339 return false;
michael@0 1340 }
michael@0 1341
michael@0 1342 args.setCallee(callee);
michael@0 1343 if (!Invoke(cx, args))
michael@0 1344 return false;
michael@0 1345
michael@0 1346 iterator = ToObject(cx, args.rval());
michael@0 1347 if (!iterator)
michael@0 1348 return false;
michael@0 1349
michael@0 1350 return true;
michael@0 1351 }
michael@0 1352
michael@0 1353 inline bool
michael@0 1354 ForOfIterator::nextFromOptimizedArray(MutableHandleValue vp, bool *done)
michael@0 1355 {
michael@0 1356 JS_ASSERT(index != NOT_ARRAY);
michael@0 1357
michael@0 1358 if (!CheckForInterrupt(cx_))
michael@0 1359 return false;
michael@0 1360
michael@0 1361 JS_ASSERT(iterator->isNative());
michael@0 1362 JS_ASSERT(iterator->is<ArrayObject>());
michael@0 1363
michael@0 1364 if (index >= iterator->as<ArrayObject>().length()) {
michael@0 1365 vp.setUndefined();
michael@0 1366 *done = true;
michael@0 1367 return true;
michael@0 1368 }
michael@0 1369 *done = false;
michael@0 1370
michael@0 1371 // Try to get array element via direct access.
michael@0 1372 if (index < iterator->getDenseInitializedLength()) {
michael@0 1373 vp.set(iterator->getDenseElement(index));
michael@0 1374 if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
michael@0 1375 ++index;
michael@0 1376 return true;
michael@0 1377 }
michael@0 1378 }
michael@0 1379
michael@0 1380 return JSObject::getElement(cx_, iterator, iterator, index++, vp);
michael@0 1381 }
michael@0 1382
michael@0 1383 bool
michael@0 1384 ForOfIterator::next(MutableHandleValue vp, bool *done)
michael@0 1385 {
michael@0 1386 JS_ASSERT(iterator);
michael@0 1387
michael@0 1388 if (index != NOT_ARRAY) {
michael@0 1389 ForOfPIC::Chain *stubChain = ForOfPIC::getOrCreate(cx_);
michael@0 1390 if (!stubChain)
michael@0 1391 return false;
michael@0 1392
michael@0 1393 if (stubChain->isArrayNextStillSane())
michael@0 1394 return nextFromOptimizedArray(vp, done);
michael@0 1395
michael@0 1396 // ArrayIterator.prototype.next changed, materialize a proper
michael@0 1397 // ArrayIterator instance and fall through to slowpath case.
michael@0 1398 if (!materializeArrayIterator())
michael@0 1399 return false;
michael@0 1400 }
michael@0 1401
michael@0 1402 RootedValue method(cx_);
michael@0 1403 if (!JSObject::getProperty(cx_, iterator, iterator, cx_->names().next, &method))
michael@0 1404 return false;
michael@0 1405
michael@0 1406 InvokeArgs args(cx_);
michael@0 1407 if (!args.init(1))
michael@0 1408 return false;
michael@0 1409 args.setCallee(method);
michael@0 1410 args.setThis(ObjectValue(*iterator));
michael@0 1411 args[0].setUndefined();
michael@0 1412 if (!Invoke(cx_, args))
michael@0 1413 return false;
michael@0 1414
michael@0 1415 RootedObject resultObj(cx_, ToObject(cx_, args.rval()));
michael@0 1416 if (!resultObj)
michael@0 1417 return false;
michael@0 1418 RootedValue doneVal(cx_);
michael@0 1419 if (!JSObject::getProperty(cx_, resultObj, resultObj, cx_->names().done, &doneVal))
michael@0 1420 return false;
michael@0 1421 *done = ToBoolean(doneVal);
michael@0 1422 if (*done) {
michael@0 1423 vp.setUndefined();
michael@0 1424 return true;
michael@0 1425 }
michael@0 1426 return JSObject::getProperty(cx_, resultObj, resultObj, cx_->names().value, vp);
michael@0 1427 }
michael@0 1428
michael@0 1429 bool
michael@0 1430 ForOfIterator::materializeArrayIterator()
michael@0 1431 {
michael@0 1432 JS_ASSERT(index != NOT_ARRAY);
michael@0 1433
michael@0 1434 const char *nameString = "ArrayValuesAt";
michael@0 1435
michael@0 1436 RootedAtom name(cx_, Atomize(cx_, nameString, strlen(nameString)));
michael@0 1437 if (!name)
michael@0 1438 return false;
michael@0 1439
michael@0 1440 RootedValue val(cx_);
michael@0 1441 if (!cx_->global()->getSelfHostedFunction(cx_, name, name, 1, &val))
michael@0 1442 return false;
michael@0 1443
michael@0 1444 InvokeArgs args(cx_);
michael@0 1445 if (!args.init(1))
michael@0 1446 return false;
michael@0 1447 args.setCallee(val);
michael@0 1448 args.setThis(ObjectValue(*iterator));
michael@0 1449 args[0].set(Int32Value(index));
michael@0 1450 if (!Invoke(cx_, args))
michael@0 1451 return false;
michael@0 1452
michael@0 1453 index = NOT_ARRAY;
michael@0 1454 // Result of call to ArrayValuesAt must be an object.
michael@0 1455 iterator = &args.rval().toObject();
michael@0 1456 return true;
michael@0 1457 }
michael@0 1458
michael@0 1459 /*** Generators **********************************************************************************/
michael@0 1460
michael@0 1461 template<typename T>
michael@0 1462 static void
michael@0 1463 FinalizeGenerator(FreeOp *fop, JSObject *obj)
michael@0 1464 {
michael@0 1465 JS_ASSERT(obj->is<T>());
michael@0 1466 JSGenerator *gen = obj->as<T>().getGenerator();
michael@0 1467 JS_ASSERT(gen);
michael@0 1468 // gen is open when a script has not called its close method while
michael@0 1469 // explicitly manipulating it.
michael@0 1470 JS_ASSERT(gen->state == JSGEN_NEWBORN ||
michael@0 1471 gen->state == JSGEN_CLOSED ||
michael@0 1472 gen->state == JSGEN_OPEN);
michael@0 1473 // If gen->state is JSGEN_CLOSED, gen->fp may be nullptr.
michael@0 1474 if (gen->fp)
michael@0 1475 JS_POISON(gen->fp, JS_SWEPT_FRAME_PATTERN, sizeof(InterpreterFrame));
michael@0 1476 JS_POISON(gen, JS_SWEPT_FRAME_PATTERN, sizeof(JSGenerator));
michael@0 1477 fop->free_(gen);
michael@0 1478 }
michael@0 1479
michael@0 1480 static void
michael@0 1481 MarkGeneratorFrame(JSTracer *trc, JSGenerator *gen)
michael@0 1482 {
michael@0 1483 MarkValueRange(trc,
michael@0 1484 HeapValueify(gen->fp->generatorArgsSnapshotBegin()),
michael@0 1485 HeapValueify(gen->fp->generatorArgsSnapshotEnd()),
michael@0 1486 "Generator Floating Args");
michael@0 1487 gen->fp->mark(trc);
michael@0 1488 MarkValueRange(trc,
michael@0 1489 HeapValueify(gen->fp->generatorSlotsSnapshotBegin()),
michael@0 1490 HeapValueify(gen->regs.sp),
michael@0 1491 "Generator Floating Stack");
michael@0 1492 }
michael@0 1493
michael@0 1494 static void
michael@0 1495 GeneratorWriteBarrierPre(JSContext *cx, JSGenerator *gen)
michael@0 1496 {
michael@0 1497 JS::Zone *zone = cx->zone();
michael@0 1498 if (zone->needsBarrier())
michael@0 1499 MarkGeneratorFrame(zone->barrierTracer(), gen);
michael@0 1500 }
michael@0 1501
michael@0 1502 static void
michael@0 1503 GeneratorWriteBarrierPost(JSContext *cx, JSGenerator *gen)
michael@0 1504 {
michael@0 1505 #ifdef JSGC_GENERATIONAL
michael@0 1506 cx->runtime()->gcStoreBuffer.putWholeCell(gen->obj);
michael@0 1507 #endif
michael@0 1508 }
michael@0 1509
michael@0 1510 /*
michael@0 1511 * Only mark generator frames/slots when the generator is not active on the
michael@0 1512 * stack or closed. Barriers when copying onto the stack or closing preserve
michael@0 1513 * gc invariants.
michael@0 1514 */
michael@0 1515 static bool
michael@0 1516 GeneratorHasMarkableFrame(JSGenerator *gen)
michael@0 1517 {
michael@0 1518 return gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN;
michael@0 1519 }
michael@0 1520
michael@0 1521 /*
michael@0 1522 * When a generator is closed, the GC things reachable from the contained frame
michael@0 1523 * and slots become unreachable and thus require a write barrier.
michael@0 1524 */
michael@0 1525 static void
michael@0 1526 SetGeneratorClosed(JSContext *cx, JSGenerator *gen)
michael@0 1527 {
michael@0 1528 JS_ASSERT(gen->state != JSGEN_CLOSED);
michael@0 1529 if (GeneratorHasMarkableFrame(gen))
michael@0 1530 GeneratorWriteBarrierPre(cx, gen);
michael@0 1531 gen->state = JSGEN_CLOSED;
michael@0 1532
michael@0 1533 #ifdef DEBUG
michael@0 1534 MakeRangeGCSafe(gen->fp->generatorArgsSnapshotBegin(),
michael@0 1535 gen->fp->generatorArgsSnapshotEnd());
michael@0 1536 MakeRangeGCSafe(gen->fp->generatorSlotsSnapshotBegin(),
michael@0 1537 gen->regs.sp);
michael@0 1538 PodZero(&gen->regs, 1);
michael@0 1539 gen->fp = nullptr;
michael@0 1540 #endif
michael@0 1541 }
michael@0 1542
michael@0 1543 template<typename T>
michael@0 1544 static void
michael@0 1545 TraceGenerator(JSTracer *trc, JSObject *obj)
michael@0 1546 {
michael@0 1547 JS_ASSERT(obj->is<T>());
michael@0 1548 JSGenerator *gen = obj->as<T>().getGenerator();
michael@0 1549 JS_ASSERT(gen);
michael@0 1550 if (GeneratorHasMarkableFrame(gen))
michael@0 1551 MarkGeneratorFrame(trc, gen);
michael@0 1552 }
michael@0 1553
michael@0 1554 GeneratorState::GeneratorState(JSContext *cx, JSGenerator *gen, JSGeneratorState futureState)
michael@0 1555 : RunState(cx, Generator, gen->fp->script()),
michael@0 1556 cx_(cx),
michael@0 1557 gen_(gen),
michael@0 1558 futureState_(futureState),
michael@0 1559 entered_(false)
michael@0 1560 { }
michael@0 1561
michael@0 1562 GeneratorState::~GeneratorState()
michael@0 1563 {
michael@0 1564 gen_->fp->setSuspended();
michael@0 1565
michael@0 1566 if (entered_)
michael@0 1567 cx_->leaveGenerator(gen_);
michael@0 1568 }
michael@0 1569
michael@0 1570 InterpreterFrame *
michael@0 1571 GeneratorState::pushInterpreterFrame(JSContext *cx)
michael@0 1572 {
michael@0 1573 /*
michael@0 1574 * Write barrier is needed since the generator stack can be updated,
michael@0 1575 * and it's not barriered in any other way. We need to do it before
michael@0 1576 * gen->state changes, which can cause us to trace the generator
michael@0 1577 * differently.
michael@0 1578 *
michael@0 1579 * We could optimize this by setting a bit on the generator to signify
michael@0 1580 * that it has been marked. If this bit has already been set, there is no
michael@0 1581 * need to mark again. The bit would have to be reset before the next GC,
michael@0 1582 * or else some kind of epoch scheme would have to be used.
michael@0 1583 */
michael@0 1584 GeneratorWriteBarrierPre(cx, gen_);
michael@0 1585 gen_->state = futureState_;
michael@0 1586
michael@0 1587 gen_->fp->clearSuspended();
michael@0 1588
michael@0 1589 cx->enterGenerator(gen_); /* OOM check above. */
michael@0 1590 entered_ = true;
michael@0 1591 return gen_->fp;
michael@0 1592 }
michael@0 1593
michael@0 1594 const Class LegacyGeneratorObject::class_ = {
michael@0 1595 "Generator",
michael@0 1596 JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
michael@0 1597 JS_PropertyStub, /* addProperty */
michael@0 1598 JS_DeletePropertyStub, /* delProperty */
michael@0 1599 JS_PropertyStub, /* getProperty */
michael@0 1600 JS_StrictPropertyStub, /* setProperty */
michael@0 1601 JS_EnumerateStub,
michael@0 1602 JS_ResolveStub,
michael@0 1603 JS_ConvertStub,
michael@0 1604 FinalizeGenerator<LegacyGeneratorObject>,
michael@0 1605 nullptr, /* call */
michael@0 1606 nullptr, /* hasInstance */
michael@0 1607 nullptr, /* construct */
michael@0 1608 TraceGenerator<LegacyGeneratorObject>,
michael@0 1609 JS_NULL_CLASS_SPEC,
michael@0 1610 {
michael@0 1611 nullptr, /* outerObject */
michael@0 1612 nullptr, /* innerObject */
michael@0 1613 iterator_iteratorObject,
michael@0 1614 }
michael@0 1615 };
michael@0 1616
michael@0 1617 const Class StarGeneratorObject::class_ = {
michael@0 1618 "Generator",
michael@0 1619 JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
michael@0 1620 JS_PropertyStub, /* addProperty */
michael@0 1621 JS_DeletePropertyStub, /* delProperty */
michael@0 1622 JS_PropertyStub, /* getProperty */
michael@0 1623 JS_StrictPropertyStub, /* setProperty */
michael@0 1624 JS_EnumerateStub,
michael@0 1625 JS_ResolveStub,
michael@0 1626 JS_ConvertStub,
michael@0 1627 FinalizeGenerator<StarGeneratorObject>,
michael@0 1628 nullptr, /* call */
michael@0 1629 nullptr, /* hasInstance */
michael@0 1630 nullptr, /* construct */
michael@0 1631 TraceGenerator<StarGeneratorObject>,
michael@0 1632 JS_NULL_CLASS_SPEC,
michael@0 1633 {
michael@0 1634 nullptr, /* outerObject */
michael@0 1635 nullptr, /* innerObject */
michael@0 1636 iterator_iteratorObject,
michael@0 1637 }
michael@0 1638 };
michael@0 1639
michael@0 1640 /*
michael@0 1641 * Called from the JSOP_GENERATOR case in the interpreter, with fp referring
michael@0 1642 * to the frame by which the generator function was activated. Create a new
michael@0 1643 * JSGenerator object, which contains its own InterpreterFrame that we populate
michael@0 1644 * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return
michael@0 1645 * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
michael@0 1646 * if they are non-null.
michael@0 1647 */
michael@0 1648 JSObject *
michael@0 1649 js_NewGenerator(JSContext *cx, const InterpreterRegs &stackRegs)
michael@0 1650 {
michael@0 1651 JS_ASSERT(stackRegs.stackDepth() == 0);
michael@0 1652 InterpreterFrame *stackfp = stackRegs.fp();
michael@0 1653
michael@0 1654 JS_ASSERT(stackfp->script()->isGenerator());
michael@0 1655
michael@0 1656 Rooted<GlobalObject*> global(cx, &stackfp->global());
michael@0 1657 RootedObject obj(cx);
michael@0 1658 if (stackfp->script()->isStarGenerator()) {
michael@0 1659 RootedValue pval(cx);
michael@0 1660 RootedObject fun(cx, stackfp->fun());
michael@0 1661 // FIXME: This would be faster if we could avoid doing a lookup to get
michael@0 1662 // the prototype for the instance. Bug 906600.
michael@0 1663 if (!JSObject::getProperty(cx, fun, fun, cx->names().prototype, &pval))
michael@0 1664 return nullptr;
michael@0 1665 JSObject *proto = pval.isObject() ? &pval.toObject() : nullptr;
michael@0 1666 if (!proto) {
michael@0 1667 proto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global);
michael@0 1668 if (!proto)
michael@0 1669 return nullptr;
michael@0 1670 }
michael@0 1671 obj = NewObjectWithGivenProto(cx, &StarGeneratorObject::class_, proto, global);
michael@0 1672 } else {
michael@0 1673 JS_ASSERT(stackfp->script()->isLegacyGenerator());
michael@0 1674 JSObject *proto = GlobalObject::getOrCreateLegacyGeneratorObjectPrototype(cx, global);
michael@0 1675 if (!proto)
michael@0 1676 return nullptr;
michael@0 1677 obj = NewObjectWithGivenProto(cx, &LegacyGeneratorObject::class_, proto, global);
michael@0 1678 }
michael@0 1679 if (!obj)
michael@0 1680 return nullptr;
michael@0 1681
michael@0 1682 /* Load and compute stack slot counts. */
michael@0 1683 Value *stackvp = stackfp->generatorArgsSnapshotBegin();
michael@0 1684 unsigned vplen = stackfp->generatorArgsSnapshotEnd() - stackvp;
michael@0 1685
michael@0 1686 /* Compute JSGenerator size. */
michael@0 1687 unsigned nbytes = sizeof(JSGenerator) +
michael@0 1688 (-1 + /* one Value included in JSGenerator */
michael@0 1689 vplen +
michael@0 1690 VALUES_PER_STACK_FRAME +
michael@0 1691 stackfp->script()->nslots()) * sizeof(HeapValue);
michael@0 1692
michael@0 1693 JS_ASSERT(nbytes % sizeof(Value) == 0);
michael@0 1694 JS_STATIC_ASSERT(sizeof(InterpreterFrame) % sizeof(HeapValue) == 0);
michael@0 1695
michael@0 1696 JSGenerator *gen = (JSGenerator *) cx->calloc_(nbytes);
michael@0 1697 if (!gen)
michael@0 1698 return nullptr;
michael@0 1699
michael@0 1700 /* Cut up floatingStack space. */
michael@0 1701 HeapValue *genvp = gen->stackSnapshot;
michael@0 1702 SetValueRangeToUndefined((Value *)genvp, vplen);
michael@0 1703
michael@0 1704 InterpreterFrame *genfp = reinterpret_cast<InterpreterFrame *>(genvp + vplen);
michael@0 1705
michael@0 1706 /* Initialize JSGenerator. */
michael@0 1707 gen->obj.init(obj);
michael@0 1708 gen->state = JSGEN_NEWBORN;
michael@0 1709 gen->fp = genfp;
michael@0 1710 gen->prevGenerator = nullptr;
michael@0 1711
michael@0 1712 /* Copy from the stack to the generator's floating frame. */
michael@0 1713 gen->regs.rebaseFromTo(stackRegs, *genfp);
michael@0 1714 genfp->copyFrameAndValues<InterpreterFrame::DoPostBarrier>(cx, (Value *)genvp, stackfp,
michael@0 1715 stackvp, stackRegs.sp);
michael@0 1716 genfp->setSuspended();
michael@0 1717 obj->setPrivate(gen);
michael@0 1718 return obj;
michael@0 1719 }
michael@0 1720
michael@0 1721 static void
michael@0 1722 SetGeneratorClosed(JSContext *cx, JSGenerator *gen);
michael@0 1723
michael@0 1724 typedef enum JSGeneratorOp {
michael@0 1725 JSGENOP_NEXT,
michael@0 1726 JSGENOP_SEND,
michael@0 1727 JSGENOP_THROW,
michael@0 1728 JSGENOP_CLOSE
michael@0 1729 } JSGeneratorOp;
michael@0 1730
michael@0 1731 /*
michael@0 1732 * Start newborn or restart yielding generator and perform the requested
michael@0 1733 * operation inside its frame.
michael@0 1734 */
michael@0 1735 static bool
michael@0 1736 SendToGenerator(JSContext *cx, JSGeneratorOp op, HandleObject obj,
michael@0 1737 JSGenerator *gen, HandleValue arg, GeneratorKind generatorKind,
michael@0 1738 MutableHandleValue rval)
michael@0 1739 {
michael@0 1740 JS_ASSERT(generatorKind == LegacyGenerator || generatorKind == StarGenerator);
michael@0 1741
michael@0 1742 if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
michael@0 1743 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NESTING_GENERATOR);
michael@0 1744 return false;
michael@0 1745 }
michael@0 1746
michael@0 1747 JSGeneratorState futureState;
michael@0 1748 JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
michael@0 1749 switch (op) {
michael@0 1750 case JSGENOP_NEXT:
michael@0 1751 case JSGENOP_SEND:
michael@0 1752 if (gen->state == JSGEN_OPEN) {
michael@0 1753 /*
michael@0 1754 * Store the argument to send as the result of the yield
michael@0 1755 * expression. The generator stack is not barriered, so we need
michael@0 1756 * write barriers here.
michael@0 1757 */
michael@0 1758 HeapValue::writeBarrierPre(gen->regs.sp[-1]);
michael@0 1759 gen->regs.sp[-1] = arg;
michael@0 1760 HeapValue::writeBarrierPost(cx->runtime(), gen->regs.sp[-1], &gen->regs.sp[-1]);
michael@0 1761 }
michael@0 1762 futureState = JSGEN_RUNNING;
michael@0 1763 break;
michael@0 1764
michael@0 1765 case JSGENOP_THROW:
michael@0 1766 cx->setPendingException(arg);
michael@0 1767 futureState = JSGEN_RUNNING;
michael@0 1768 break;
michael@0 1769
michael@0 1770 default:
michael@0 1771 JS_ASSERT(op == JSGENOP_CLOSE);
michael@0 1772 JS_ASSERT(generatorKind == LegacyGenerator);
michael@0 1773 cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
michael@0 1774 futureState = JSGEN_CLOSING;
michael@0 1775 break;
michael@0 1776 }
michael@0 1777
michael@0 1778 bool ok;
michael@0 1779 {
michael@0 1780 GeneratorState state(cx, gen, futureState);
michael@0 1781 ok = RunScript(cx, state);
michael@0 1782 if (!ok && gen->state == JSGEN_CLOSED)
michael@0 1783 return false;
michael@0 1784 }
michael@0 1785
michael@0 1786 if (gen->fp->isYielding()) {
michael@0 1787 /*
michael@0 1788 * Yield is ordinarily infallible, but ok can be false here if a
michael@0 1789 * Debugger.Frame.onPop hook fails.
michael@0 1790 */
michael@0 1791 JS_ASSERT(gen->state == JSGEN_RUNNING);
michael@0 1792 JS_ASSERT(op != JSGENOP_CLOSE);
michael@0 1793 gen->fp->clearYielding();
michael@0 1794 gen->state = JSGEN_OPEN;
michael@0 1795 GeneratorWriteBarrierPost(cx, gen);
michael@0 1796 rval.set(gen->fp->returnValue());
michael@0 1797 return ok;
michael@0 1798 }
michael@0 1799
michael@0 1800 if (ok) {
michael@0 1801 if (generatorKind == StarGenerator) {
michael@0 1802 // Star generators return a {value:FOO, done:true} object.
michael@0 1803 rval.set(gen->fp->returnValue());
michael@0 1804 } else {
michael@0 1805 JS_ASSERT(generatorKind == LegacyGenerator);
michael@0 1806
michael@0 1807 // Otherwise we discard the return value and throw a StopIteration
michael@0 1808 // if needed.
michael@0 1809 rval.setUndefined();
michael@0 1810 if (op != JSGENOP_CLOSE)
michael@0 1811 ok = js_ThrowStopIteration(cx);
michael@0 1812 }
michael@0 1813 }
michael@0 1814
michael@0 1815 SetGeneratorClosed(cx, gen);
michael@0 1816 return ok;
michael@0 1817 }
michael@0 1818
michael@0 1819 MOZ_ALWAYS_INLINE bool
michael@0 1820 star_generator_next(JSContext *cx, CallArgs args)
michael@0 1821 {
michael@0 1822 RootedObject thisObj(cx, &args.thisv().toObject());
michael@0 1823 JSGenerator *gen = thisObj->as<StarGeneratorObject>().getGenerator();
michael@0 1824
michael@0 1825 if (gen->state == JSGEN_CLOSED) {
michael@0 1826 RootedObject obj(cx, CreateItrResultObject(cx, JS::UndefinedHandleValue, true));
michael@0 1827 if (!obj)
michael@0 1828 return false;
michael@0 1829 args.rval().setObject(*obj);
michael@0 1830 return true;
michael@0 1831 }
michael@0 1832
michael@0 1833 if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) {
michael@0 1834 RootedValue val(cx, args[0]);
michael@0 1835 js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
michael@0 1836 JSDVG_SEARCH_STACK, val, js::NullPtr());
michael@0 1837 return false;
michael@0 1838 }
michael@0 1839
michael@0 1840 return SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0), StarGenerator,
michael@0 1841 args.rval());
michael@0 1842 }
michael@0 1843
michael@0 1844 MOZ_ALWAYS_INLINE bool
michael@0 1845 star_generator_throw(JSContext *cx, CallArgs args)
michael@0 1846 {
michael@0 1847 RootedObject thisObj(cx, &args.thisv().toObject());
michael@0 1848
michael@0 1849 JSGenerator *gen = thisObj->as<StarGeneratorObject>().getGenerator();
michael@0 1850 if (gen->state == JSGEN_CLOSED) {
michael@0 1851 cx->setPendingException(args.get(0));
michael@0 1852 return false;
michael@0 1853 }
michael@0 1854
michael@0 1855 return SendToGenerator(cx, JSGENOP_THROW, thisObj, gen, args.get(0), StarGenerator,
michael@0 1856 args.rval());
michael@0 1857 }
michael@0 1858
michael@0 1859 MOZ_ALWAYS_INLINE bool
michael@0 1860 legacy_generator_next(JSContext *cx, CallArgs args)
michael@0 1861 {
michael@0 1862 RootedObject thisObj(cx, &args.thisv().toObject());
michael@0 1863
michael@0 1864 JSGenerator *gen = thisObj->as<LegacyGeneratorObject>().getGenerator();
michael@0 1865 if (gen->state == JSGEN_CLOSED)
michael@0 1866 return js_ThrowStopIteration(cx);
michael@0 1867
michael@0 1868 if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) {
michael@0 1869 RootedValue val(cx, args[0]);
michael@0 1870 js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
michael@0 1871 JSDVG_SEARCH_STACK, val, js::NullPtr());
michael@0 1872 return false;
michael@0 1873 }
michael@0 1874
michael@0 1875 return SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0), LegacyGenerator,
michael@0 1876 args.rval());
michael@0 1877 }
michael@0 1878
michael@0 1879 MOZ_ALWAYS_INLINE bool
michael@0 1880 legacy_generator_throw(JSContext *cx, CallArgs args)
michael@0 1881 {
michael@0 1882 RootedObject thisObj(cx, &args.thisv().toObject());
michael@0 1883
michael@0 1884 JSGenerator *gen = thisObj->as<LegacyGeneratorObject>().getGenerator();
michael@0 1885 if (gen->state == JSGEN_CLOSED) {
michael@0 1886 cx->setPendingException(args.length() >= 1 ? args[0] : UndefinedValue());
michael@0 1887 return false;
michael@0 1888 }
michael@0 1889
michael@0 1890 return SendToGenerator(cx, JSGENOP_THROW, thisObj, gen, args.get(0), LegacyGenerator,
michael@0 1891 args.rval());
michael@0 1892 }
michael@0 1893
michael@0 1894 static bool
michael@0 1895 CloseLegacyGenerator(JSContext *cx, HandleObject obj, MutableHandleValue rval)
michael@0 1896 {
michael@0 1897 JS_ASSERT(obj->is<LegacyGeneratorObject>());
michael@0 1898
michael@0 1899 JSGenerator *gen = obj->as<LegacyGeneratorObject>().getGenerator();
michael@0 1900
michael@0 1901 if (gen->state == JSGEN_CLOSED) {
michael@0 1902 rval.setUndefined();
michael@0 1903 return true;
michael@0 1904 }
michael@0 1905
michael@0 1906 if (gen->state == JSGEN_NEWBORN) {
michael@0 1907 SetGeneratorClosed(cx, gen);
michael@0 1908 rval.setUndefined();
michael@0 1909 return true;
michael@0 1910 }
michael@0 1911
michael@0 1912 return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, JS::UndefinedHandleValue, LegacyGenerator,
michael@0 1913 rval);
michael@0 1914 }
michael@0 1915
michael@0 1916 static bool
michael@0 1917 CloseLegacyGenerator(JSContext *cx, HandleObject obj)
michael@0 1918 {
michael@0 1919 RootedValue rval(cx);
michael@0 1920 return CloseLegacyGenerator(cx, obj, &rval);
michael@0 1921 }
michael@0 1922
michael@0 1923 MOZ_ALWAYS_INLINE bool
michael@0 1924 legacy_generator_close(JSContext *cx, CallArgs args)
michael@0 1925 {
michael@0 1926 RootedObject thisObj(cx, &args.thisv().toObject());
michael@0 1927
michael@0 1928 return CloseLegacyGenerator(cx, thisObj, args.rval());
michael@0 1929 }
michael@0 1930
michael@0 1931 template<typename T>
michael@0 1932 MOZ_ALWAYS_INLINE bool
michael@0 1933 IsObjectOfType(HandleValue v)
michael@0 1934 {
michael@0 1935 return v.isObject() && v.toObject().is<T>();
michael@0 1936 }
michael@0 1937
michael@0 1938 template<typename T, NativeImpl Impl>
michael@0 1939 static bool
michael@0 1940 NativeMethod(JSContext *cx, unsigned argc, Value *vp)
michael@0 1941 {
michael@0 1942 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 1943 return CallNonGenericMethod<IsObjectOfType<T>, Impl>(cx, args);
michael@0 1944 }
michael@0 1945
michael@0 1946 #define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
michael@0 1947 #define JS_METHOD(name, T, impl, len, attrs) JS_FN(name, (NativeMethod<T,impl>), len, attrs)
michael@0 1948
michael@0 1949 static const JSFunctionSpec star_generator_methods[] = {
michael@0 1950 JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0),
michael@0 1951 JS_METHOD("next", StarGeneratorObject, star_generator_next, 1, 0),
michael@0 1952 JS_METHOD("throw", StarGeneratorObject, star_generator_throw, 1, 0),
michael@0 1953 JS_FS_END
michael@0 1954 };
michael@0 1955
michael@0 1956 static const JSFunctionSpec legacy_generator_methods[] = {
michael@0 1957 JS_SELF_HOSTED_FN("@@iterator", "LegacyGeneratorIteratorShim", 0, 0),
michael@0 1958 // "send" is an alias for "next".
michael@0 1959 JS_METHOD("next", LegacyGeneratorObject, legacy_generator_next, 1, JSPROP_ROPERM),
michael@0 1960 JS_METHOD("send", LegacyGeneratorObject, legacy_generator_next, 1, JSPROP_ROPERM),
michael@0 1961 JS_METHOD("throw", LegacyGeneratorObject, legacy_generator_throw, 1, JSPROP_ROPERM),
michael@0 1962 JS_METHOD("close", LegacyGeneratorObject, legacy_generator_close, 0, JSPROP_ROPERM),
michael@0 1963 JS_FS_END
michael@0 1964 };
michael@0 1965
michael@0 1966 static JSObject*
michael@0 1967 NewSingletonObjectWithObjectPrototype(JSContext *cx, Handle<GlobalObject *> global)
michael@0 1968 {
michael@0 1969 JSObject *proto = global->getOrCreateObjectPrototype(cx);
michael@0 1970 if (!proto)
michael@0 1971 return nullptr;
michael@0 1972 return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject);
michael@0 1973 }
michael@0 1974
michael@0 1975 static JSObject*
michael@0 1976 NewSingletonObjectWithFunctionPrototype(JSContext *cx, Handle<GlobalObject *> global)
michael@0 1977 {
michael@0 1978 JSObject *proto = global->getOrCreateFunctionPrototype(cx);
michael@0 1979 if (!proto)
michael@0 1980 return nullptr;
michael@0 1981 return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject);
michael@0 1982 }
michael@0 1983
michael@0 1984 /* static */ bool
michael@0 1985 GlobalObject::initIteratorClasses(JSContext *cx, Handle<GlobalObject *> global)
michael@0 1986 {
michael@0 1987 RootedObject iteratorProto(cx);
michael@0 1988 Value iteratorProtoVal = global->getPrototype(JSProto_Iterator);
michael@0 1989 if (iteratorProtoVal.isObject()) {
michael@0 1990 iteratorProto = &iteratorProtoVal.toObject();
michael@0 1991 } else {
michael@0 1992 iteratorProto = global->createBlankPrototype(cx, &PropertyIteratorObject::class_);
michael@0 1993 if (!iteratorProto)
michael@0 1994 return false;
michael@0 1995
michael@0 1996 AutoIdVector blank(cx);
michael@0 1997 NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, blank);
michael@0 1998 if (!ni)
michael@0 1999 return false;
michael@0 2000 ni->init(nullptr, nullptr, 0 /* flags */, 0, 0);
michael@0 2001
michael@0 2002 iteratorProto->as<PropertyIteratorObject>().setNativeIterator(ni);
michael@0 2003
michael@0 2004 Rooted<JSFunction*> ctor(cx);
michael@0 2005 ctor = global->createConstructor(cx, IteratorConstructor, cx->names().Iterator, 2);
michael@0 2006 if (!ctor)
michael@0 2007 return false;
michael@0 2008 if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto))
michael@0 2009 return false;
michael@0 2010 if (!DefinePropertiesAndBrand(cx, iteratorProto, nullptr, iterator_methods))
michael@0 2011 return false;
michael@0 2012 if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Iterator,
michael@0 2013 ctor, iteratorProto))
michael@0 2014 {
michael@0 2015 return false;
michael@0 2016 }
michael@0 2017 }
michael@0 2018
michael@0 2019 RootedObject proto(cx);
michael@0 2020 if (global->getSlot(ARRAY_ITERATOR_PROTO).isUndefined()) {
michael@0 2021 const Class *cls = &ArrayIteratorObject::class_;
michael@0 2022 proto = global->createBlankPrototypeInheriting(cx, cls, *iteratorProto);
michael@0 2023 if (!proto || !DefinePropertiesAndBrand(cx, proto, nullptr, array_iterator_methods))
michael@0 2024 return false;
michael@0 2025 global->setReservedSlot(ARRAY_ITERATOR_PROTO, ObjectValue(*proto));
michael@0 2026 }
michael@0 2027
michael@0 2028 if (global->getSlot(STRING_ITERATOR_PROTO).isUndefined()) {
michael@0 2029 const Class *cls = &StringIteratorPrototypeClass;
michael@0 2030 proto = global->createBlankPrototype(cx, cls);
michael@0 2031 if (!proto || !DefinePropertiesAndBrand(cx, proto, nullptr, string_iterator_methods))
michael@0 2032 return false;
michael@0 2033 global->setReservedSlot(STRING_ITERATOR_PROTO, ObjectValue(*proto));
michael@0 2034 }
michael@0 2035
michael@0 2036 if (global->getSlot(LEGACY_GENERATOR_OBJECT_PROTO).isUndefined()) {
michael@0 2037 proto = NewSingletonObjectWithObjectPrototype(cx, global);
michael@0 2038 if (!proto || !DefinePropertiesAndBrand(cx, proto, nullptr, legacy_generator_methods))
michael@0 2039 return false;
michael@0 2040 global->setReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO, ObjectValue(*proto));
michael@0 2041 }
michael@0 2042
michael@0 2043 if (global->getSlot(STAR_GENERATOR_OBJECT_PROTO).isUndefined()) {
michael@0 2044 RootedObject genObjectProto(cx, NewSingletonObjectWithObjectPrototype(cx, global));
michael@0 2045 if (!genObjectProto)
michael@0 2046 return false;
michael@0 2047 if (!DefinePropertiesAndBrand(cx, genObjectProto, nullptr, star_generator_methods))
michael@0 2048 return false;
michael@0 2049
michael@0 2050 RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
michael@0 2051 if (!genFunctionProto)
michael@0 2052 return false;
michael@0 2053 if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto))
michael@0 2054 return false;
michael@0 2055
michael@0 2056 RootedValue function(cx, global->getConstructor(JSProto_Function));
michael@0 2057 if (!function.toObjectOrNull())
michael@0 2058 return false;
michael@0 2059 RootedAtom name(cx, cx->names().GeneratorFunction);
michael@0 2060 RootedObject genFunction(cx, NewFunctionWithProto(cx, NullPtr(), Generator, 1,
michael@0 2061 JSFunction::NATIVE_CTOR, global, name,
michael@0 2062 &function.toObject()));
michael@0 2063 if (!genFunction)
michael@0 2064 return false;
michael@0 2065 if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto))
michael@0 2066 return false;
michael@0 2067
michael@0 2068 global->setSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto));
michael@0 2069 global->setConstructor(JSProto_GeneratorFunction, ObjectValue(*genFunction));
michael@0 2070 global->setPrototype(JSProto_GeneratorFunction, ObjectValue(*genFunctionProto));
michael@0 2071 }
michael@0 2072
michael@0 2073 if (global->getPrototype(JSProto_StopIteration).isUndefined()) {
michael@0 2074 proto = global->createBlankPrototype(cx, &StopIterationObject::class_);
michael@0 2075 if (!proto || !JSObject::freeze(cx, proto))
michael@0 2076 return false;
michael@0 2077
michael@0 2078 // This should use a non-JSProtoKey'd slot, but this is easier for now.
michael@0 2079 if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_StopIteration, proto, proto))
michael@0 2080 return false;
michael@0 2081
michael@0 2082 global->setConstructor(JSProto_StopIteration, ObjectValue(*proto));
michael@0 2083 }
michael@0 2084
michael@0 2085 return true;
michael@0 2086 }
michael@0 2087
michael@0 2088 JSObject *
michael@0 2089 js_InitIteratorClasses(JSContext *cx, HandleObject obj)
michael@0 2090 {
michael@0 2091 Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
michael@0 2092 if (!GlobalObject::initIteratorClasses(cx, global))
michael@0 2093 return nullptr;
michael@0 2094 return global->getIteratorPrototype();
michael@0 2095 }

mercurial