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