1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsiter.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2095 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* JavaScript iterators. */ 1.11 + 1.12 +#include "jsiter.h" 1.13 + 1.14 +#include "mozilla/ArrayUtils.h" 1.15 +#include "mozilla/MemoryReporting.h" 1.16 +#include "mozilla/PodOperations.h" 1.17 + 1.18 +#include "jsarray.h" 1.19 +#include "jsatom.h" 1.20 +#include "jscntxt.h" 1.21 +#include "jsgc.h" 1.22 +#include "jsobj.h" 1.23 +#include "jsopcode.h" 1.24 +#include "jsproxy.h" 1.25 +#include "jsscript.h" 1.26 +#include "jstypes.h" 1.27 +#include "jsutil.h" 1.28 + 1.29 +#include "ds/Sort.h" 1.30 +#include "gc/Marking.h" 1.31 +#include "vm/GeneratorObject.h" 1.32 +#include "vm/GlobalObject.h" 1.33 +#include "vm/Interpreter.h" 1.34 +#include "vm/Shape.h" 1.35 +#include "vm/StopIterationObject.h" 1.36 + 1.37 +#include "jsinferinlines.h" 1.38 +#include "jsobjinlines.h" 1.39 +#include "jsscriptinlines.h" 1.40 + 1.41 +#include "vm/Stack-inl.h" 1.42 +#include "vm/String-inl.h" 1.43 + 1.44 +using namespace js; 1.45 +using namespace js::gc; 1.46 +using JS::ForOfIterator; 1.47 + 1.48 +using mozilla::ArrayLength; 1.49 +#ifdef JS_MORE_DETERMINISTIC 1.50 +using mozilla::PodCopy; 1.51 +#endif 1.52 +using mozilla::PodZero; 1.53 + 1.54 +typedef Rooted<PropertyIteratorObject*> RootedPropertyIteratorObject; 1.55 + 1.56 +static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::FINALIZE_OBJECT2_BACKGROUND; 1.57 + 1.58 +void 1.59 +NativeIterator::mark(JSTracer *trc) 1.60 +{ 1.61 + for (HeapPtr<JSFlatString> *str = begin(); str < end(); str++) 1.62 + MarkString(trc, str, "prop"); 1.63 + if (obj) 1.64 + MarkObject(trc, &obj, "obj"); 1.65 + 1.66 + // The SuppressDeletedPropertyHelper loop can GC, so make sure that if the 1.67 + // GC removes any elements from the list, it won't remove this one. 1.68 + if (iterObj_) 1.69 + MarkObjectUnbarriered(trc, &iterObj_, "iterObj"); 1.70 +} 1.71 + 1.72 +struct IdHashPolicy { 1.73 + typedef jsid Lookup; 1.74 + static HashNumber hash(jsid id) { 1.75 + return JSID_BITS(id); 1.76 + } 1.77 + static bool match(jsid id1, jsid id2) { 1.78 + return id1 == id2; 1.79 + } 1.80 +}; 1.81 + 1.82 +typedef HashSet<jsid, IdHashPolicy> IdSet; 1.83 + 1.84 +static inline bool 1.85 +NewKeyValuePair(JSContext *cx, jsid id, const Value &val, MutableHandleValue rval) 1.86 +{ 1.87 + JS::AutoValueArray<2> vec(cx); 1.88 + vec[0].set(IdToValue(id)); 1.89 + vec[1].set(val); 1.90 + 1.91 + JSObject *aobj = NewDenseCopiedArray(cx, 2, vec.begin()); 1.92 + if (!aobj) 1.93 + return false; 1.94 + rval.setObject(*aobj); 1.95 + return true; 1.96 +} 1.97 + 1.98 +static inline bool 1.99 +Enumerate(JSContext *cx, HandleObject pobj, jsid id, 1.100 + bool enumerable, unsigned flags, IdSet& ht, AutoIdVector *props) 1.101 +{ 1.102 + /* 1.103 + * We implement __proto__ using a property on |Object.prototype|, but 1.104 + * because __proto__ is highly deserving of removal, we don't want it to 1.105 + * show up in property enumeration, even if only for |Object.prototype| 1.106 + * (think introspection by Prototype-like frameworks that add methods to 1.107 + * the built-in prototypes). So exclude __proto__ if the object where the 1.108 + * property was found has no [[Prototype]] and might be |Object.prototype|. 1.109 + */ 1.110 + if (MOZ_UNLIKELY(!pobj->getTaggedProto().isObject() && JSID_IS_ATOM(id, cx->names().proto))) 1.111 + return true; 1.112 + 1.113 + if (!(flags & JSITER_OWNONLY) || pobj->is<ProxyObject>() || pobj->getOps()->enumerate) { 1.114 + /* If we've already seen this, we definitely won't add it. */ 1.115 + IdSet::AddPtr p = ht.lookupForAdd(id); 1.116 + if (MOZ_UNLIKELY(!!p)) 1.117 + return true; 1.118 + 1.119 + /* 1.120 + * It's not necessary to add properties to the hash table at the end of 1.121 + * the prototype chain, but custom enumeration behaviors might return 1.122 + * duplicated properties, so always add in such cases. 1.123 + */ 1.124 + if ((pobj->is<ProxyObject>() || pobj->getProto() || pobj->getOps()->enumerate) && !ht.add(p, id)) 1.125 + return false; 1.126 + } 1.127 + 1.128 + if (enumerable || (flags & JSITER_HIDDEN)) 1.129 + return props->append(id); 1.130 + 1.131 + return true; 1.132 +} 1.133 + 1.134 +static bool 1.135 +EnumerateNativeProperties(JSContext *cx, HandleObject pobj, unsigned flags, IdSet &ht, 1.136 + AutoIdVector *props) 1.137 +{ 1.138 + /* Collect any dense elements from this object. */ 1.139 + size_t initlen = pobj->getDenseInitializedLength(); 1.140 + const Value *vp = pobj->getDenseElements(); 1.141 + for (size_t i = 0; i < initlen; ++i, ++vp) { 1.142 + if (!vp->isMagic(JS_ELEMENTS_HOLE)) { 1.143 + /* Dense arrays never get so large that i would not fit into an integer id. */ 1.144 + if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props)) 1.145 + return false; 1.146 + } 1.147 + } 1.148 + 1.149 + /* Collect any typed array elements from this object. */ 1.150 + if (pobj->is<TypedArrayObject>()) { 1.151 + size_t len = pobj->as<TypedArrayObject>().length(); 1.152 + for (size_t i = 0; i < len; i++) { 1.153 + if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props)) 1.154 + return false; 1.155 + } 1.156 + } 1.157 + 1.158 + size_t initialLength = props->length(); 1.159 + 1.160 + /* Collect all unique properties from this object's scope. */ 1.161 + Shape::Range<NoGC> r(pobj->lastProperty()); 1.162 + for (; !r.empty(); r.popFront()) { 1.163 + Shape &shape = r.front(); 1.164 + 1.165 + if (!Enumerate(cx, pobj, shape.propid(), shape.enumerable(), flags, ht, props)) 1.166 + return false; 1.167 + } 1.168 + 1.169 + ::Reverse(props->begin() + initialLength, props->end()); 1.170 + return true; 1.171 +} 1.172 + 1.173 +#ifdef JS_MORE_DETERMINISTIC 1.174 + 1.175 +struct SortComparatorIds 1.176 +{ 1.177 + JSContext *const cx; 1.178 + 1.179 + SortComparatorIds(JSContext *cx) 1.180 + : cx(cx) {} 1.181 + 1.182 + bool operator()(jsid a, jsid b, bool *lessOrEqualp) 1.183 + { 1.184 + /* Pick an arbitrary total order on jsids that is stable across executions. */ 1.185 + RootedString astr(cx, IdToString(cx, a)); 1.186 + if (!astr) 1.187 + return false; 1.188 + RootedString bstr(cx, IdToString(cx, b)); 1.189 + if (!bstr) 1.190 + return false; 1.191 + 1.192 + int32_t result; 1.193 + if (!CompareStrings(cx, astr, bstr, &result)) 1.194 + return false; 1.195 + 1.196 + *lessOrEqualp = (result <= 0); 1.197 + return true; 1.198 + } 1.199 +}; 1.200 + 1.201 +#endif /* JS_MORE_DETERMINISTIC */ 1.202 + 1.203 +static bool 1.204 +Snapshot(JSContext *cx, JSObject *pobj_, unsigned flags, AutoIdVector *props) 1.205 +{ 1.206 + IdSet ht(cx); 1.207 + if (!ht.init(32)) 1.208 + return false; 1.209 + 1.210 + RootedObject pobj(cx, pobj_); 1.211 + 1.212 + do { 1.213 + const Class *clasp = pobj->getClass(); 1.214 + if (pobj->isNative() && 1.215 + !pobj->getOps()->enumerate && 1.216 + !(clasp->flags & JSCLASS_NEW_ENUMERATE)) { 1.217 + if (!clasp->enumerate(cx, pobj)) 1.218 + return false; 1.219 + if (!EnumerateNativeProperties(cx, pobj, flags, ht, props)) 1.220 + return false; 1.221 + } else { 1.222 + if (pobj->is<ProxyObject>()) { 1.223 + AutoIdVector proxyProps(cx); 1.224 + if (flags & JSITER_OWNONLY) { 1.225 + if (flags & JSITER_HIDDEN) { 1.226 + if (!Proxy::getOwnPropertyNames(cx, pobj, proxyProps)) 1.227 + return false; 1.228 + } else { 1.229 + if (!Proxy::keys(cx, pobj, proxyProps)) 1.230 + return false; 1.231 + } 1.232 + } else { 1.233 + if (!Proxy::enumerate(cx, pobj, proxyProps)) 1.234 + return false; 1.235 + } 1.236 + for (size_t n = 0, len = proxyProps.length(); n < len; n++) { 1.237 + if (!Enumerate(cx, pobj, proxyProps[n], true, flags, ht, props)) 1.238 + return false; 1.239 + } 1.240 + /* Proxy objects enumerate the prototype on their own, so we are done here. */ 1.241 + break; 1.242 + } 1.243 + RootedValue state(cx); 1.244 + RootedId id(cx); 1.245 + JSIterateOp op = (flags & JSITER_HIDDEN) ? JSENUMERATE_INIT_ALL : JSENUMERATE_INIT; 1.246 + if (!JSObject::enumerate(cx, pobj, op, &state, &id)) 1.247 + return false; 1.248 + if (state.isMagic(JS_NATIVE_ENUMERATE)) { 1.249 + if (!EnumerateNativeProperties(cx, pobj, flags, ht, props)) 1.250 + return false; 1.251 + } else { 1.252 + while (true) { 1.253 + RootedId id(cx); 1.254 + if (!JSObject::enumerate(cx, pobj, JSENUMERATE_NEXT, &state, &id)) 1.255 + return false; 1.256 + if (state.isNull()) 1.257 + break; 1.258 + if (!Enumerate(cx, pobj, id, true, flags, ht, props)) 1.259 + return false; 1.260 + } 1.261 + } 1.262 + } 1.263 + 1.264 + if (flags & JSITER_OWNONLY) 1.265 + break; 1.266 + 1.267 + } while ((pobj = pobj->getProto()) != nullptr); 1.268 + 1.269 +#ifdef JS_MORE_DETERMINISTIC 1.270 + 1.271 + /* 1.272 + * In some cases the enumeration order for an object depends on the 1.273 + * execution mode (interpreter vs. JIT), especially for native objects 1.274 + * with a class enumerate hook (where resolving a property changes the 1.275 + * resulting enumeration order). These aren't really bugs, but the 1.276 + * differences can change the generated output and confuse correctness 1.277 + * fuzzers, so we sort the ids if such a fuzzer is running. 1.278 + * 1.279 + * We don't do this in the general case because (a) doing so is slow, 1.280 + * and (b) it also breaks the web, which expects enumeration order to 1.281 + * follow the order in which properties are added, in certain cases. 1.282 + * Since ECMA does not specify an enumeration order for objects, both 1.283 + * behaviors are technically correct to do. 1.284 + */ 1.285 + 1.286 + jsid *ids = props->begin(); 1.287 + size_t n = props->length(); 1.288 + 1.289 + AutoIdVector tmp(cx); 1.290 + if (!tmp.resize(n)) 1.291 + return false; 1.292 + PodCopy(tmp.begin(), ids, n); 1.293 + 1.294 + if (!MergeSort(ids, n, tmp.begin(), SortComparatorIds(cx))) 1.295 + return false; 1.296 + 1.297 +#endif /* JS_MORE_DETERMINISTIC */ 1.298 + 1.299 + return true; 1.300 +} 1.301 + 1.302 +bool 1.303 +js::VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap) 1.304 +{ 1.305 + JS_STATIC_ASSERT(sizeof(JSIdArray) > sizeof(jsid)); 1.306 + size_t len = props.length(); 1.307 + size_t idsz = len * sizeof(jsid); 1.308 + size_t sz = (sizeof(JSIdArray) - sizeof(jsid)) + idsz; 1.309 + JSIdArray *ida = static_cast<JSIdArray *>(cx->malloc_(sz)); 1.310 + if (!ida) 1.311 + return false; 1.312 + 1.313 + ida->length = static_cast<int>(len); 1.314 + jsid *v = props.begin(); 1.315 + for (int i = 0; i < ida->length; i++) 1.316 + ida->vector[i].init(v[i]); 1.317 + *idap = ida; 1.318 + return true; 1.319 +} 1.320 + 1.321 +JS_FRIEND_API(bool) 1.322 +js::GetPropertyNames(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector *props) 1.323 +{ 1.324 + return Snapshot(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), props); 1.325 +} 1.326 + 1.327 +size_t sCustomIteratorCount = 0; 1.328 + 1.329 +static inline bool 1.330 +GetCustomIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleValue vp) 1.331 +{ 1.332 + JS_CHECK_RECURSION(cx, return false); 1.333 + 1.334 + /* Check whether we have a valid __iterator__ method. */ 1.335 + HandlePropertyName name = cx->names().iteratorIntrinsic; 1.336 + if (!JSObject::getProperty(cx, obj, obj, name, vp)) 1.337 + return false; 1.338 + 1.339 + /* If there is no custom __iterator__ method, we are done here. */ 1.340 + if (!vp.isObject()) { 1.341 + vp.setUndefined(); 1.342 + return true; 1.343 + } 1.344 + 1.345 + if (!cx->runningWithTrustedPrincipals()) 1.346 + ++sCustomIteratorCount; 1.347 + 1.348 + /* Otherwise call it and return that object. */ 1.349 + Value arg = BooleanValue((flags & JSITER_FOREACH) == 0); 1.350 + if (!Invoke(cx, ObjectValue(*obj), vp, 1, &arg, vp)) 1.351 + return false; 1.352 + if (vp.isPrimitive()) { 1.353 + /* 1.354 + * We are always coming from js::ValueToIterator, and we are no longer on 1.355 + * trace, so the object we are iterating over is on top of the stack (-1). 1.356 + */ 1.357 + JSAutoByteString bytes; 1.358 + if (!AtomToPrintableString(cx, name, &bytes)) 1.359 + return false; 1.360 + RootedValue val(cx, ObjectValue(*obj)); 1.361 + js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE, 1.362 + -1, val, js::NullPtr(), bytes.ptr()); 1.363 + return false; 1.364 + } 1.365 + return true; 1.366 +} 1.367 + 1.368 +template <typename T> 1.369 +static inline bool 1.370 +Compare(T *a, T *b, size_t c) 1.371 +{ 1.372 + size_t n = (c + size_t(7)) / size_t(8); 1.373 + switch (c % 8) { 1.374 + case 0: do { if (*a++ != *b++) return false; 1.375 + case 7: if (*a++ != *b++) return false; 1.376 + case 6: if (*a++ != *b++) return false; 1.377 + case 5: if (*a++ != *b++) return false; 1.378 + case 4: if (*a++ != *b++) return false; 1.379 + case 3: if (*a++ != *b++) return false; 1.380 + case 2: if (*a++ != *b++) return false; 1.381 + case 1: if (*a++ != *b++) return false; 1.382 + } while (--n > 0); 1.383 + } 1.384 + return true; 1.385 +} 1.386 + 1.387 +static inline PropertyIteratorObject * 1.388 +NewPropertyIteratorObject(JSContext *cx, unsigned flags) 1.389 +{ 1.390 + if (flags & JSITER_ENUMERATE) { 1.391 + RootedTypeObject type(cx, cx->getNewType(&PropertyIteratorObject::class_, nullptr)); 1.392 + if (!type) 1.393 + return nullptr; 1.394 + 1.395 + JSObject *metadata = nullptr; 1.396 + if (!NewObjectMetadata(cx, &metadata)) 1.397 + return nullptr; 1.398 + 1.399 + const Class *clasp = &PropertyIteratorObject::class_; 1.400 + RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, nullptr, nullptr, metadata, 1.401 + ITERATOR_FINALIZE_KIND)); 1.402 + if (!shape) 1.403 + return nullptr; 1.404 + 1.405 + JSObject *obj = JSObject::create(cx, ITERATOR_FINALIZE_KIND, 1.406 + GetInitialHeap(GenericObject, clasp), shape, type); 1.407 + if (!obj) 1.408 + return nullptr; 1.409 + 1.410 + JS_ASSERT(obj->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS); 1.411 + return &obj->as<PropertyIteratorObject>(); 1.412 + } 1.413 + 1.414 + JSObject *obj = NewBuiltinClassInstance(cx, &PropertyIteratorObject::class_); 1.415 + if (!obj) 1.416 + return nullptr; 1.417 + 1.418 + return &obj->as<PropertyIteratorObject>(); 1.419 +} 1.420 + 1.421 +NativeIterator * 1.422 +NativeIterator::allocateIterator(JSContext *cx, uint32_t slength, const AutoIdVector &props) 1.423 +{ 1.424 + size_t plength = props.length(); 1.425 + NativeIterator *ni = (NativeIterator *) 1.426 + cx->malloc_(sizeof(NativeIterator) 1.427 + + plength * sizeof(JSString *) 1.428 + + slength * sizeof(Shape *)); 1.429 + if (!ni) 1.430 + return nullptr; 1.431 + AutoValueVector strings(cx); 1.432 + ni->props_array = ni->props_cursor = (HeapPtr<JSFlatString> *) (ni + 1); 1.433 + ni->props_end = ni->props_array + plength; 1.434 + if (plength) { 1.435 + for (size_t i = 0; i < plength; i++) { 1.436 + JSFlatString *str = IdToString(cx, props[i]); 1.437 + if (!str || !strings.append(StringValue(str))) 1.438 + return nullptr; 1.439 + ni->props_array[i].init(str); 1.440 + } 1.441 + } 1.442 + ni->next_ = nullptr; 1.443 + ni->prev_ = nullptr; 1.444 + return ni; 1.445 +} 1.446 + 1.447 +NativeIterator * 1.448 +NativeIterator::allocateSentinel(JSContext *cx) 1.449 +{ 1.450 + NativeIterator *ni = (NativeIterator *)js_malloc(sizeof(NativeIterator)); 1.451 + if (!ni) 1.452 + return nullptr; 1.453 + 1.454 + PodZero(ni); 1.455 + 1.456 + ni->next_ = ni; 1.457 + ni->prev_ = ni; 1.458 + return ni; 1.459 +} 1.460 + 1.461 +inline void 1.462 +NativeIterator::init(JSObject *obj, JSObject *iterObj, unsigned flags, uint32_t slength, uint32_t key) 1.463 +{ 1.464 + this->obj.init(obj); 1.465 + this->iterObj_ = iterObj; 1.466 + this->flags = flags; 1.467 + this->shapes_array = (Shape **) this->props_end; 1.468 + this->shapes_length = slength; 1.469 + this->shapes_key = key; 1.470 +} 1.471 + 1.472 +static inline void 1.473 +RegisterEnumerator(JSContext *cx, PropertyIteratorObject *iterobj, NativeIterator *ni) 1.474 +{ 1.475 + /* Register non-escaping native enumerators (for-in) with the current context. */ 1.476 + if (ni->flags & JSITER_ENUMERATE) { 1.477 + ni->link(cx->compartment()->enumerators); 1.478 + 1.479 + JS_ASSERT(!(ni->flags & JSITER_ACTIVE)); 1.480 + ni->flags |= JSITER_ACTIVE; 1.481 + } 1.482 +} 1.483 + 1.484 +static inline bool 1.485 +VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &keys, 1.486 + uint32_t slength, uint32_t key, MutableHandleValue vp) 1.487 +{ 1.488 + JS_ASSERT(!(flags & JSITER_FOREACH)); 1.489 + 1.490 + if (obj) { 1.491 + if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx)) 1.492 + return false; 1.493 + types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED); 1.494 + } 1.495 + 1.496 + Rooted<PropertyIteratorObject *> iterobj(cx, NewPropertyIteratorObject(cx, flags)); 1.497 + if (!iterobj) 1.498 + return false; 1.499 + 1.500 + NativeIterator *ni = NativeIterator::allocateIterator(cx, slength, keys); 1.501 + if (!ni) 1.502 + return false; 1.503 + ni->init(obj, iterobj, flags, slength, key); 1.504 + 1.505 + if (slength) { 1.506 + /* 1.507 + * Fill in the shape array from scratch. We can't use the array that was 1.508 + * computed for the cache lookup earlier, as constructing iterobj could 1.509 + * have triggered a shape-regenerating GC. Don't bother with regenerating 1.510 + * the shape key; if such a GC *does* occur, we can only get hits through 1.511 + * the one-slot lastNativeIterator cache. 1.512 + */ 1.513 + JSObject *pobj = obj; 1.514 + size_t ind = 0; 1.515 + do { 1.516 + ni->shapes_array[ind++] = pobj->lastProperty(); 1.517 + pobj = pobj->getProto(); 1.518 + } while (pobj); 1.519 + JS_ASSERT(ind == slength); 1.520 + } 1.521 + 1.522 + iterobj->setNativeIterator(ni); 1.523 + vp.setObject(*iterobj); 1.524 + 1.525 + RegisterEnumerator(cx, iterobj, ni); 1.526 + return true; 1.527 +} 1.528 + 1.529 +bool 1.530 +js::VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &props, 1.531 + MutableHandleValue vp) 1.532 +{ 1.533 + return VectorToKeyIterator(cx, obj, flags, props, 0, 0, vp); 1.534 +} 1.535 + 1.536 +bool 1.537 +js::VectorToValueIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &keys, 1.538 + MutableHandleValue vp) 1.539 +{ 1.540 + JS_ASSERT(flags & JSITER_FOREACH); 1.541 + 1.542 + if (obj) { 1.543 + if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx)) 1.544 + return false; 1.545 + types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED); 1.546 + } 1.547 + 1.548 + Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx, flags)); 1.549 + if (!iterobj) 1.550 + return false; 1.551 + 1.552 + NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, keys); 1.553 + if (!ni) 1.554 + return false; 1.555 + ni->init(obj, iterobj, flags, 0, 0); 1.556 + 1.557 + iterobj->setNativeIterator(ni); 1.558 + vp.setObject(*iterobj); 1.559 + 1.560 + RegisterEnumerator(cx, iterobj, ni); 1.561 + return true; 1.562 +} 1.563 + 1.564 +bool 1.565 +js::EnumeratedIdVectorToIterator(JSContext *cx, HandleObject obj, unsigned flags, 1.566 + AutoIdVector &props, MutableHandleValue vp) 1.567 +{ 1.568 + if (!(flags & JSITER_FOREACH)) 1.569 + return VectorToKeyIterator(cx, obj, flags, props, vp); 1.570 + 1.571 + return VectorToValueIterator(cx, obj, flags, props, vp); 1.572 +} 1.573 + 1.574 +static inline void 1.575 +UpdateNativeIterator(NativeIterator *ni, JSObject *obj) 1.576 +{ 1.577 + // Update the object for which the native iterator is associated, so 1.578 + // SuppressDeletedPropertyHelper will recognize the iterator as a match. 1.579 + ni->obj = obj; 1.580 +} 1.581 + 1.582 +bool 1.583 +js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleValue vp) 1.584 +{ 1.585 + Vector<Shape *, 8> shapes(cx); 1.586 + uint32_t key = 0; 1.587 + 1.588 + bool keysOnly = (flags == JSITER_ENUMERATE); 1.589 + 1.590 + if (obj) { 1.591 + if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) { 1.592 + JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH)); 1.593 + if (!iterobj) 1.594 + return false; 1.595 + vp.setObject(*iterobj); 1.596 + return true; 1.597 + } 1.598 + 1.599 + if (keysOnly) { 1.600 + /* 1.601 + * Check to see if this is the same as the most recent object which 1.602 + * was iterated over. We don't explicitly check for shapeless 1.603 + * objects here, as they are not inserted into the cache and 1.604 + * will result in a miss. 1.605 + */ 1.606 + PropertyIteratorObject *last = cx->runtime()->nativeIterCache.last; 1.607 + if (last) { 1.608 + NativeIterator *lastni = last->getNativeIterator(); 1.609 + if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) && 1.610 + obj->isNative() && 1.611 + obj->hasEmptyElements() && 1.612 + obj->lastProperty() == lastni->shapes_array[0]) 1.613 + { 1.614 + JSObject *proto = obj->getProto(); 1.615 + if (proto->isNative() && 1.616 + proto->hasEmptyElements() && 1.617 + proto->lastProperty() == lastni->shapes_array[1] && 1.618 + !proto->getProto()) 1.619 + { 1.620 + vp.setObject(*last); 1.621 + UpdateNativeIterator(lastni, obj); 1.622 + RegisterEnumerator(cx, last, lastni); 1.623 + return true; 1.624 + } 1.625 + } 1.626 + } 1.627 + 1.628 + /* 1.629 + * The iterator object for JSITER_ENUMERATE never escapes, so we 1.630 + * don't care for the proper parent/proto to be set. This also 1.631 + * allows us to re-use a previous iterator object that is not 1.632 + * currently active. 1.633 + */ 1.634 + { 1.635 + JSObject *pobj = obj; 1.636 + do { 1.637 + if (!pobj->isNative() || 1.638 + !pobj->hasEmptyElements() || 1.639 + pobj->is<TypedArrayObject>() || 1.640 + pobj->hasUncacheableProto() || 1.641 + pobj->getOps()->enumerate || 1.642 + pobj->getClass()->enumerate != JS_EnumerateStub || 1.643 + pobj->nativeContainsPure(cx->names().iteratorIntrinsic)) 1.644 + { 1.645 + shapes.clear(); 1.646 + goto miss; 1.647 + } 1.648 + Shape *shape = pobj->lastProperty(); 1.649 + key = (key + (key << 16)) ^ (uintptr_t(shape) >> 3); 1.650 + if (!shapes.append(shape)) 1.651 + return false; 1.652 + pobj = pobj->getProto(); 1.653 + } while (pobj); 1.654 + } 1.655 + 1.656 + PropertyIteratorObject *iterobj = cx->runtime()->nativeIterCache.get(key); 1.657 + if (iterobj) { 1.658 + NativeIterator *ni = iterobj->getNativeIterator(); 1.659 + if (!(ni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) && 1.660 + ni->shapes_key == key && 1.661 + ni->shapes_length == shapes.length() && 1.662 + Compare(ni->shapes_array, shapes.begin(), ni->shapes_length)) { 1.663 + vp.setObject(*iterobj); 1.664 + 1.665 + UpdateNativeIterator(ni, obj); 1.666 + RegisterEnumerator(cx, iterobj, ni); 1.667 + if (shapes.length() == 2) 1.668 + cx->runtime()->nativeIterCache.last = iterobj; 1.669 + return true; 1.670 + } 1.671 + } 1.672 + } 1.673 + 1.674 + miss: 1.675 + if (obj->is<ProxyObject>()) 1.676 + return Proxy::iterate(cx, obj, flags, vp); 1.677 + 1.678 + if (!GetCustomIterator(cx, obj, flags, vp)) 1.679 + return false; 1.680 + if (!vp.isUndefined()) 1.681 + return true; 1.682 + } 1.683 + 1.684 + /* NB: for (var p in null) succeeds by iterating over no properties. */ 1.685 + 1.686 + AutoIdVector keys(cx); 1.687 + if (flags & JSITER_FOREACH) { 1.688 + if (MOZ_LIKELY(obj != nullptr) && !Snapshot(cx, obj, flags, &keys)) 1.689 + return false; 1.690 + JS_ASSERT(shapes.empty()); 1.691 + if (!VectorToValueIterator(cx, obj, flags, keys, vp)) 1.692 + return false; 1.693 + } else { 1.694 + if (MOZ_LIKELY(obj != nullptr) && !Snapshot(cx, obj, flags, &keys)) 1.695 + return false; 1.696 + if (!VectorToKeyIterator(cx, obj, flags, keys, shapes.length(), key, vp)) 1.697 + return false; 1.698 + } 1.699 + 1.700 + PropertyIteratorObject *iterobj = &vp.toObject().as<PropertyIteratorObject>(); 1.701 + 1.702 + /* Cache the iterator object if possible. */ 1.703 + if (shapes.length()) 1.704 + cx->runtime()->nativeIterCache.set(key, iterobj); 1.705 + 1.706 + if (shapes.length() == 2) 1.707 + cx->runtime()->nativeIterCache.last = iterobj; 1.708 + return true; 1.709 +} 1.710 + 1.711 +JSObject * 1.712 +js::GetIteratorObject(JSContext *cx, HandleObject obj, uint32_t flags) 1.713 +{ 1.714 + RootedValue value(cx); 1.715 + if (!GetIterator(cx, obj, flags, &value)) 1.716 + return nullptr; 1.717 + return &value.toObject(); 1.718 +} 1.719 + 1.720 +JSObject * 1.721 +js::CreateItrResultObject(JSContext *cx, HandleValue value, bool done) 1.722 +{ 1.723 + // FIXME: We can cache the iterator result object shape somewhere. 1.724 + AssertHeapIsIdle(cx); 1.725 + 1.726 + RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx)); 1.727 + if (!proto) 1.728 + return nullptr; 1.729 + 1.730 + RootedObject obj(cx, NewObjectWithGivenProto(cx, &JSObject::class_, proto, cx->global())); 1.731 + if (!obj) 1.732 + return nullptr; 1.733 + 1.734 + if (!JSObject::defineProperty(cx, obj, cx->names().value, value)) 1.735 + return nullptr; 1.736 + 1.737 + RootedValue doneBool(cx, BooleanValue(done)); 1.738 + if (!JSObject::defineProperty(cx, obj, cx->names().done, doneBool)) 1.739 + return nullptr; 1.740 + 1.741 + return obj; 1.742 +} 1.743 + 1.744 +bool 1.745 +js_ThrowStopIteration(JSContext *cx) 1.746 +{ 1.747 + JS_ASSERT(!JS_IsExceptionPending(cx)); 1.748 + 1.749 + // StopIteration isn't a constructor, but it's stored in GlobalObject 1.750 + // as one, out of laziness. Hence the GetBuiltinConstructor call here. 1.751 + RootedObject ctor(cx); 1.752 + if (GetBuiltinConstructor(cx, JSProto_StopIteration, &ctor)) 1.753 + cx->setPendingException(ObjectValue(*ctor)); 1.754 + return false; 1.755 +} 1.756 + 1.757 +/*** Iterator objects ****************************************************************************/ 1.758 + 1.759 +bool 1.760 +js::IteratorConstructor(JSContext *cx, unsigned argc, Value *vp) 1.761 +{ 1.762 + CallArgs args = CallArgsFromVp(argc, vp); 1.763 + if (args.length() == 0) { 1.764 + js_ReportMissingArg(cx, args.calleev(), 0); 1.765 + return false; 1.766 + } 1.767 + 1.768 + bool keyonly = false; 1.769 + if (args.length() >= 2) 1.770 + keyonly = ToBoolean(args[1]); 1.771 + unsigned flags = JSITER_OWNONLY | (keyonly ? 0 : (JSITER_FOREACH | JSITER_KEYVALUE)); 1.772 + 1.773 + if (!ValueToIterator(cx, flags, args[0])) 1.774 + return false; 1.775 + args.rval().set(args[0]); 1.776 + return true; 1.777 +} 1.778 + 1.779 +MOZ_ALWAYS_INLINE bool 1.780 +IsIterator(HandleValue v) 1.781 +{ 1.782 + return v.isObject() && v.toObject().hasClass(&PropertyIteratorObject::class_); 1.783 +} 1.784 + 1.785 +MOZ_ALWAYS_INLINE bool 1.786 +iterator_next_impl(JSContext *cx, CallArgs args) 1.787 +{ 1.788 + JS_ASSERT(IsIterator(args.thisv())); 1.789 + 1.790 + RootedObject thisObj(cx, &args.thisv().toObject()); 1.791 + 1.792 + if (!js_IteratorMore(cx, thisObj, args.rval())) 1.793 + return false; 1.794 + 1.795 + if (!args.rval().toBoolean()) { 1.796 + js_ThrowStopIteration(cx); 1.797 + return false; 1.798 + } 1.799 + 1.800 + return js_IteratorNext(cx, thisObj, args.rval()); 1.801 +} 1.802 + 1.803 +static bool 1.804 +iterator_next(JSContext *cx, unsigned argc, Value *vp) 1.805 +{ 1.806 + CallArgs args = CallArgsFromVp(argc, vp); 1.807 + return CallNonGenericMethod<IsIterator, iterator_next_impl>(cx, args); 1.808 +} 1.809 + 1.810 +static const JSFunctionSpec iterator_methods[] = { 1.811 + JS_SELF_HOSTED_FN("@@iterator", "LegacyIteratorShim", 0, 0), 1.812 + JS_FN("next", iterator_next, 0, 0), 1.813 + JS_FS_END 1.814 +}; 1.815 + 1.816 +static JSObject * 1.817 +iterator_iteratorObject(JSContext *cx, HandleObject obj, bool keysonly) 1.818 +{ 1.819 + return obj; 1.820 +} 1.821 + 1.822 +size_t 1.823 +PropertyIteratorObject::sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const 1.824 +{ 1.825 + return mallocSizeOf(getPrivate()); 1.826 +} 1.827 + 1.828 +void 1.829 +PropertyIteratorObject::trace(JSTracer *trc, JSObject *obj) 1.830 +{ 1.831 + if (NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator()) 1.832 + ni->mark(trc); 1.833 +} 1.834 + 1.835 +void 1.836 +PropertyIteratorObject::finalize(FreeOp *fop, JSObject *obj) 1.837 +{ 1.838 + if (NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator()) 1.839 + fop->free_(ni); 1.840 +} 1.841 + 1.842 +const Class PropertyIteratorObject::class_ = { 1.843 + "Iterator", 1.844 + JSCLASS_IMPLEMENTS_BARRIERS | 1.845 + JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) | 1.846 + JSCLASS_HAS_PRIVATE | 1.847 + JSCLASS_BACKGROUND_FINALIZE, 1.848 + JS_PropertyStub, /* addProperty */ 1.849 + JS_DeletePropertyStub, /* delProperty */ 1.850 + JS_PropertyStub, /* getProperty */ 1.851 + JS_StrictPropertyStub, /* setProperty */ 1.852 + JS_EnumerateStub, 1.853 + JS_ResolveStub, 1.854 + JS_ConvertStub, 1.855 + finalize, 1.856 + nullptr, /* call */ 1.857 + nullptr, /* hasInstance */ 1.858 + nullptr, /* construct */ 1.859 + trace, 1.860 + JS_NULL_CLASS_SPEC, 1.861 + { 1.862 + nullptr, /* outerObject */ 1.863 + nullptr, /* innerObject */ 1.864 + iterator_iteratorObject, 1.865 + } 1.866 +}; 1.867 + 1.868 +enum { 1.869 + ArrayIteratorSlotIteratedObject, 1.870 + ArrayIteratorSlotNextIndex, 1.871 + ArrayIteratorSlotItemKind, 1.872 + ArrayIteratorSlotCount 1.873 +}; 1.874 + 1.875 +const Class ArrayIteratorObject::class_ = { 1.876 + "Array Iterator", 1.877 + JSCLASS_IMPLEMENTS_BARRIERS | 1.878 + JSCLASS_HAS_RESERVED_SLOTS(ArrayIteratorSlotCount), 1.879 + JS_PropertyStub, /* addProperty */ 1.880 + JS_DeletePropertyStub, /* delProperty */ 1.881 + JS_PropertyStub, /* getProperty */ 1.882 + JS_StrictPropertyStub, /* setProperty */ 1.883 + JS_EnumerateStub, 1.884 + JS_ResolveStub, 1.885 + JS_ConvertStub, 1.886 + nullptr /* finalize */ 1.887 +}; 1.888 + 1.889 +static const JSFunctionSpec array_iterator_methods[] = { 1.890 + JS_SELF_HOSTED_FN("@@iterator", "ArrayIteratorIdentity", 0, 0), 1.891 + JS_SELF_HOSTED_FN("next", "ArrayIteratorNext", 0, 0), 1.892 + JS_FS_END 1.893 +}; 1.894 + 1.895 +static const Class StringIteratorPrototypeClass = { 1.896 + "String Iterator", 1.897 + JSCLASS_IMPLEMENTS_BARRIERS, 1.898 + JS_PropertyStub, /* addProperty */ 1.899 + JS_DeletePropertyStub, /* delProperty */ 1.900 + JS_PropertyStub, /* getProperty */ 1.901 + JS_StrictPropertyStub, /* setProperty */ 1.902 + JS_EnumerateStub, 1.903 + JS_ResolveStub, 1.904 + JS_ConvertStub, 1.905 + nullptr /* finalize */ 1.906 +}; 1.907 + 1.908 +enum { 1.909 + StringIteratorSlotIteratedObject, 1.910 + StringIteratorSlotNextIndex, 1.911 + StringIteratorSlotCount 1.912 +}; 1.913 + 1.914 +const Class StringIteratorObject::class_ = { 1.915 + "String Iterator", 1.916 + JSCLASS_IMPLEMENTS_BARRIERS | 1.917 + JSCLASS_HAS_RESERVED_SLOTS(StringIteratorSlotCount), 1.918 + JS_PropertyStub, /* addProperty */ 1.919 + JS_DeletePropertyStub, /* delProperty */ 1.920 + JS_PropertyStub, /* getProperty */ 1.921 + JS_StrictPropertyStub, /* setProperty */ 1.922 + JS_EnumerateStub, 1.923 + JS_ResolveStub, 1.924 + JS_ConvertStub, 1.925 + nullptr /* finalize */ 1.926 +}; 1.927 + 1.928 +static const JSFunctionSpec string_iterator_methods[] = { 1.929 + JS_SELF_HOSTED_FN("@@iterator", "StringIteratorIdentity", 0, 0), 1.930 + JS_SELF_HOSTED_FN("next", "StringIteratorNext", 0, 0), 1.931 + JS_FS_END 1.932 +}; 1.933 + 1.934 +static bool 1.935 +CloseLegacyGenerator(JSContext *cx, HandleObject genobj); 1.936 + 1.937 +bool 1.938 +js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp) 1.939 +{ 1.940 + /* JSITER_KEYVALUE must always come with JSITER_FOREACH */ 1.941 + JS_ASSERT_IF(flags & JSITER_KEYVALUE, flags & JSITER_FOREACH); 1.942 + 1.943 + /* 1.944 + * Make sure the more/next state machine doesn't get stuck. A value might 1.945 + * be left in iterValue when a trace is left due to an interrupt after 1.946 + * JSOP_MOREITER but before the value is picked up by FOR*. 1.947 + */ 1.948 + cx->iterValue.setMagic(JS_NO_ITER_VALUE); 1.949 + 1.950 + RootedObject obj(cx); 1.951 + if (vp.isObject()) { 1.952 + /* Common case. */ 1.953 + obj = &vp.toObject(); 1.954 + } else { 1.955 + /* 1.956 + * Enumerating over null and undefined gives an empty enumerator, so 1.957 + * that |for (var p in <null or undefined>) <loop>;| never executes 1.958 + * <loop>, per ES5 12.6.4. 1.959 + */ 1.960 + if (!(flags & JSITER_ENUMERATE) || !vp.isNullOrUndefined()) { 1.961 + obj = ToObject(cx, vp); 1.962 + if (!obj) 1.963 + return false; 1.964 + } 1.965 + } 1.966 + 1.967 + return GetIterator(cx, obj, flags, vp); 1.968 +} 1.969 + 1.970 +bool 1.971 +js::CloseIterator(JSContext *cx, HandleObject obj) 1.972 +{ 1.973 + cx->iterValue.setMagic(JS_NO_ITER_VALUE); 1.974 + 1.975 + if (obj->is<PropertyIteratorObject>()) { 1.976 + /* Remove enumerators from the active list, which is a stack. */ 1.977 + NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator(); 1.978 + 1.979 + if (ni->flags & JSITER_ENUMERATE) { 1.980 + ni->unlink(); 1.981 + 1.982 + JS_ASSERT(ni->flags & JSITER_ACTIVE); 1.983 + ni->flags &= ~JSITER_ACTIVE; 1.984 + 1.985 + /* 1.986 + * Reset the enumerator; it may still be in the cached iterators 1.987 + * for this thread, and can be reused. 1.988 + */ 1.989 + ni->props_cursor = ni->props_array; 1.990 + } 1.991 + } else if (obj->is<LegacyGeneratorObject>()) { 1.992 + return CloseLegacyGenerator(cx, obj); 1.993 + } 1.994 + return true; 1.995 +} 1.996 + 1.997 +bool 1.998 +js::UnwindIteratorForException(JSContext *cx, HandleObject obj) 1.999 +{ 1.1000 + RootedValue v(cx); 1.1001 + bool getOk = cx->getPendingException(&v); 1.1002 + cx->clearPendingException(); 1.1003 + if (!CloseIterator(cx, obj)) 1.1004 + return false; 1.1005 + if (!getOk) 1.1006 + return false; 1.1007 + cx->setPendingException(v); 1.1008 + return true; 1.1009 +} 1.1010 + 1.1011 +void 1.1012 +js::UnwindIteratorForUncatchableException(JSContext *cx, JSObject *obj) 1.1013 +{ 1.1014 + if (obj->is<PropertyIteratorObject>()) { 1.1015 + NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator(); 1.1016 + if (ni->flags & JSITER_ENUMERATE) 1.1017 + ni->unlink(); 1.1018 + } 1.1019 +} 1.1020 + 1.1021 +/* 1.1022 + * Suppress enumeration of deleted properties. This function must be called 1.1023 + * when a property is deleted and there might be active enumerators. 1.1024 + * 1.1025 + * We maintain a list of active non-escaping for-in enumerators. To suppress 1.1026 + * a property, we check whether each active enumerator contains the (obj, id) 1.1027 + * pair and has not yet enumerated |id|. If so, and |id| is the next property, 1.1028 + * we simply advance the cursor. Otherwise, we delete |id| from the list. 1.1029 + * 1.1030 + * We do not suppress enumeration of a property deleted along an object's 1.1031 + * prototype chain. Only direct deletions on the object are handled. 1.1032 + * 1.1033 + * This function can suppress multiple properties at once. The |predicate| 1.1034 + * argument is an object which can be called on an id and returns true or 1.1035 + * false. It also must have a method |matchesAtMostOne| which allows us to 1.1036 + * stop searching after the first deletion if true. 1.1037 + */ 1.1038 +template<typename StringPredicate> 1.1039 +static bool 1.1040 +SuppressDeletedPropertyHelper(JSContext *cx, HandleObject obj, StringPredicate predicate) 1.1041 +{ 1.1042 + NativeIterator *enumeratorList = cx->compartment()->enumerators; 1.1043 + NativeIterator *ni = enumeratorList->next(); 1.1044 + 1.1045 + while (ni != enumeratorList) { 1.1046 + again: 1.1047 + /* This only works for identified suppressed keys, not values. */ 1.1048 + if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) { 1.1049 + /* Check whether id is still to come. */ 1.1050 + HeapPtr<JSFlatString> *props_cursor = ni->current(); 1.1051 + HeapPtr<JSFlatString> *props_end = ni->end(); 1.1052 + for (HeapPtr<JSFlatString> *idp = props_cursor; idp < props_end; ++idp) { 1.1053 + if (predicate(*idp)) { 1.1054 + /* 1.1055 + * Check whether another property along the prototype chain 1.1056 + * became visible as a result of this deletion. 1.1057 + */ 1.1058 + RootedObject proto(cx); 1.1059 + if (!JSObject::getProto(cx, obj, &proto)) 1.1060 + return false; 1.1061 + if (proto) { 1.1062 + RootedObject obj2(cx); 1.1063 + RootedShape prop(cx); 1.1064 + RootedId id(cx); 1.1065 + RootedValue idv(cx, StringValue(*idp)); 1.1066 + if (!ValueToId<CanGC>(cx, idv, &id)) 1.1067 + return false; 1.1068 + if (!JSObject::lookupGeneric(cx, proto, id, &obj2, &prop)) 1.1069 + return false; 1.1070 + if (prop) { 1.1071 + unsigned attrs; 1.1072 + if (obj2->isNative()) 1.1073 + attrs = GetShapeAttributes(obj2, prop); 1.1074 + else if (!JSObject::getGenericAttributes(cx, obj2, id, &attrs)) 1.1075 + return false; 1.1076 + 1.1077 + if (attrs & JSPROP_ENUMERATE) 1.1078 + continue; 1.1079 + } 1.1080 + } 1.1081 + 1.1082 + /* 1.1083 + * If lookupProperty or getAttributes above removed a property from 1.1084 + * ni, start over. 1.1085 + */ 1.1086 + if (props_end != ni->props_end || props_cursor != ni->props_cursor) 1.1087 + goto again; 1.1088 + 1.1089 + /* 1.1090 + * No property along the prototype chain stepped in to take the 1.1091 + * property's place, so go ahead and delete id from the list. 1.1092 + * If it is the next property to be enumerated, just skip it. 1.1093 + */ 1.1094 + if (idp == props_cursor) { 1.1095 + ni->incCursor(); 1.1096 + } else { 1.1097 + for (HeapPtr<JSFlatString> *p = idp; p + 1 != props_end; p++) 1.1098 + *p = *(p + 1); 1.1099 + ni->props_end = ni->end() - 1; 1.1100 + 1.1101 + /* 1.1102 + * This invokes the pre barrier on this element, since 1.1103 + * it's no longer going to be marked, and ensures that 1.1104 + * any existing remembered set entry will be dropped. 1.1105 + */ 1.1106 + *ni->props_end = nullptr; 1.1107 + } 1.1108 + 1.1109 + /* Don't reuse modified native iterators. */ 1.1110 + ni->flags |= JSITER_UNREUSABLE; 1.1111 + 1.1112 + if (predicate.matchesAtMostOne()) 1.1113 + break; 1.1114 + } 1.1115 + } 1.1116 + } 1.1117 + ni = ni->next(); 1.1118 + } 1.1119 + return true; 1.1120 +} 1.1121 + 1.1122 +namespace { 1.1123 + 1.1124 +class SingleStringPredicate { 1.1125 + Handle<JSFlatString*> str; 1.1126 +public: 1.1127 + SingleStringPredicate(Handle<JSFlatString*> str) : str(str) {} 1.1128 + 1.1129 + bool operator()(JSFlatString *str) { return EqualStrings(str, this->str); } 1.1130 + bool matchesAtMostOne() { return true; } 1.1131 +}; 1.1132 + 1.1133 +} /* anonymous namespace */ 1.1134 + 1.1135 +bool 1.1136 +js_SuppressDeletedProperty(JSContext *cx, HandleObject obj, jsid id) 1.1137 +{ 1.1138 + Rooted<JSFlatString*> str(cx, IdToString(cx, id)); 1.1139 + if (!str) 1.1140 + return false; 1.1141 + return SuppressDeletedPropertyHelper(cx, obj, SingleStringPredicate(str)); 1.1142 +} 1.1143 + 1.1144 +bool 1.1145 +js_SuppressDeletedElement(JSContext *cx, HandleObject obj, uint32_t index) 1.1146 +{ 1.1147 + RootedId id(cx); 1.1148 + if (!IndexToId(cx, index, &id)) 1.1149 + return false; 1.1150 + return js_SuppressDeletedProperty(cx, obj, id); 1.1151 +} 1.1152 + 1.1153 +namespace { 1.1154 + 1.1155 +class IndexRangePredicate { 1.1156 + uint32_t begin, end; 1.1157 + 1.1158 + public: 1.1159 + IndexRangePredicate(uint32_t begin, uint32_t end) : begin(begin), end(end) {} 1.1160 + 1.1161 + bool operator()(JSFlatString *str) { 1.1162 + uint32_t index; 1.1163 + return str->isIndex(&index) && begin <= index && index < end; 1.1164 + } 1.1165 + 1.1166 + bool matchesAtMostOne() { return false; } 1.1167 +}; 1.1168 + 1.1169 +} /* anonymous namespace */ 1.1170 + 1.1171 +bool 1.1172 +js_SuppressDeletedElements(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end) 1.1173 +{ 1.1174 + return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end)); 1.1175 +} 1.1176 + 1.1177 +bool 1.1178 +js_IteratorMore(JSContext *cx, HandleObject iterobj, MutableHandleValue rval) 1.1179 +{ 1.1180 + /* Fast path for native iterators */ 1.1181 + NativeIterator *ni = nullptr; 1.1182 + if (iterobj->is<PropertyIteratorObject>()) { 1.1183 + /* Key iterators are handled by fast-paths. */ 1.1184 + ni = iterobj->as<PropertyIteratorObject>().getNativeIterator(); 1.1185 + bool more = ni->props_cursor < ni->props_end; 1.1186 + if (ni->isKeyIter() || !more) { 1.1187 + rval.setBoolean(more); 1.1188 + return true; 1.1189 + } 1.1190 + } 1.1191 + 1.1192 + /* We might still have a pending value. */ 1.1193 + if (!cx->iterValue.isMagic(JS_NO_ITER_VALUE)) { 1.1194 + rval.setBoolean(true); 1.1195 + return true; 1.1196 + } 1.1197 + 1.1198 + /* We're reentering below and can call anything. */ 1.1199 + JS_CHECK_RECURSION(cx, return false); 1.1200 + 1.1201 + /* Fetch and cache the next value from the iterator. */ 1.1202 + if (ni) { 1.1203 + JS_ASSERT(!ni->isKeyIter()); 1.1204 + RootedId id(cx); 1.1205 + RootedValue current(cx, StringValue(*ni->current())); 1.1206 + if (!ValueToId<CanGC>(cx, current, &id)) 1.1207 + return false; 1.1208 + ni->incCursor(); 1.1209 + RootedObject obj(cx, ni->obj); 1.1210 + if (!JSObject::getGeneric(cx, obj, obj, id, rval)) 1.1211 + return false; 1.1212 + if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, rval, rval)) 1.1213 + return false; 1.1214 + } else { 1.1215 + /* Call the iterator object's .next method. */ 1.1216 + if (!JSObject::getProperty(cx, iterobj, iterobj, cx->names().next, rval)) 1.1217 + return false; 1.1218 + if (!Invoke(cx, ObjectValue(*iterobj), rval, 0, nullptr, rval)) { 1.1219 + /* Check for StopIteration. */ 1.1220 + if (!cx->isExceptionPending()) 1.1221 + return false; 1.1222 + RootedValue exception(cx); 1.1223 + if (!cx->getPendingException(&exception)) 1.1224 + return false; 1.1225 + if (!JS_IsStopIteration(exception)) 1.1226 + return false; 1.1227 + 1.1228 + cx->clearPendingException(); 1.1229 + cx->iterValue.setMagic(JS_NO_ITER_VALUE); 1.1230 + rval.setBoolean(false); 1.1231 + return true; 1.1232 + } 1.1233 + } 1.1234 + 1.1235 + /* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */ 1.1236 + JS_ASSERT(!rval.isMagic(JS_NO_ITER_VALUE)); 1.1237 + cx->iterValue = rval; 1.1238 + rval.setBoolean(true); 1.1239 + return true; 1.1240 +} 1.1241 + 1.1242 +bool 1.1243 +js_IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval) 1.1244 +{ 1.1245 + /* Fast path for native iterators */ 1.1246 + if (iterobj->is<PropertyIteratorObject>()) { 1.1247 + /* 1.1248 + * Implement next directly as all the methods of the native iterator are 1.1249 + * read-only and permanent. 1.1250 + */ 1.1251 + NativeIterator *ni = iterobj->as<PropertyIteratorObject>().getNativeIterator(); 1.1252 + if (ni->isKeyIter()) { 1.1253 + JS_ASSERT(ni->props_cursor < ni->props_end); 1.1254 + rval.setString(*ni->current()); 1.1255 + ni->incCursor(); 1.1256 + return true; 1.1257 + } 1.1258 + } 1.1259 + 1.1260 + JS_ASSERT(!cx->iterValue.isMagic(JS_NO_ITER_VALUE)); 1.1261 + rval.set(cx->iterValue); 1.1262 + cx->iterValue.setMagic(JS_NO_ITER_VALUE); 1.1263 + 1.1264 + return true; 1.1265 +} 1.1266 + 1.1267 +static bool 1.1268 +stopiter_hasInstance(JSContext *cx, HandleObject obj, MutableHandleValue v, bool *bp) 1.1269 +{ 1.1270 + *bp = JS_IsStopIteration(v); 1.1271 + return true; 1.1272 +} 1.1273 + 1.1274 +const Class StopIterationObject::class_ = { 1.1275 + "StopIteration", 1.1276 + JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration), 1.1277 + JS_PropertyStub, /* addProperty */ 1.1278 + JS_DeletePropertyStub, /* delProperty */ 1.1279 + JS_PropertyStub, /* getProperty */ 1.1280 + JS_StrictPropertyStub, /* setProperty */ 1.1281 + JS_EnumerateStub, 1.1282 + JS_ResolveStub, 1.1283 + JS_ConvertStub, 1.1284 + nullptr, /* finalize */ 1.1285 + nullptr, /* call */ 1.1286 + stopiter_hasInstance, 1.1287 + nullptr /* construct */ 1.1288 +}; 1.1289 + 1.1290 +bool 1.1291 +ForOfIterator::init(HandleValue iterable, NonIterableBehavior nonIterableBehavior) 1.1292 +{ 1.1293 + JSContext *cx = cx_; 1.1294 + RootedObject iterableObj(cx, ToObject(cx, iterable)); 1.1295 + if (!iterableObj) 1.1296 + return false; 1.1297 + 1.1298 + JS_ASSERT(index == NOT_ARRAY); 1.1299 + 1.1300 + // Check the PIC first for a match. 1.1301 + if (iterableObj->is<ArrayObject>()) { 1.1302 + ForOfPIC::Chain *stubChain = ForOfPIC::getOrCreate(cx); 1.1303 + if (!stubChain) 1.1304 + return false; 1.1305 + 1.1306 + bool optimized; 1.1307 + if (!stubChain->tryOptimizeArray(cx, iterableObj, &optimized)) 1.1308 + return false; 1.1309 + 1.1310 + if (optimized) { 1.1311 + // Got optimized stub. Array is optimizable. 1.1312 + index = 0; 1.1313 + iterator = iterableObj; 1.1314 + return true; 1.1315 + } 1.1316 + } 1.1317 + 1.1318 + JS_ASSERT(index == NOT_ARRAY); 1.1319 + 1.1320 + // The iterator is the result of calling obj[@@iterator](). 1.1321 + InvokeArgs args(cx); 1.1322 + if (!args.init(0)) 1.1323 + return false; 1.1324 + args.setThis(ObjectValue(*iterableObj)); 1.1325 + 1.1326 + RootedValue callee(cx); 1.1327 + if (!JSObject::getProperty(cx, iterableObj, iterableObj, cx->names().std_iterator, &callee)) 1.1328 + return false; 1.1329 + 1.1330 + // Throw if obj[@@iterator] isn't callable if we were asked to do so. 1.1331 + // js::Invoke is about to check for this kind of error anyway, but it would 1.1332 + // throw an inscrutable error message about |method| rather than this nice 1.1333 + // one about |obj|. 1.1334 + if (!callee.isObject() || !callee.toObject().isCallable()) { 1.1335 + if (nonIterableBehavior == AllowNonIterable) 1.1336 + return true; 1.1337 + char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, iterable, NullPtr()); 1.1338 + if (!bytes) 1.1339 + return false; 1.1340 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, bytes); 1.1341 + js_free(bytes); 1.1342 + return false; 1.1343 + } 1.1344 + 1.1345 + args.setCallee(callee); 1.1346 + if (!Invoke(cx, args)) 1.1347 + return false; 1.1348 + 1.1349 + iterator = ToObject(cx, args.rval()); 1.1350 + if (!iterator) 1.1351 + return false; 1.1352 + 1.1353 + return true; 1.1354 +} 1.1355 + 1.1356 +inline bool 1.1357 +ForOfIterator::nextFromOptimizedArray(MutableHandleValue vp, bool *done) 1.1358 +{ 1.1359 + JS_ASSERT(index != NOT_ARRAY); 1.1360 + 1.1361 + if (!CheckForInterrupt(cx_)) 1.1362 + return false; 1.1363 + 1.1364 + JS_ASSERT(iterator->isNative()); 1.1365 + JS_ASSERT(iterator->is<ArrayObject>()); 1.1366 + 1.1367 + if (index >= iterator->as<ArrayObject>().length()) { 1.1368 + vp.setUndefined(); 1.1369 + *done = true; 1.1370 + return true; 1.1371 + } 1.1372 + *done = false; 1.1373 + 1.1374 + // Try to get array element via direct access. 1.1375 + if (index < iterator->getDenseInitializedLength()) { 1.1376 + vp.set(iterator->getDenseElement(index)); 1.1377 + if (!vp.isMagic(JS_ELEMENTS_HOLE)) { 1.1378 + ++index; 1.1379 + return true; 1.1380 + } 1.1381 + } 1.1382 + 1.1383 + return JSObject::getElement(cx_, iterator, iterator, index++, vp); 1.1384 +} 1.1385 + 1.1386 +bool 1.1387 +ForOfIterator::next(MutableHandleValue vp, bool *done) 1.1388 +{ 1.1389 + JS_ASSERT(iterator); 1.1390 + 1.1391 + if (index != NOT_ARRAY) { 1.1392 + ForOfPIC::Chain *stubChain = ForOfPIC::getOrCreate(cx_); 1.1393 + if (!stubChain) 1.1394 + return false; 1.1395 + 1.1396 + if (stubChain->isArrayNextStillSane()) 1.1397 + return nextFromOptimizedArray(vp, done); 1.1398 + 1.1399 + // ArrayIterator.prototype.next changed, materialize a proper 1.1400 + // ArrayIterator instance and fall through to slowpath case. 1.1401 + if (!materializeArrayIterator()) 1.1402 + return false; 1.1403 + } 1.1404 + 1.1405 + RootedValue method(cx_); 1.1406 + if (!JSObject::getProperty(cx_, iterator, iterator, cx_->names().next, &method)) 1.1407 + return false; 1.1408 + 1.1409 + InvokeArgs args(cx_); 1.1410 + if (!args.init(1)) 1.1411 + return false; 1.1412 + args.setCallee(method); 1.1413 + args.setThis(ObjectValue(*iterator)); 1.1414 + args[0].setUndefined(); 1.1415 + if (!Invoke(cx_, args)) 1.1416 + return false; 1.1417 + 1.1418 + RootedObject resultObj(cx_, ToObject(cx_, args.rval())); 1.1419 + if (!resultObj) 1.1420 + return false; 1.1421 + RootedValue doneVal(cx_); 1.1422 + if (!JSObject::getProperty(cx_, resultObj, resultObj, cx_->names().done, &doneVal)) 1.1423 + return false; 1.1424 + *done = ToBoolean(doneVal); 1.1425 + if (*done) { 1.1426 + vp.setUndefined(); 1.1427 + return true; 1.1428 + } 1.1429 + return JSObject::getProperty(cx_, resultObj, resultObj, cx_->names().value, vp); 1.1430 +} 1.1431 + 1.1432 +bool 1.1433 +ForOfIterator::materializeArrayIterator() 1.1434 +{ 1.1435 + JS_ASSERT(index != NOT_ARRAY); 1.1436 + 1.1437 + const char *nameString = "ArrayValuesAt"; 1.1438 + 1.1439 + RootedAtom name(cx_, Atomize(cx_, nameString, strlen(nameString))); 1.1440 + if (!name) 1.1441 + return false; 1.1442 + 1.1443 + RootedValue val(cx_); 1.1444 + if (!cx_->global()->getSelfHostedFunction(cx_, name, name, 1, &val)) 1.1445 + return false; 1.1446 + 1.1447 + InvokeArgs args(cx_); 1.1448 + if (!args.init(1)) 1.1449 + return false; 1.1450 + args.setCallee(val); 1.1451 + args.setThis(ObjectValue(*iterator)); 1.1452 + args[0].set(Int32Value(index)); 1.1453 + if (!Invoke(cx_, args)) 1.1454 + return false; 1.1455 + 1.1456 + index = NOT_ARRAY; 1.1457 + // Result of call to ArrayValuesAt must be an object. 1.1458 + iterator = &args.rval().toObject(); 1.1459 + return true; 1.1460 +} 1.1461 + 1.1462 +/*** Generators **********************************************************************************/ 1.1463 + 1.1464 +template<typename T> 1.1465 +static void 1.1466 +FinalizeGenerator(FreeOp *fop, JSObject *obj) 1.1467 +{ 1.1468 + JS_ASSERT(obj->is<T>()); 1.1469 + JSGenerator *gen = obj->as<T>().getGenerator(); 1.1470 + JS_ASSERT(gen); 1.1471 + // gen is open when a script has not called its close method while 1.1472 + // explicitly manipulating it. 1.1473 + JS_ASSERT(gen->state == JSGEN_NEWBORN || 1.1474 + gen->state == JSGEN_CLOSED || 1.1475 + gen->state == JSGEN_OPEN); 1.1476 + // If gen->state is JSGEN_CLOSED, gen->fp may be nullptr. 1.1477 + if (gen->fp) 1.1478 + JS_POISON(gen->fp, JS_SWEPT_FRAME_PATTERN, sizeof(InterpreterFrame)); 1.1479 + JS_POISON(gen, JS_SWEPT_FRAME_PATTERN, sizeof(JSGenerator)); 1.1480 + fop->free_(gen); 1.1481 +} 1.1482 + 1.1483 +static void 1.1484 +MarkGeneratorFrame(JSTracer *trc, JSGenerator *gen) 1.1485 +{ 1.1486 + MarkValueRange(trc, 1.1487 + HeapValueify(gen->fp->generatorArgsSnapshotBegin()), 1.1488 + HeapValueify(gen->fp->generatorArgsSnapshotEnd()), 1.1489 + "Generator Floating Args"); 1.1490 + gen->fp->mark(trc); 1.1491 + MarkValueRange(trc, 1.1492 + HeapValueify(gen->fp->generatorSlotsSnapshotBegin()), 1.1493 + HeapValueify(gen->regs.sp), 1.1494 + "Generator Floating Stack"); 1.1495 +} 1.1496 + 1.1497 +static void 1.1498 +GeneratorWriteBarrierPre(JSContext *cx, JSGenerator *gen) 1.1499 +{ 1.1500 + JS::Zone *zone = cx->zone(); 1.1501 + if (zone->needsBarrier()) 1.1502 + MarkGeneratorFrame(zone->barrierTracer(), gen); 1.1503 +} 1.1504 + 1.1505 +static void 1.1506 +GeneratorWriteBarrierPost(JSContext *cx, JSGenerator *gen) 1.1507 +{ 1.1508 +#ifdef JSGC_GENERATIONAL 1.1509 + cx->runtime()->gcStoreBuffer.putWholeCell(gen->obj); 1.1510 +#endif 1.1511 +} 1.1512 + 1.1513 +/* 1.1514 + * Only mark generator frames/slots when the generator is not active on the 1.1515 + * stack or closed. Barriers when copying onto the stack or closing preserve 1.1516 + * gc invariants. 1.1517 + */ 1.1518 +static bool 1.1519 +GeneratorHasMarkableFrame(JSGenerator *gen) 1.1520 +{ 1.1521 + return gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN; 1.1522 +} 1.1523 + 1.1524 +/* 1.1525 + * When a generator is closed, the GC things reachable from the contained frame 1.1526 + * and slots become unreachable and thus require a write barrier. 1.1527 + */ 1.1528 +static void 1.1529 +SetGeneratorClosed(JSContext *cx, JSGenerator *gen) 1.1530 +{ 1.1531 + JS_ASSERT(gen->state != JSGEN_CLOSED); 1.1532 + if (GeneratorHasMarkableFrame(gen)) 1.1533 + GeneratorWriteBarrierPre(cx, gen); 1.1534 + gen->state = JSGEN_CLOSED; 1.1535 + 1.1536 +#ifdef DEBUG 1.1537 + MakeRangeGCSafe(gen->fp->generatorArgsSnapshotBegin(), 1.1538 + gen->fp->generatorArgsSnapshotEnd()); 1.1539 + MakeRangeGCSafe(gen->fp->generatorSlotsSnapshotBegin(), 1.1540 + gen->regs.sp); 1.1541 + PodZero(&gen->regs, 1); 1.1542 + gen->fp = nullptr; 1.1543 +#endif 1.1544 +} 1.1545 + 1.1546 +template<typename T> 1.1547 +static void 1.1548 +TraceGenerator(JSTracer *trc, JSObject *obj) 1.1549 +{ 1.1550 + JS_ASSERT(obj->is<T>()); 1.1551 + JSGenerator *gen = obj->as<T>().getGenerator(); 1.1552 + JS_ASSERT(gen); 1.1553 + if (GeneratorHasMarkableFrame(gen)) 1.1554 + MarkGeneratorFrame(trc, gen); 1.1555 +} 1.1556 + 1.1557 +GeneratorState::GeneratorState(JSContext *cx, JSGenerator *gen, JSGeneratorState futureState) 1.1558 + : RunState(cx, Generator, gen->fp->script()), 1.1559 + cx_(cx), 1.1560 + gen_(gen), 1.1561 + futureState_(futureState), 1.1562 + entered_(false) 1.1563 +{ } 1.1564 + 1.1565 +GeneratorState::~GeneratorState() 1.1566 +{ 1.1567 + gen_->fp->setSuspended(); 1.1568 + 1.1569 + if (entered_) 1.1570 + cx_->leaveGenerator(gen_); 1.1571 +} 1.1572 + 1.1573 +InterpreterFrame * 1.1574 +GeneratorState::pushInterpreterFrame(JSContext *cx) 1.1575 +{ 1.1576 + /* 1.1577 + * Write barrier is needed since the generator stack can be updated, 1.1578 + * and it's not barriered in any other way. We need to do it before 1.1579 + * gen->state changes, which can cause us to trace the generator 1.1580 + * differently. 1.1581 + * 1.1582 + * We could optimize this by setting a bit on the generator to signify 1.1583 + * that it has been marked. If this bit has already been set, there is no 1.1584 + * need to mark again. The bit would have to be reset before the next GC, 1.1585 + * or else some kind of epoch scheme would have to be used. 1.1586 + */ 1.1587 + GeneratorWriteBarrierPre(cx, gen_); 1.1588 + gen_->state = futureState_; 1.1589 + 1.1590 + gen_->fp->clearSuspended(); 1.1591 + 1.1592 + cx->enterGenerator(gen_); /* OOM check above. */ 1.1593 + entered_ = true; 1.1594 + return gen_->fp; 1.1595 +} 1.1596 + 1.1597 +const Class LegacyGeneratorObject::class_ = { 1.1598 + "Generator", 1.1599 + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, 1.1600 + JS_PropertyStub, /* addProperty */ 1.1601 + JS_DeletePropertyStub, /* delProperty */ 1.1602 + JS_PropertyStub, /* getProperty */ 1.1603 + JS_StrictPropertyStub, /* setProperty */ 1.1604 + JS_EnumerateStub, 1.1605 + JS_ResolveStub, 1.1606 + JS_ConvertStub, 1.1607 + FinalizeGenerator<LegacyGeneratorObject>, 1.1608 + nullptr, /* call */ 1.1609 + nullptr, /* hasInstance */ 1.1610 + nullptr, /* construct */ 1.1611 + TraceGenerator<LegacyGeneratorObject>, 1.1612 + JS_NULL_CLASS_SPEC, 1.1613 + { 1.1614 + nullptr, /* outerObject */ 1.1615 + nullptr, /* innerObject */ 1.1616 + iterator_iteratorObject, 1.1617 + } 1.1618 +}; 1.1619 + 1.1620 +const Class StarGeneratorObject::class_ = { 1.1621 + "Generator", 1.1622 + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS, 1.1623 + JS_PropertyStub, /* addProperty */ 1.1624 + JS_DeletePropertyStub, /* delProperty */ 1.1625 + JS_PropertyStub, /* getProperty */ 1.1626 + JS_StrictPropertyStub, /* setProperty */ 1.1627 + JS_EnumerateStub, 1.1628 + JS_ResolveStub, 1.1629 + JS_ConvertStub, 1.1630 + FinalizeGenerator<StarGeneratorObject>, 1.1631 + nullptr, /* call */ 1.1632 + nullptr, /* hasInstance */ 1.1633 + nullptr, /* construct */ 1.1634 + TraceGenerator<StarGeneratorObject>, 1.1635 + JS_NULL_CLASS_SPEC, 1.1636 + { 1.1637 + nullptr, /* outerObject */ 1.1638 + nullptr, /* innerObject */ 1.1639 + iterator_iteratorObject, 1.1640 + } 1.1641 +}; 1.1642 + 1.1643 +/* 1.1644 + * Called from the JSOP_GENERATOR case in the interpreter, with fp referring 1.1645 + * to the frame by which the generator function was activated. Create a new 1.1646 + * JSGenerator object, which contains its own InterpreterFrame that we populate 1.1647 + * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return 1.1648 + * from the activation in fp, so we can steal away fp->callobj and fp->argsobj 1.1649 + * if they are non-null. 1.1650 + */ 1.1651 +JSObject * 1.1652 +js_NewGenerator(JSContext *cx, const InterpreterRegs &stackRegs) 1.1653 +{ 1.1654 + JS_ASSERT(stackRegs.stackDepth() == 0); 1.1655 + InterpreterFrame *stackfp = stackRegs.fp(); 1.1656 + 1.1657 + JS_ASSERT(stackfp->script()->isGenerator()); 1.1658 + 1.1659 + Rooted<GlobalObject*> global(cx, &stackfp->global()); 1.1660 + RootedObject obj(cx); 1.1661 + if (stackfp->script()->isStarGenerator()) { 1.1662 + RootedValue pval(cx); 1.1663 + RootedObject fun(cx, stackfp->fun()); 1.1664 + // FIXME: This would be faster if we could avoid doing a lookup to get 1.1665 + // the prototype for the instance. Bug 906600. 1.1666 + if (!JSObject::getProperty(cx, fun, fun, cx->names().prototype, &pval)) 1.1667 + return nullptr; 1.1668 + JSObject *proto = pval.isObject() ? &pval.toObject() : nullptr; 1.1669 + if (!proto) { 1.1670 + proto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global); 1.1671 + if (!proto) 1.1672 + return nullptr; 1.1673 + } 1.1674 + obj = NewObjectWithGivenProto(cx, &StarGeneratorObject::class_, proto, global); 1.1675 + } else { 1.1676 + JS_ASSERT(stackfp->script()->isLegacyGenerator()); 1.1677 + JSObject *proto = GlobalObject::getOrCreateLegacyGeneratorObjectPrototype(cx, global); 1.1678 + if (!proto) 1.1679 + return nullptr; 1.1680 + obj = NewObjectWithGivenProto(cx, &LegacyGeneratorObject::class_, proto, global); 1.1681 + } 1.1682 + if (!obj) 1.1683 + return nullptr; 1.1684 + 1.1685 + /* Load and compute stack slot counts. */ 1.1686 + Value *stackvp = stackfp->generatorArgsSnapshotBegin(); 1.1687 + unsigned vplen = stackfp->generatorArgsSnapshotEnd() - stackvp; 1.1688 + 1.1689 + /* Compute JSGenerator size. */ 1.1690 + unsigned nbytes = sizeof(JSGenerator) + 1.1691 + (-1 + /* one Value included in JSGenerator */ 1.1692 + vplen + 1.1693 + VALUES_PER_STACK_FRAME + 1.1694 + stackfp->script()->nslots()) * sizeof(HeapValue); 1.1695 + 1.1696 + JS_ASSERT(nbytes % sizeof(Value) == 0); 1.1697 + JS_STATIC_ASSERT(sizeof(InterpreterFrame) % sizeof(HeapValue) == 0); 1.1698 + 1.1699 + JSGenerator *gen = (JSGenerator *) cx->calloc_(nbytes); 1.1700 + if (!gen) 1.1701 + return nullptr; 1.1702 + 1.1703 + /* Cut up floatingStack space. */ 1.1704 + HeapValue *genvp = gen->stackSnapshot; 1.1705 + SetValueRangeToUndefined((Value *)genvp, vplen); 1.1706 + 1.1707 + InterpreterFrame *genfp = reinterpret_cast<InterpreterFrame *>(genvp + vplen); 1.1708 + 1.1709 + /* Initialize JSGenerator. */ 1.1710 + gen->obj.init(obj); 1.1711 + gen->state = JSGEN_NEWBORN; 1.1712 + gen->fp = genfp; 1.1713 + gen->prevGenerator = nullptr; 1.1714 + 1.1715 + /* Copy from the stack to the generator's floating frame. */ 1.1716 + gen->regs.rebaseFromTo(stackRegs, *genfp); 1.1717 + genfp->copyFrameAndValues<InterpreterFrame::DoPostBarrier>(cx, (Value *)genvp, stackfp, 1.1718 + stackvp, stackRegs.sp); 1.1719 + genfp->setSuspended(); 1.1720 + obj->setPrivate(gen); 1.1721 + return obj; 1.1722 +} 1.1723 + 1.1724 +static void 1.1725 +SetGeneratorClosed(JSContext *cx, JSGenerator *gen); 1.1726 + 1.1727 +typedef enum JSGeneratorOp { 1.1728 + JSGENOP_NEXT, 1.1729 + JSGENOP_SEND, 1.1730 + JSGENOP_THROW, 1.1731 + JSGENOP_CLOSE 1.1732 +} JSGeneratorOp; 1.1733 + 1.1734 +/* 1.1735 + * Start newborn or restart yielding generator and perform the requested 1.1736 + * operation inside its frame. 1.1737 + */ 1.1738 +static bool 1.1739 +SendToGenerator(JSContext *cx, JSGeneratorOp op, HandleObject obj, 1.1740 + JSGenerator *gen, HandleValue arg, GeneratorKind generatorKind, 1.1741 + MutableHandleValue rval) 1.1742 +{ 1.1743 + JS_ASSERT(generatorKind == LegacyGenerator || generatorKind == StarGenerator); 1.1744 + 1.1745 + if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) { 1.1746 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NESTING_GENERATOR); 1.1747 + return false; 1.1748 + } 1.1749 + 1.1750 + JSGeneratorState futureState; 1.1751 + JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN); 1.1752 + switch (op) { 1.1753 + case JSGENOP_NEXT: 1.1754 + case JSGENOP_SEND: 1.1755 + if (gen->state == JSGEN_OPEN) { 1.1756 + /* 1.1757 + * Store the argument to send as the result of the yield 1.1758 + * expression. The generator stack is not barriered, so we need 1.1759 + * write barriers here. 1.1760 + */ 1.1761 + HeapValue::writeBarrierPre(gen->regs.sp[-1]); 1.1762 + gen->regs.sp[-1] = arg; 1.1763 + HeapValue::writeBarrierPost(cx->runtime(), gen->regs.sp[-1], &gen->regs.sp[-1]); 1.1764 + } 1.1765 + futureState = JSGEN_RUNNING; 1.1766 + break; 1.1767 + 1.1768 + case JSGENOP_THROW: 1.1769 + cx->setPendingException(arg); 1.1770 + futureState = JSGEN_RUNNING; 1.1771 + break; 1.1772 + 1.1773 + default: 1.1774 + JS_ASSERT(op == JSGENOP_CLOSE); 1.1775 + JS_ASSERT(generatorKind == LegacyGenerator); 1.1776 + cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING)); 1.1777 + futureState = JSGEN_CLOSING; 1.1778 + break; 1.1779 + } 1.1780 + 1.1781 + bool ok; 1.1782 + { 1.1783 + GeneratorState state(cx, gen, futureState); 1.1784 + ok = RunScript(cx, state); 1.1785 + if (!ok && gen->state == JSGEN_CLOSED) 1.1786 + return false; 1.1787 + } 1.1788 + 1.1789 + if (gen->fp->isYielding()) { 1.1790 + /* 1.1791 + * Yield is ordinarily infallible, but ok can be false here if a 1.1792 + * Debugger.Frame.onPop hook fails. 1.1793 + */ 1.1794 + JS_ASSERT(gen->state == JSGEN_RUNNING); 1.1795 + JS_ASSERT(op != JSGENOP_CLOSE); 1.1796 + gen->fp->clearYielding(); 1.1797 + gen->state = JSGEN_OPEN; 1.1798 + GeneratorWriteBarrierPost(cx, gen); 1.1799 + rval.set(gen->fp->returnValue()); 1.1800 + return ok; 1.1801 + } 1.1802 + 1.1803 + if (ok) { 1.1804 + if (generatorKind == StarGenerator) { 1.1805 + // Star generators return a {value:FOO, done:true} object. 1.1806 + rval.set(gen->fp->returnValue()); 1.1807 + } else { 1.1808 + JS_ASSERT(generatorKind == LegacyGenerator); 1.1809 + 1.1810 + // Otherwise we discard the return value and throw a StopIteration 1.1811 + // if needed. 1.1812 + rval.setUndefined(); 1.1813 + if (op != JSGENOP_CLOSE) 1.1814 + ok = js_ThrowStopIteration(cx); 1.1815 + } 1.1816 + } 1.1817 + 1.1818 + SetGeneratorClosed(cx, gen); 1.1819 + return ok; 1.1820 +} 1.1821 + 1.1822 +MOZ_ALWAYS_INLINE bool 1.1823 +star_generator_next(JSContext *cx, CallArgs args) 1.1824 +{ 1.1825 + RootedObject thisObj(cx, &args.thisv().toObject()); 1.1826 + JSGenerator *gen = thisObj->as<StarGeneratorObject>().getGenerator(); 1.1827 + 1.1828 + if (gen->state == JSGEN_CLOSED) { 1.1829 + RootedObject obj(cx, CreateItrResultObject(cx, JS::UndefinedHandleValue, true)); 1.1830 + if (!obj) 1.1831 + return false; 1.1832 + args.rval().setObject(*obj); 1.1833 + return true; 1.1834 + } 1.1835 + 1.1836 + if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) { 1.1837 + RootedValue val(cx, args[0]); 1.1838 + js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND, 1.1839 + JSDVG_SEARCH_STACK, val, js::NullPtr()); 1.1840 + return false; 1.1841 + } 1.1842 + 1.1843 + return SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0), StarGenerator, 1.1844 + args.rval()); 1.1845 +} 1.1846 + 1.1847 +MOZ_ALWAYS_INLINE bool 1.1848 +star_generator_throw(JSContext *cx, CallArgs args) 1.1849 +{ 1.1850 + RootedObject thisObj(cx, &args.thisv().toObject()); 1.1851 + 1.1852 + JSGenerator *gen = thisObj->as<StarGeneratorObject>().getGenerator(); 1.1853 + if (gen->state == JSGEN_CLOSED) { 1.1854 + cx->setPendingException(args.get(0)); 1.1855 + return false; 1.1856 + } 1.1857 + 1.1858 + return SendToGenerator(cx, JSGENOP_THROW, thisObj, gen, args.get(0), StarGenerator, 1.1859 + args.rval()); 1.1860 +} 1.1861 + 1.1862 +MOZ_ALWAYS_INLINE bool 1.1863 +legacy_generator_next(JSContext *cx, CallArgs args) 1.1864 +{ 1.1865 + RootedObject thisObj(cx, &args.thisv().toObject()); 1.1866 + 1.1867 + JSGenerator *gen = thisObj->as<LegacyGeneratorObject>().getGenerator(); 1.1868 + if (gen->state == JSGEN_CLOSED) 1.1869 + return js_ThrowStopIteration(cx); 1.1870 + 1.1871 + if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) { 1.1872 + RootedValue val(cx, args[0]); 1.1873 + js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND, 1.1874 + JSDVG_SEARCH_STACK, val, js::NullPtr()); 1.1875 + return false; 1.1876 + } 1.1877 + 1.1878 + return SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0), LegacyGenerator, 1.1879 + args.rval()); 1.1880 +} 1.1881 + 1.1882 +MOZ_ALWAYS_INLINE bool 1.1883 +legacy_generator_throw(JSContext *cx, CallArgs args) 1.1884 +{ 1.1885 + RootedObject thisObj(cx, &args.thisv().toObject()); 1.1886 + 1.1887 + JSGenerator *gen = thisObj->as<LegacyGeneratorObject>().getGenerator(); 1.1888 + if (gen->state == JSGEN_CLOSED) { 1.1889 + cx->setPendingException(args.length() >= 1 ? args[0] : UndefinedValue()); 1.1890 + return false; 1.1891 + } 1.1892 + 1.1893 + return SendToGenerator(cx, JSGENOP_THROW, thisObj, gen, args.get(0), LegacyGenerator, 1.1894 + args.rval()); 1.1895 +} 1.1896 + 1.1897 +static bool 1.1898 +CloseLegacyGenerator(JSContext *cx, HandleObject obj, MutableHandleValue rval) 1.1899 +{ 1.1900 + JS_ASSERT(obj->is<LegacyGeneratorObject>()); 1.1901 + 1.1902 + JSGenerator *gen = obj->as<LegacyGeneratorObject>().getGenerator(); 1.1903 + 1.1904 + if (gen->state == JSGEN_CLOSED) { 1.1905 + rval.setUndefined(); 1.1906 + return true; 1.1907 + } 1.1908 + 1.1909 + if (gen->state == JSGEN_NEWBORN) { 1.1910 + SetGeneratorClosed(cx, gen); 1.1911 + rval.setUndefined(); 1.1912 + return true; 1.1913 + } 1.1914 + 1.1915 + return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, JS::UndefinedHandleValue, LegacyGenerator, 1.1916 + rval); 1.1917 +} 1.1918 + 1.1919 +static bool 1.1920 +CloseLegacyGenerator(JSContext *cx, HandleObject obj) 1.1921 +{ 1.1922 + RootedValue rval(cx); 1.1923 + return CloseLegacyGenerator(cx, obj, &rval); 1.1924 +} 1.1925 + 1.1926 +MOZ_ALWAYS_INLINE bool 1.1927 +legacy_generator_close(JSContext *cx, CallArgs args) 1.1928 +{ 1.1929 + RootedObject thisObj(cx, &args.thisv().toObject()); 1.1930 + 1.1931 + return CloseLegacyGenerator(cx, thisObj, args.rval()); 1.1932 +} 1.1933 + 1.1934 +template<typename T> 1.1935 +MOZ_ALWAYS_INLINE bool 1.1936 +IsObjectOfType(HandleValue v) 1.1937 +{ 1.1938 + return v.isObject() && v.toObject().is<T>(); 1.1939 +} 1.1940 + 1.1941 +template<typename T, NativeImpl Impl> 1.1942 +static bool 1.1943 +NativeMethod(JSContext *cx, unsigned argc, Value *vp) 1.1944 +{ 1.1945 + CallArgs args = CallArgsFromVp(argc, vp); 1.1946 + return CallNonGenericMethod<IsObjectOfType<T>, Impl>(cx, args); 1.1947 +} 1.1948 + 1.1949 +#define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT) 1.1950 +#define JS_METHOD(name, T, impl, len, attrs) JS_FN(name, (NativeMethod<T,impl>), len, attrs) 1.1951 + 1.1952 +static const JSFunctionSpec star_generator_methods[] = { 1.1953 + JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0), 1.1954 + JS_METHOD("next", StarGeneratorObject, star_generator_next, 1, 0), 1.1955 + JS_METHOD("throw", StarGeneratorObject, star_generator_throw, 1, 0), 1.1956 + JS_FS_END 1.1957 +}; 1.1958 + 1.1959 +static const JSFunctionSpec legacy_generator_methods[] = { 1.1960 + JS_SELF_HOSTED_FN("@@iterator", "LegacyGeneratorIteratorShim", 0, 0), 1.1961 + // "send" is an alias for "next". 1.1962 + JS_METHOD("next", LegacyGeneratorObject, legacy_generator_next, 1, JSPROP_ROPERM), 1.1963 + JS_METHOD("send", LegacyGeneratorObject, legacy_generator_next, 1, JSPROP_ROPERM), 1.1964 + JS_METHOD("throw", LegacyGeneratorObject, legacy_generator_throw, 1, JSPROP_ROPERM), 1.1965 + JS_METHOD("close", LegacyGeneratorObject, legacy_generator_close, 0, JSPROP_ROPERM), 1.1966 + JS_FS_END 1.1967 +}; 1.1968 + 1.1969 +static JSObject* 1.1970 +NewSingletonObjectWithObjectPrototype(JSContext *cx, Handle<GlobalObject *> global) 1.1971 +{ 1.1972 + JSObject *proto = global->getOrCreateObjectPrototype(cx); 1.1973 + if (!proto) 1.1974 + return nullptr; 1.1975 + return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject); 1.1976 +} 1.1977 + 1.1978 +static JSObject* 1.1979 +NewSingletonObjectWithFunctionPrototype(JSContext *cx, Handle<GlobalObject *> global) 1.1980 +{ 1.1981 + JSObject *proto = global->getOrCreateFunctionPrototype(cx); 1.1982 + if (!proto) 1.1983 + return nullptr; 1.1984 + return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject); 1.1985 +} 1.1986 + 1.1987 +/* static */ bool 1.1988 +GlobalObject::initIteratorClasses(JSContext *cx, Handle<GlobalObject *> global) 1.1989 +{ 1.1990 + RootedObject iteratorProto(cx); 1.1991 + Value iteratorProtoVal = global->getPrototype(JSProto_Iterator); 1.1992 + if (iteratorProtoVal.isObject()) { 1.1993 + iteratorProto = &iteratorProtoVal.toObject(); 1.1994 + } else { 1.1995 + iteratorProto = global->createBlankPrototype(cx, &PropertyIteratorObject::class_); 1.1996 + if (!iteratorProto) 1.1997 + return false; 1.1998 + 1.1999 + AutoIdVector blank(cx); 1.2000 + NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, blank); 1.2001 + if (!ni) 1.2002 + return false; 1.2003 + ni->init(nullptr, nullptr, 0 /* flags */, 0, 0); 1.2004 + 1.2005 + iteratorProto->as<PropertyIteratorObject>().setNativeIterator(ni); 1.2006 + 1.2007 + Rooted<JSFunction*> ctor(cx); 1.2008 + ctor = global->createConstructor(cx, IteratorConstructor, cx->names().Iterator, 2); 1.2009 + if (!ctor) 1.2010 + return false; 1.2011 + if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto)) 1.2012 + return false; 1.2013 + if (!DefinePropertiesAndBrand(cx, iteratorProto, nullptr, iterator_methods)) 1.2014 + return false; 1.2015 + if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Iterator, 1.2016 + ctor, iteratorProto)) 1.2017 + { 1.2018 + return false; 1.2019 + } 1.2020 + } 1.2021 + 1.2022 + RootedObject proto(cx); 1.2023 + if (global->getSlot(ARRAY_ITERATOR_PROTO).isUndefined()) { 1.2024 + const Class *cls = &ArrayIteratorObject::class_; 1.2025 + proto = global->createBlankPrototypeInheriting(cx, cls, *iteratorProto); 1.2026 + if (!proto || !DefinePropertiesAndBrand(cx, proto, nullptr, array_iterator_methods)) 1.2027 + return false; 1.2028 + global->setReservedSlot(ARRAY_ITERATOR_PROTO, ObjectValue(*proto)); 1.2029 + } 1.2030 + 1.2031 + if (global->getSlot(STRING_ITERATOR_PROTO).isUndefined()) { 1.2032 + const Class *cls = &StringIteratorPrototypeClass; 1.2033 + proto = global->createBlankPrototype(cx, cls); 1.2034 + if (!proto || !DefinePropertiesAndBrand(cx, proto, nullptr, string_iterator_methods)) 1.2035 + return false; 1.2036 + global->setReservedSlot(STRING_ITERATOR_PROTO, ObjectValue(*proto)); 1.2037 + } 1.2038 + 1.2039 + if (global->getSlot(LEGACY_GENERATOR_OBJECT_PROTO).isUndefined()) { 1.2040 + proto = NewSingletonObjectWithObjectPrototype(cx, global); 1.2041 + if (!proto || !DefinePropertiesAndBrand(cx, proto, nullptr, legacy_generator_methods)) 1.2042 + return false; 1.2043 + global->setReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO, ObjectValue(*proto)); 1.2044 + } 1.2045 + 1.2046 + if (global->getSlot(STAR_GENERATOR_OBJECT_PROTO).isUndefined()) { 1.2047 + RootedObject genObjectProto(cx, NewSingletonObjectWithObjectPrototype(cx, global)); 1.2048 + if (!genObjectProto) 1.2049 + return false; 1.2050 + if (!DefinePropertiesAndBrand(cx, genObjectProto, nullptr, star_generator_methods)) 1.2051 + return false; 1.2052 + 1.2053 + RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global)); 1.2054 + if (!genFunctionProto) 1.2055 + return false; 1.2056 + if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto)) 1.2057 + return false; 1.2058 + 1.2059 + RootedValue function(cx, global->getConstructor(JSProto_Function)); 1.2060 + if (!function.toObjectOrNull()) 1.2061 + return false; 1.2062 + RootedAtom name(cx, cx->names().GeneratorFunction); 1.2063 + RootedObject genFunction(cx, NewFunctionWithProto(cx, NullPtr(), Generator, 1, 1.2064 + JSFunction::NATIVE_CTOR, global, name, 1.2065 + &function.toObject())); 1.2066 + if (!genFunction) 1.2067 + return false; 1.2068 + if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto)) 1.2069 + return false; 1.2070 + 1.2071 + global->setSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto)); 1.2072 + global->setConstructor(JSProto_GeneratorFunction, ObjectValue(*genFunction)); 1.2073 + global->setPrototype(JSProto_GeneratorFunction, ObjectValue(*genFunctionProto)); 1.2074 + } 1.2075 + 1.2076 + if (global->getPrototype(JSProto_StopIteration).isUndefined()) { 1.2077 + proto = global->createBlankPrototype(cx, &StopIterationObject::class_); 1.2078 + if (!proto || !JSObject::freeze(cx, proto)) 1.2079 + return false; 1.2080 + 1.2081 + // This should use a non-JSProtoKey'd slot, but this is easier for now. 1.2082 + if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_StopIteration, proto, proto)) 1.2083 + return false; 1.2084 + 1.2085 + global->setConstructor(JSProto_StopIteration, ObjectValue(*proto)); 1.2086 + } 1.2087 + 1.2088 + return true; 1.2089 +} 1.2090 + 1.2091 +JSObject * 1.2092 +js_InitIteratorClasses(JSContext *cx, HandleObject obj) 1.2093 +{ 1.2094 + Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); 1.2095 + if (!GlobalObject::initIteratorClasses(cx, global)) 1.2096 + return nullptr; 1.2097 + return global->getIteratorPrototype(); 1.2098 +}