| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifndef jsiter_h |
| #define jsiter_h |
| |
| /* |
| * JavaScript iterators. |
| */ |
| #include "jscntxt.h" |
| |
| #include "gc/Barrier.h" |
| #include "vm/Stack.h" |
| |
| /* |
| * For cacheable native iterators, whether the iterator is currently active. |
| * Not serialized by XDR. |
| */ |
| #define JSITER_ACTIVE 0x1000 |
| #define JSITER_UNREUSABLE 0x2000 |
| |
| namespace js { |
| |
| struct NativeIterator |
| { |
| HeapPtrObject obj; // Object being iterated. |
| JSObject *iterObj_; // Internal iterator object. |
| HeapPtr<JSFlatString> *props_array; |
| HeapPtr<JSFlatString> *props_cursor; |
| HeapPtr<JSFlatString> *props_end; |
| Shape **shapes_array; |
| uint32_t shapes_length; |
| uint32_t shapes_key; |
| uint32_t flags; |
| |
| private: |
| /* While in compartment->enumerators, these form a doubly linked list. */ |
| NativeIterator *next_; |
| NativeIterator *prev_; |
| |
| public: |
| bool isKeyIter() const { |
| return (flags & JSITER_FOREACH) == 0; |
| } |
| |
| inline HeapPtr<JSFlatString> *begin() const { |
| return props_array; |
| } |
| |
| inline HeapPtr<JSFlatString> *end() const { |
| return props_end; |
| } |
| |
| size_t numKeys() const { |
| return end() - begin(); |
| } |
| |
| JSObject *iterObj() const { |
| return iterObj_; |
| } |
| HeapPtr<JSFlatString> *current() const { |
| JS_ASSERT(props_cursor < props_end); |
| return props_cursor; |
| } |
| |
| NativeIterator *next() { |
| return next_; |
| } |
| |
| static inline size_t offsetOfNext() { |
| return offsetof(NativeIterator, next_); |
| } |
| static inline size_t offsetOfPrev() { |
| return offsetof(NativeIterator, prev_); |
| } |
| |
| void incCursor() { |
| props_cursor = props_cursor + 1; |
| } |
| void link(NativeIterator *other) { |
| /* A NativeIterator cannot appear in the enumerator list twice. */ |
| JS_ASSERT(!next_ && !prev_); |
| JS_ASSERT(flags & JSITER_ENUMERATE); |
| |
| this->next_ = other; |
| this->prev_ = other->prev_; |
| other->prev_->next_ = this; |
| other->prev_ = this; |
| } |
| void unlink() { |
| JS_ASSERT(flags & JSITER_ENUMERATE); |
| |
| next_->prev_ = prev_; |
| prev_->next_ = next_; |
| next_ = NULL; |
| prev_ = NULL; |
| } |
| |
| static NativeIterator *allocateSentinel(JSContext *cx); |
| static NativeIterator *allocateIterator(JSContext *cx, uint32_t slength, |
| const js::AutoIdVector &props); |
| void init(JSObject *obj, JSObject *iterObj, unsigned flags, uint32_t slength, uint32_t key); |
| |
| void mark(JSTracer *trc); |
| |
| static void destroy(NativeIterator *iter) { |
| js_free(iter); |
| } |
| }; |
| |
| class PropertyIteratorObject : public JSObject |
| { |
| public: |
| static Class class_; |
| |
| inline NativeIterator *getNativeIterator() const; |
| inline void setNativeIterator(js::NativeIterator *ni); |
| |
| size_t sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const; |
| |
| private: |
| static void trace(JSTracer *trc, JSObject *obj); |
| static void finalize(FreeOp *fop, JSObject *obj); |
| }; |
| |
| /* |
| * Array iterators are roughly like this: |
| * |
| * Array.prototype.iterator = function iterator() { |
| * for (var i = 0; i < (this.length >>> 0); i++) |
| * yield this[i]; |
| * } |
| * |
| * However they are not generators. They are a different class. The semantics |
| * of Array iterators will be given in the eventual ES6 spec in full detail. |
| */ |
| class ElementIteratorObject : public JSObject |
| { |
| public: |
| static Class class_; |
| |
| static JSObject *create(JSContext *cx, Handle<Value> target); |
| static const JSFunctionSpec methods[]; |
| |
| enum { |
| TargetSlot, |
| IndexSlot, |
| NumSlots |
| }; |
| |
| static JSBool next(JSContext *cx, unsigned argc, Value *vp); |
| static bool next_impl(JSContext *cx, JS::CallArgs args); |
| }; |
| |
| bool |
| VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap); |
| |
| bool |
| GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleValue vp); |
| |
| JSObject * |
| GetIteratorObject(JSContext *cx, HandleObject obj, unsigned flags); |
| |
| bool |
| VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &props, |
| MutableHandleValue vp); |
| |
| bool |
| VectorToValueIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &props, |
| MutableHandleValue vp); |
| |
| /* |
| * Creates either a key or value iterator, depending on flags. For a value |
| * iterator, performs value-lookup to convert the given list of jsids. |
| */ |
| bool |
| EnumeratedIdVectorToIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &props, |
| MutableHandleValue vp); |
| |
| /* |
| * Convert the value stored in *vp to its iteration object. The flags should |
| * contain JSITER_ENUMERATE if js::ValueToIterator is called when enumerating |
| * for-in semantics are required, and when the caller can guarantee that the |
| * iterator will never be exposed to scripts. |
| */ |
| bool |
| ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp); |
| |
| bool |
| CloseIterator(JSContext *cx, HandleObject iterObj); |
| |
| bool |
| UnwindIteratorForException(JSContext *cx, js::HandleObject obj); |
| |
| void |
| UnwindIteratorForUncatchableException(JSContext *cx, JSObject *obj); |
| |
| JSBool |
| IteratorConstructor(JSContext *cx, unsigned argc, Value *vp); |
| |
| } |
| |
| extern bool |
| js_SuppressDeletedProperty(JSContext *cx, js::HandleObject obj, jsid id); |
| |
| extern bool |
| js_SuppressDeletedElement(JSContext *cx, js::HandleObject obj, uint32_t index); |
| |
| extern bool |
| js_SuppressDeletedElements(JSContext *cx, js::HandleObject obj, uint32_t begin, uint32_t end); |
| |
| /* |
| * IteratorMore() indicates whether another value is available. It might |
| * internally call iterobj.next() and then cache the value until its |
| * picked up by IteratorNext(). The value is cached in the current context. |
| */ |
| extern bool |
| js_IteratorMore(JSContext *cx, js::HandleObject iterobj, js::MutableHandleValue rval); |
| |
| extern bool |
| js_IteratorNext(JSContext *cx, js::HandleObject iterobj, js::MutableHandleValue rval); |
| |
| extern JSBool |
| js_ThrowStopIteration(JSContext *cx); |
| |
| namespace js { |
| |
| /* |
| * Get the next value from an iterator object. |
| * |
| * On success, store the next value in *vp and return true; if there are no |
| * more values, store the magic value JS_NO_ITER_VALUE in *vp and return true. |
| */ |
| inline bool |
| Next(JSContext *cx, HandleObject iter, MutableHandleValue vp) |
| { |
| if (!js_IteratorMore(cx, iter, vp)) |
| return false; |
| if (vp.toBoolean()) |
| return js_IteratorNext(cx, iter, vp); |
| vp.setMagic(JS_NO_ITER_VALUE); |
| return true; |
| } |
| |
| /* |
| * Convenience class for imitating a JS level for-of loop. Typical usage: |
| * |
| * ForOfIterator it(cx, iterable); |
| * while (it.next()) { |
| * if (!DoStuff(cx, it.value())) |
| * return false; |
| * } |
| * if (!it.close()) |
| * return false; |
| * |
| * The final it.close() check is needed in order to check for cases where |
| * any of the iterator operations fail. |
| * |
| * it.close() may be skipped only if something in the body of the loop fails |
| * and the failure is allowed to propagate on cx, as in this example if DoStuff |
| * fails. In that case, ForOfIterator's destructor does all necessary cleanup. |
| */ |
| class ForOfIterator |
| { |
| private: |
| JSContext *cx; |
| RootedObject iterator; |
| RootedValue currentValue; |
| bool ok; |
| bool closed; |
| |
| ForOfIterator(const ForOfIterator &) MOZ_DELETE; |
| ForOfIterator &operator=(const ForOfIterator &) MOZ_DELETE; |
| |
| public: |
| ForOfIterator(JSContext *cx, const Value &iterable) |
| : cx(cx), iterator(cx, NULL), currentValue(cx), closed(false) |
| { |
| RootedValue iterv(cx, iterable); |
| ok = ValueToIterator(cx, JSITER_FOR_OF, &iterv); |
| iterator = ok ? &iterv.get().toObject() : NULL; |
| } |
| |
| ~ForOfIterator() { |
| if (!closed) |
| close(); |
| } |
| |
| bool next() { |
| JS_ASSERT(!closed); |
| ok = ok && Next(cx, iterator, ¤tValue); |
| return ok && !currentValue.get().isMagic(JS_NO_ITER_VALUE); |
| } |
| |
| Value &value() { |
| JS_ASSERT(ok); |
| JS_ASSERT(!closed); |
| return currentValue.get(); |
| } |
| |
| bool close() { |
| JS_ASSERT(!closed); |
| closed = true; |
| if (!iterator) |
| return false; |
| bool throwing = cx->isExceptionPending(); |
| RootedValue exc(cx); |
| if (throwing) { |
| exc = cx->getPendingException(); |
| cx->clearPendingException(); |
| } |
| bool closedOK = CloseIterator(cx, iterator); |
| if (throwing && closedOK) |
| cx->setPendingException(exc); |
| return ok && !throwing && closedOK; |
| } |
| }; |
| |
| } /* namespace js */ |
| |
| #if JS_HAS_GENERATORS |
| |
| /* |
| * Generator state codes. |
| */ |
| enum JSGeneratorState |
| { |
| JSGEN_NEWBORN, /* not yet started */ |
| JSGEN_OPEN, /* started by a .next() or .send(undefined) call */ |
| JSGEN_RUNNING, /* currently executing via .next(), etc., call */ |
| JSGEN_CLOSING, /* close method is doing asynchronous return */ |
| JSGEN_CLOSED /* closed, cannot be started or closed again */ |
| }; |
| |
| struct JSGenerator |
| { |
| js::HeapPtrObject obj; |
| JSGeneratorState state; |
| js::FrameRegs regs; |
| JSGenerator *prevGenerator; |
| js::StackFrame *fp; |
| js::HeapValue stackSnapshot[1]; |
| }; |
| |
| extern JSObject * |
| js_NewGenerator(JSContext *cx, const js::FrameRegs ®s); |
| |
| namespace js { |
| |
| bool |
| GeneratorHasMarkableFrame(JSGenerator *gen); |
| |
| } /* namespace js */ |
| #endif |
| |
| extern JSObject * |
| js_InitIteratorClasses(JSContext *cx, js::HandleObject obj); |
| |
| #endif /* jsiter_h */ |