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