1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef vm_PIC_h 8 #define vm_PIC_h 9 10 #include "vm/GlobalObject.h" 11 #include "vm/NativeObject.h" 12 13 namespace js { 14 15 class Shape; 16 17 template <typename Category> 18 class PICChain; 19 20 /* 21 * The basic PICStub just has a pointer to the next stub. 22 */ 23 template <typename Category> 24 class PICStub { 25 friend class PICChain<Category>; 26 27 private: 28 using CatStub = typename Category::Stub; 29 using CatChain = typename Category::Chain; 30 31 protected: 32 CatStub* next_; 33 PICStub()34 PICStub() : next_(nullptr) {} PICStub(const CatStub * next)35 explicit PICStub(const CatStub* next) : next_(next) { MOZ_ASSERT(next_); } PICStub(const CatStub & other)36 explicit PICStub(const CatStub& other) : next_(other.next_) {} 37 38 public: next()39 CatStub* next() const { return next_; } 40 41 protected: append(CatStub * stub)42 void append(CatStub* stub) { 43 MOZ_ASSERT(!next_); 44 MOZ_ASSERT(!stub->next_); 45 next_ = stub; 46 } 47 }; 48 49 /* 50 * The basic PIC just has a pointer to the list of stubs. 51 */ 52 template <typename Category> 53 class PICChain { 54 private: 55 using CatStub = typename Category::Stub; 56 using CatChain = typename Category::Chain; 57 58 protected: 59 CatStub* stubs_; 60 PICChain()61 PICChain() : stubs_(nullptr) {} 62 // PICs should never be copy constructed. 63 PICChain(const PICChain<Category>& other) = delete; 64 65 public: stubs()66 CatStub* stubs() const { return stubs_; } 67 68 void addStub(JSObject* obj, CatStub* stub); 69 numStubs()70 unsigned numStubs() const { 71 unsigned count = 0; 72 for (CatStub* stub = stubs_; stub; stub = stub->next()) { 73 count++; 74 } 75 return count; 76 } 77 }; 78 79 // Class for object that holds ForOfPIC chain. 80 class ForOfPICObject : public NativeObject { 81 public: 82 enum { ChainSlot, SlotCount }; 83 84 static const JSClass class_; 85 }; 86 87 /* 88 * ForOfPIC defines a PIC category for optimizing for-of operations. 89 */ 90 struct ForOfPIC { 91 /* Forward declarations so template-substitution works. */ 92 class Stub; 93 class Chain; 94 95 ForOfPIC() = delete; 96 ForOfPIC(const ForOfPIC& other) = delete; 97 98 using BaseStub = PICStub<ForOfPIC>; 99 using BaseChain = PICChain<ForOfPIC>; 100 101 /* 102 * A ForOfPIC has only one kind of stub for now: one that holds the shape 103 * of an array object that does not override its @@iterator property. 104 */ 105 class Stub : public BaseStub { 106 private: 107 // Shape of matching array object. 108 Shape* shape_; 109 110 public: StubForOfPIC111 explicit Stub(Shape* shape) : BaseStub(), shape_(shape) { 112 MOZ_ASSERT(shape_); 113 } 114 shapeForOfPIC115 Shape* shape() { return shape_; } 116 }; 117 118 /* 119 * A ForOfPIC chain holds the following: 120 * 121 * Array.prototype (arrayProto_) 122 * To ensure that the incoming array has the standard proto. 123 * 124 * Array.prototype's shape (arrayProtoShape_) 125 * To ensure that Array.prototype has not been modified. 126 * 127 * ArrayIterator.prototype 128 * ArrayIterator.prototype's shape 129 * (arrayIteratorProto_, arrayIteratorProtoShape_) 130 * To ensure that an ArrayIterator.prototype has not been modified. 131 * 132 * Array.prototype's slot number for @@iterator 133 * Array.prototype's canonical value for @@iterator 134 * (arrayProtoIteratorSlot_, canonicalIteratorFunc_) 135 * To quickly retrieve and ensure that the iterator constructor 136 * stored in the slot has not changed. 137 * 138 * ArrayIterator.prototype's slot number for 'next' 139 * ArrayIterator.prototype's canonical value for 'next' 140 * (arrayIteratorProtoNextSlot_, canonicalNextFunc_) 141 * To quickly retrieve and ensure that the 'next' method for 142 * ArrayIterator objects has not changed. 143 */ 144 class Chain : public BaseChain { 145 private: 146 // Pointer to owning JSObject for memory accounting purposes. 147 const GCPtrObject picObject_; 148 149 // Pointer to canonical Array.prototype and ArrayIterator.prototype 150 GCPtrNativeObject arrayProto_; 151 GCPtrNativeObject arrayIteratorProto_; 152 153 // Shape of matching Array.prototype object, and slot containing 154 // the @@iterator for it, and the canonical value. 155 GCPtrShape arrayProtoShape_; 156 uint32_t arrayProtoIteratorSlot_; 157 GCPtrValue canonicalIteratorFunc_; 158 159 // Shape of matching ArrayIteratorProto, and slot containing 160 // the 'next' property, and the canonical value. 161 GCPtrShape arrayIteratorProtoShape_; 162 uint32_t arrayIteratorProtoNextSlot_; 163 GCPtrValue canonicalNextFunc_; 164 165 // Initialization flag marking lazy initialization of above fields. 166 bool initialized_; 167 168 // Disabled flag is set when we don't want to try optimizing anymore 169 // because core objects were changed. 170 bool disabled_; 171 172 static const unsigned MAX_STUBS = 10; 173 174 public: ChainForOfPIC175 explicit Chain(JSObject* picObject) 176 : BaseChain(), 177 picObject_(picObject), 178 arrayProto_(nullptr), 179 arrayIteratorProto_(nullptr), 180 arrayProtoShape_(nullptr), 181 arrayProtoIteratorSlot_(-1), 182 canonicalIteratorFunc_(UndefinedValue()), 183 arrayIteratorProtoShape_(nullptr), 184 arrayIteratorProtoNextSlot_(-1), 185 initialized_(false), 186 disabled_(false) {} 187 188 // Initialize the canonical iterator function. 189 bool initialize(JSContext* cx); 190 191 // Try to optimize this chain for an object. 192 bool tryOptimizeArray(JSContext* cx, HandleArrayObject array, 193 bool* optimized); 194 195 // Check if %ArrayIteratorPrototype% still uses the default "next" method. 196 bool tryOptimizeArrayIteratorNext(JSContext* cx, bool* optimized); 197 198 void trace(JSTracer* trc); 199 void finalize(JSFreeOp* fop, JSObject* obj); 200 201 private: 202 // Check if the global array-related objects have not been messed with 203 // in a way that would disable this PIC. 204 bool isArrayStateStillSane(); 205 206 // Check if ArrayIterator.next is still optimizable. isArrayNextStillSaneForOfPIC207 inline bool isArrayNextStillSane() { 208 return (arrayIteratorProto_->shape() == arrayIteratorProtoShape_) && 209 (arrayIteratorProto_->getSlot(arrayIteratorProtoNextSlot_) == 210 canonicalNextFunc_); 211 } 212 213 // Check if a matching optimized stub for the given object exists. 214 bool hasMatchingStub(ArrayObject* obj); 215 216 // Reset the PIC and all info associated with it. 217 void reset(JSContext* cx); 218 219 // Erase the stub chain. 220 void eraseChain(JSContext* cx); 221 222 void freeAllStubs(JSFreeOp* fop); 223 }; 224 225 static NativeObject* createForOfPICObject(JSContext* cx, 226 Handle<GlobalObject*> global); 227 fromJSObjectForOfPIC228 static inline Chain* fromJSObject(NativeObject* obj) { 229 MOZ_ASSERT(obj->is<ForOfPICObject>()); 230 return obj->maybePtrFromReservedSlot<Chain>(ForOfPICObject::ChainSlot); 231 } getOrCreateForOfPIC232 static inline Chain* getOrCreate(JSContext* cx) { 233 NativeObject* obj = cx->global()->getForOfPICObject(); 234 if (obj) { 235 return fromJSObject(obj); 236 } 237 return create(cx); 238 } 239 static Chain* create(JSContext* cx); 240 }; 241 242 } // namespace js 243 244 #endif /* vm_PIC_h */ 245