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