| /* -*- 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 vm_PIC_h |
| #define vm_PIC_h |
| |
| #include "jsapi.h" |
| #include "jscntxt.h" |
| #include "jsfriendapi.h" |
| #include "jsobj.h" |
| |
| #include "gc/Barrier.h" |
| #include "gc/Heap.h" |
| #include "gc/Marking.h" |
| |
| #include "js/Value.h" |
| #include "vm/GlobalObject.h" |
| |
| namespace js { |
| |
| class Shape; |
| |
| template <typename Category> class PICChain; |
| |
| /* |
| * The basic PICStub just has a pointer to the next stub. |
| */ |
| template <typename Category> |
| class PICStub |
| { |
| friend class PICChain<Category>; |
| private: |
| typedef typename Category::Stub CatStub; |
| typedef typename Category::Chain CatChain; |
| |
| protected: |
| CatStub* next_; |
| |
| PICStub() : next_(nullptr) {} |
| explicit PICStub(const CatStub* next) : next_(next) { |
| MOZ_ASSERT(next_); |
| } |
| explicit PICStub(const CatStub& other) : next_(other.next_) {} |
| |
| public: |
| CatStub* next() const { |
| return next_; |
| } |
| |
| protected: |
| void append(CatStub* stub) { |
| MOZ_ASSERT(!next_); |
| MOZ_ASSERT(!stub->next_); |
| next_ = stub; |
| } |
| }; |
| |
| /* |
| * The basic PIC just has a pointer to the list of stubs. |
| */ |
| template <typename Category> |
| class PICChain |
| { |
| private: |
| typedef typename Category::Stub CatStub; |
| typedef typename Category::Chain CatChain; |
| |
| protected: |
| CatStub* stubs_; |
| |
| PICChain() : stubs_(nullptr) {} |
| // PICs should never be copy constructed. |
| PICChain(const PICChain<Category>& other) = delete; |
| |
| public: |
| CatStub* stubs() const { |
| return stubs_; |
| } |
| |
| void addStub(CatStub* stub) { |
| MOZ_ASSERT(stub); |
| MOZ_ASSERT(!stub->next()); |
| if (!stubs_) { |
| stubs_ = stub; |
| return; |
| } |
| |
| CatStub* cur = stubs_; |
| while (cur->next()) |
| cur = cur->next(); |
| cur->append(stub); |
| } |
| |
| unsigned numStubs() const { |
| unsigned count = 0; |
| for (CatStub* stub = stubs_; stub; stub = stub->next()) |
| count++; |
| return count; |
| } |
| |
| void removeStub(CatStub* stub, CatStub* previous) { |
| if (previous) { |
| MOZ_ASSERT(previous->next() == stub); |
| previous->next_ = stub->next(); |
| } else { |
| MOZ_ASSERT(stub == stubs_); |
| stubs_ = stub->next(); |
| } |
| js_delete(stub); |
| } |
| }; |
| |
| /* |
| * ForOfPIC defines a PIC category for optimizing for-of operations. |
| */ |
| struct ForOfPIC |
| { |
| /* Forward declarations so template-substitution works. */ |
| class Stub; |
| class Chain; |
| |
| ForOfPIC() = delete; |
| ForOfPIC(const ForOfPIC& other) = delete; |
| |
| typedef PICStub<ForOfPIC> BaseStub; |
| typedef PICChain<ForOfPIC> BaseChain; |
| |
| /* |
| * A ForOfPIC has only one kind of stub for now: one that holds the shape |
| * of an array object that does not override its @@iterator property. |
| */ |
| class Stub : public BaseStub |
| { |
| private: |
| // Shape of matching array object. |
| Shape* shape_; |
| |
| public: |
| explicit Stub(Shape* shape) |
| : BaseStub(), |
| shape_(shape) |
| { |
| MOZ_ASSERT(shape_); |
| } |
| |
| Shape* shape() { |
| return shape_; |
| } |
| }; |
| |
| /* |
| * A ForOfPIC chain holds the following: |
| * |
| * Array.prototype (arrayProto_) |
| * To ensure that the incoming array has the standard proto. |
| * |
| * Array.prototype's shape (arrayProtoShape_) |
| * To ensure that Array.prototype has not been modified. |
| * |
| * ArrayIterator.prototype (arrayIteratorProto_) |
| * ArrayIterator.prototype's shape (arrayIteratorProtoShape_) |
| * To ensure that an ArrayIterator.prototype has not been modified. |
| * |
| * Array.prototype's slot number for @@iterator (arrayProtoIteratorSlot_) |
| * Array.prototype's canonical value for @@iterator (canonicalIteratorFunc_) |
| * To quickly retrieve and ensure that the iterator constructor |
| * stored in the slot has not changed. |
| * |
| * ArrayIterator.prototype's slot number for 'next' (arrayIteratorProtoNextSlot_) |
| * ArrayIterator.prototype's canonical value for 'next' (canonicalNextFunc_) |
| * To quickly retrieve and ensure that the 'next' method for ArrayIterator |
| * objects has not changed. |
| */ |
| class Chain : public BaseChain |
| { |
| private: |
| // Pointer to canonical Array.prototype and ArrayIterator.prototype |
| HeapPtrNativeObject arrayProto_; |
| HeapPtrNativeObject arrayIteratorProto_; |
| |
| // Shape of matching Array.prototype object, and slot containing |
| // the @@iterator for it, and the canonical value. |
| HeapPtrShape arrayProtoShape_; |
| uint32_t arrayProtoIteratorSlot_; |
| HeapValue canonicalIteratorFunc_; |
| |
| // Shape of matching ArrayIteratorProto, and slot containing |
| // the 'next' property, and the canonical value. |
| HeapPtrShape arrayIteratorProtoShape_; |
| uint32_t arrayIteratorProtoNextSlot_; |
| HeapValue canonicalNextFunc_; |
| |
| // Initialization flag marking lazy initialization of above fields. |
| bool initialized_; |
| |
| // Disabled flag is set when we don't want to try optimizing anymore |
| // because core objects were changed. |
| bool disabled_; |
| |
| static const unsigned MAX_STUBS = 10; |
| |
| public: |
| Chain() |
| : BaseChain(), |
| arrayProto_(nullptr), |
| arrayIteratorProto_(nullptr), |
| arrayProtoShape_(nullptr), |
| arrayProtoIteratorSlot_(-1), |
| canonicalIteratorFunc_(UndefinedValue()), |
| arrayIteratorProtoShape_(nullptr), |
| arrayIteratorProtoNextSlot_(-1), |
| initialized_(false), |
| disabled_(false) |
| {} |
| |
| // Initialize the canonical iterator function. |
| bool initialize(JSContext* cx); |
| |
| // Check if a given array object is optimized by this PIC. |
| Stub* isArrayOptimized(ArrayObject* obj); |
| |
| // Try to optimize this chain for an object. |
| bool tryOptimizeArray(JSContext* cx, HandleArrayObject array, bool* optimized); |
| |
| // Check if the global array-related objects have not been messed with |
| // in a way that would disable this PIC. |
| bool isArrayStateStillSane(); |
| |
| // Check if ArrayIterator.next is still optimizable. |
| inline bool isArrayNextStillSane() { |
| return (arrayIteratorProto_->lastProperty() == arrayIteratorProtoShape_) && |
| (arrayIteratorProto_->getSlot(arrayIteratorProtoNextSlot_) == canonicalNextFunc_); |
| } |
| |
| void mark(JSTracer* trc); |
| void sweep(FreeOp* fop); |
| |
| private: |
| // Get a matching optimized stub for the given object. |
| Stub* getMatchingStub(JSObject* obj); |
| |
| // Check if the given object is for-of optimizable with this PIC. |
| bool isOptimizableArray(JSObject* obj); |
| |
| // Reset the PIC and all info associated with it. |
| void reset(JSContext* cx); |
| |
| // Erase the stub chain. |
| void eraseChain(); |
| }; |
| |
| // Class for object that holds ForOfPIC chain. |
| static const Class jsclass; |
| |
| static NativeObject* createForOfPICObject(JSContext* cx, Handle<GlobalObject*> global); |
| |
| static inline Chain* fromJSObject(NativeObject* obj) { |
| MOZ_ASSERT(js::GetObjectClass(obj) == &ForOfPIC::jsclass); |
| return (ForOfPIC::Chain*) obj->getPrivate(); |
| } |
| static inline Chain* getOrCreate(JSContext* cx) { |
| NativeObject* obj = cx->global()->getForOfPICObject(); |
| if (obj) |
| return fromJSObject(obj); |
| return create(cx); |
| } |
| static Chain* create(JSContext* cx); |
| }; |
| |
| |
| } // namespace js |
| |
| #endif /* vm_PIC_h */ |