bool ForOfIterator::next(MutableHandleValue vp, bool* done) { MOZ_ASSERT(iterator); if (index != NOT_ARRAY) { ForOfPIC::Chain* stubChain = ForOfPIC::getOrCreate(cx_); if (!stubChain) return false; if (stubChain->isArrayNextStillSane()) return nextFromOptimizedArray(vp, done); // ArrayIterator.prototype.next changed, materialize a proper // ArrayIterator instance and fall through to slowpath case. if (!materializeArrayIterator()) return false; } RootedValue method(cx_); if (!GetProperty(cx_, iterator, iterator, cx_->names().next, &method)) return false; InvokeArgs args(cx_); if (!args.init(1)) return false; args.setCallee(method); args.setThis(ObjectValue(*iterator)); args[0].setUndefined(); if (!Invoke(cx_, args)) return false; RootedObject resultObj(cx_, ToObject(cx_, args.rval())); if (!resultObj) return false; RootedValue doneVal(cx_); if (!GetProperty(cx_, resultObj, resultObj, cx_->names().done, &doneVal)) return false; *done = ToBoolean(doneVal); if (*done) { vp.setUndefined(); return true; } return GetProperty(cx_, resultObj, resultObj, cx_->names().value, vp); }
bool ForOfIterator::next(MutableHandleValue vp, bool* done) { MOZ_ASSERT(iterator); if (index != NOT_ARRAY) { ForOfPIC::Chain* stubChain = ForOfPIC::getOrCreate(cx_); if (!stubChain) return false; if (stubChain->isArrayNextStillSane()) return nextFromOptimizedArray(vp, done); // ArrayIterator.prototype.next changed, materialize a proper // ArrayIterator instance and fall through to slowpath case. if (!materializeArrayIterator()) return false; } RootedValue v(cx_); if (!GetProperty(cx_, iterator, iterator, cx_->names().next, &v)) return false; if (!js::Call(cx_, v, iterator, &v)) return false; if (!v.isObject()) return ThrowCheckIsObject(cx_, CheckIsObjectKind::IteratorNext); RootedObject resultObj(cx_, &v.toObject()); if (!GetProperty(cx_, resultObj, resultObj, cx_->names().done, &v)) return false; *done = ToBoolean(v); if (*done) { vp.setUndefined(); return true; } return GetProperty(cx_, resultObj, resultObj, cx_->names().value, vp); }
bool ForOfIterator::init(HandleValue iterable, NonIterableBehavior nonIterableBehavior) { JSContext* cx = cx_; RootedObject iterableObj(cx, ToObject(cx, iterable)); if (!iterableObj) return false; MOZ_ASSERT(index == NOT_ARRAY); // Check the PIC first for a match. if (iterableObj->is<ArrayObject>()) { ForOfPIC::Chain* stubChain = ForOfPIC::getOrCreate(cx); if (!stubChain) return false; bool optimized; if (!stubChain->tryOptimizeArray(cx, iterableObj.as<ArrayObject>(), &optimized)) return false; if (optimized) { // Got optimized stub. Array is optimizable. index = 0; iterator = iterableObj; return true; } } MOZ_ASSERT(index == NOT_ARRAY); // The iterator is the result of calling obj[@@iterator](). InvokeArgs args(cx); if (!args.init(0)) return false; args.setThis(ObjectValue(*iterableObj)); RootedValue callee(cx); RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator)); if (!GetProperty(cx, iterableObj, iterableObj, iteratorId, &callee)) return false; // If obj[@@iterator] is undefined and we were asked to allow non-iterables, // bail out now without setting iterator. This will make valueIsIterable(), // which our caller should check, return false. if (nonIterableBehavior == AllowNonIterable && callee.isUndefined()) return true; // Throw if obj[@@iterator] isn't callable. // js::Invoke is about to check for this kind of error anyway, but it would // throw an inscrutable error message about |method| rather than this nice // one about |obj|. if (!callee.isObject() || !callee.toObject().isCallable()) { UniquePtr<char[], JS::FreePolicy> bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, iterable, nullptr); if (!bytes) return false; JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, bytes.get()); return false; } args.setCallee(callee); if (!Invoke(cx, args)) return false; iterator = ToObject(cx, args.rval()); if (!iterator) return false; return true; }