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 static const JSClass class_; 83 }; 84 85 /* 86 * ForOfPIC defines a PIC category for optimizing for-of operations. 87 */ 88 struct ForOfPIC { 89 /* Forward declarations so template-substitution works. */ 90 class Stub; 91 class Chain; 92 93 ForOfPIC() = delete; 94 ForOfPIC(const ForOfPIC& other) = delete; 95 96 using BaseStub = PICStub<ForOfPIC>; 97 using BaseChain = PICChain<ForOfPIC>; 98 99 /* 100 * A ForOfPIC has only one kind of stub for now: one that holds the shape 101 * of an array object that does not override its @@iterator property. 102 */ 103 class Stub : public BaseStub { 104 private: 105 // Shape of matching array object. 106 Shape* shape_; 107 108 public: StubForOfPIC109 explicit Stub(Shape* shape) : BaseStub(), shape_(shape) { 110 MOZ_ASSERT(shape_); 111 } 112 shapeForOfPIC113 Shape* shape() { return shape_; } 114 }; 115 116 /* 117 * A ForOfPIC chain holds the following: 118 * 119 * Array.prototype (arrayProto_) 120 * To ensure that the incoming array has the standard proto. 121 * 122 * Array.prototype's shape (arrayProtoShape_) 123 * To ensure that Array.prototype has not been modified. 124 * 125 * ArrayIterator.prototype 126 * ArrayIterator.prototype's shape 127 * (arrayIteratorProto_, arrayIteratorProtoShape_) 128 * To ensure that an ArrayIterator.prototype has not been modified. 129 * 130 * Array.prototype's slot number for @@iterator 131 * Array.prototype's canonical value for @@iterator 132 * (arrayProtoIteratorSlot_, canonicalIteratorFunc_) 133 * To quickly retrieve and ensure that the iterator constructor 134 * stored in the slot has not changed. 135 * 136 * ArrayIterator.prototype's slot number for 'next' 137 * ArrayIterator.prototype's canonical value for 'next' 138 * (arrayIteratorProtoNextSlot_, canonicalNextFunc_) 139 * To quickly retrieve and ensure that the 'next' method for 140 * ArrayIterator objects has not changed. 141 */ 142 class Chain : public BaseChain { 143 private: 144 // Pointer to owning JSObject for memory accounting purposes. 145 const GCPtrObject picObject_; 146 147 // Pointer to canonical Array.prototype and ArrayIterator.prototype 148 GCPtrNativeObject arrayProto_; 149 GCPtrNativeObject arrayIteratorProto_; 150 151 // Shape of matching Array.prototype object, and slot containing 152 // the @@iterator for it, and the canonical value. 153 GCPtrShape arrayProtoShape_; 154 uint32_t arrayProtoIteratorSlot_; 155 GCPtrValue canonicalIteratorFunc_; 156 157 // Shape of matching ArrayIteratorProto, and slot containing 158 // the 'next' property, and the canonical value. 159 GCPtrShape arrayIteratorProtoShape_; 160 uint32_t arrayIteratorProtoNextSlot_; 161 GCPtrValue canonicalNextFunc_; 162 163 // Initialization flag marking lazy initialization of above fields. 164 bool initialized_; 165 166 // Disabled flag is set when we don't want to try optimizing anymore 167 // because core objects were changed. 168 bool disabled_; 169 170 static const unsigned MAX_STUBS = 10; 171 172 public: ChainForOfPIC173 explicit Chain(JSObject* picObject) 174 : BaseChain(), 175 picObject_(picObject), 176 arrayProto_(nullptr), 177 arrayIteratorProto_(nullptr), 178 arrayProtoShape_(nullptr), 179 arrayProtoIteratorSlot_(-1), 180 canonicalIteratorFunc_(UndefinedValue()), 181 arrayIteratorProtoShape_(nullptr), 182 arrayIteratorProtoNextSlot_(-1), 183 initialized_(false), 184 disabled_(false) {} 185 186 // Initialize the canonical iterator function. 187 bool initialize(JSContext* cx); 188 189 // Try to optimize this chain for an object. 190 bool tryOptimizeArray(JSContext* cx, HandleArrayObject array, 191 bool* optimized); 192 193 // Check if %ArrayIteratorPrototype% still uses the default "next" method. 194 bool tryOptimizeArrayIteratorNext(JSContext* cx, bool* optimized); 195 196 void trace(JSTracer* trc); 197 void finalize(JSFreeOp* fop, JSObject* obj); 198 199 private: 200 // Check if the global array-related objects have not been messed with 201 // in a way that would disable this PIC. 202 bool isArrayStateStillSane(); 203 204 // Check if ArrayIterator.next is still optimizable. isArrayNextStillSaneForOfPIC205 inline bool isArrayNextStillSane() { 206 return (arrayIteratorProto_->shape() == arrayIteratorProtoShape_) && 207 (arrayIteratorProto_->getSlot(arrayIteratorProtoNextSlot_) == 208 canonicalNextFunc_); 209 } 210 211 // Check if a matching optimized stub for the given object exists. 212 bool hasMatchingStub(ArrayObject* obj); 213 214 // Reset the PIC and all info associated with it. 215 void reset(JSContext* cx); 216 217 // Erase the stub chain. 218 void eraseChain(JSContext* cx); 219 220 void freeAllStubs(JSFreeOp* fop); 221 }; 222 223 static NativeObject* createForOfPICObject(JSContext* cx, 224 Handle<GlobalObject*> global); 225 fromJSObjectForOfPIC226 static inline Chain* fromJSObject(NativeObject* obj) { 227 MOZ_ASSERT(obj->is<ForOfPICObject>()); 228 return (ForOfPIC::Chain*)obj->getPrivate(); 229 } getOrCreateForOfPIC230 static inline Chain* getOrCreate(JSContext* cx) { 231 NativeObject* obj = cx->global()->getForOfPICObject(); 232 if (obj) { 233 return fromJSObject(obj); 234 } 235 return create(cx); 236 } 237 static Chain* create(JSContext* cx); 238 }; 239 240 } // namespace js 241 242 #endif /* vm_PIC_h */ 243