js/src/vm/PIC.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:ac7d4c93368f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
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 #include "vm/PIC.h"
8 #include "jscntxt.h"
9 #include "jsobj.h"
10 #include "gc/Marking.h"
11
12 #include "vm/GlobalObject.h"
13 #include "vm/ObjectImpl.h"
14 #include "vm/SelfHosting.h"
15 #include "jsobjinlines.h"
16 #include "vm/ObjectImpl-inl.h"
17
18 using namespace js;
19 using namespace js::gc;
20
21 bool
22 js::ForOfPIC::Chain::initialize(JSContext *cx)
23 {
24 JS_ASSERT(!initialized_);
25
26 // Get the canonical Array.prototype
27 RootedObject arrayProto(cx, GlobalObject::getOrCreateArrayPrototype(cx, cx->global()));
28 if (!arrayProto)
29 return false;
30
31 // Get the canonical ArrayIterator.prototype
32 RootedObject arrayIteratorProto(cx,
33 GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global()));
34 if (!arrayIteratorProto)
35 return false;
36
37 // From this point on, we can't fail. Set initialized and fill the fields
38 // for the canonical Array.prototype and ArrayIterator.prototype objects.
39 initialized_ = true;
40 arrayProto_ = arrayProto;
41 arrayIteratorProto_ = arrayIteratorProto;
42
43 // Shortcut returns below means Array for-of will never be optimizable,
44 // do set disabled_ now, and clear it later when we succeed.
45 disabled_ = true;
46
47 // Look up '@@iterator' on Array.prototype, ensure it's a slotful shape.
48 Shape *iterShape = arrayProto->nativeLookup(cx, cx->names().std_iterator);
49 if (!iterShape || !iterShape->hasSlot() || !iterShape->hasDefaultGetter())
50 return true;
51
52 // Get the referred value, and ensure it holds the canonical ArrayValues function.
53 Value iterator = arrayProto->getSlot(iterShape->slot());
54 JSFunction *iterFun;
55 if (!IsFunctionObject(iterator, &iterFun))
56 return true;
57 if (!IsSelfHostedFunctionWithName(iterFun, cx->names().ArrayValues))
58 return true;
59
60 // Look up the 'next' value on ArrayIterator.prototype
61 Shape *nextShape = arrayIteratorProto->nativeLookup(cx, cx->names().next);
62 if (!nextShape || !nextShape->hasSlot())
63 return true;
64
65 // Get the referred value, ensure it holds the canonical ArrayIteratorNext function.
66 Value next = arrayIteratorProto->getSlot(nextShape->slot());
67 JSFunction *nextFun;
68 if (!IsFunctionObject(next, &nextFun))
69 return true;
70 if (!IsSelfHostedFunctionWithName(nextFun, cx->names().ArrayIteratorNext))
71 return true;
72
73 disabled_ = false;
74 arrayProtoShape_ = arrayProto->lastProperty();
75 arrayProtoIteratorSlot_ = iterShape->slot();
76 canonicalIteratorFunc_ = iterator;
77 arrayIteratorProtoShape_ = arrayIteratorProto->lastProperty();
78 arrayIteratorProtoNextSlot_ = nextShape->slot();
79 canonicalNextFunc_ = next;
80 return true;
81 }
82
83 js::ForOfPIC::Stub *
84 js::ForOfPIC::Chain::isArrayOptimized(ArrayObject *obj)
85 {
86 Stub *stub = getMatchingStub(obj);
87 if (!stub)
88 return nullptr;
89
90 // Ensure that this is an otherwise optimizable array.
91 if (!isOptimizableArray(obj))
92 return nullptr;
93
94 // Not yet enough! Ensure that the world as we know it remains sane.
95 if (!isArrayStateStillSane())
96 return nullptr;
97
98 return stub;
99 }
100
101 bool
102 js::ForOfPIC::Chain::tryOptimizeArray(JSContext *cx, HandleObject array, bool *optimized)
103 {
104 JS_ASSERT(array->is<ArrayObject>());
105 JS_ASSERT(optimized);
106
107 *optimized = false;
108
109 if (!initialized_) {
110 // If PIC is not initialized, initialize it.
111 if (!initialize(cx))
112 return false;
113
114 } else if (!disabled_ && !isArrayStateStillSane()) {
115 // Otherwise, if array state is no longer sane, reinitialize.
116 reset(cx);
117
118 if (!initialize(cx))
119 return false;
120 }
121 JS_ASSERT(initialized_);
122
123 // If PIC is disabled, don't bother trying to optimize.
124 if (disabled_)
125 return true;
126
127 // By the time we get here, we should have a sane array state to work with.
128 JS_ASSERT(isArrayStateStillSane());
129
130 // Check if stub already exists.
131 ForOfPIC::Stub *stub = isArrayOptimized(&array->as<ArrayObject>());
132 if (stub) {
133 *optimized = true;
134 return true;
135 }
136
137 // If the number of stubs is about to exceed the limit, throw away entire
138 // existing cache before adding new stubs. We shouldn't really have heavy
139 // churn on these.
140 if (numStubs() >= MAX_STUBS)
141 eraseChain();
142
143 // Ensure array's prototype is the actual Array.prototype
144 if (!isOptimizableArray(array))
145 return true;
146
147 // Ensure array doesn't define '@@iterator' directly.
148 if (array->nativeLookup(cx, cx->names().std_iterator))
149 return true;
150
151 // Good to optimize now, create stub to add.
152 RootedShape shape(cx, array->lastProperty());
153 stub = cx->new_<Stub>(shape);
154 if (!stub)
155 return false;
156
157 // Add the stub.
158 addStub(stub);
159
160 *optimized = true;
161 return true;
162 }
163
164 js::ForOfPIC::Stub *
165 js::ForOfPIC::Chain::getMatchingStub(JSObject *obj)
166 {
167 // Ensure PIC is initialized and not disabled.
168 if (!initialized_ || disabled_)
169 return nullptr;
170
171 // Check if there is a matching stub.
172 for (Stub *stub = stubs(); stub != nullptr; stub = stub->next()) {
173 if (stub->shape() == obj->lastProperty())
174 return stub;
175 }
176
177 return nullptr;
178 }
179
180 bool
181 js::ForOfPIC::Chain::isOptimizableArray(JSObject *obj)
182 {
183 JS_ASSERT(obj->is<ArrayObject>());
184
185 // Ensure object's prototype is the actual Array.prototype
186 if (!obj->getTaggedProto().isObject())
187 return false;
188 if (obj->getTaggedProto().toObject() != arrayProto_)
189 return false;
190
191 return true;
192 }
193
194 bool
195 js::ForOfPIC::Chain::isArrayStateStillSane()
196 {
197 // Ensure that canonical Array.prototype has matching shape.
198 if (arrayProto_->lastProperty() != arrayProtoShape_)
199 return false;
200
201 // Ensure that Array.prototype['@@iterator'] contains the
202 // canonical iterator function.
203 if (arrayProto_->getSlot(arrayProtoIteratorSlot_) != canonicalIteratorFunc_)
204 return false;
205
206 // Chain to isArrayNextStillSane.
207 return isArrayNextStillSane();
208 }
209
210 void
211 js::ForOfPIC::Chain::reset(JSContext *cx)
212 {
213 // Should never reset a disabled_ stub.
214 JS_ASSERT(!disabled_);
215
216 // Erase the chain.
217 eraseChain();
218
219 arrayProto_ = nullptr;
220 arrayIteratorProto_ = nullptr;
221
222 arrayProtoShape_ = nullptr;
223 arrayProtoIteratorSlot_ = -1;
224 canonicalIteratorFunc_ = UndefinedValue();
225
226 arrayIteratorProtoShape_ = nullptr;
227 arrayIteratorProtoNextSlot_ = -1;
228 canonicalNextFunc_ = UndefinedValue();
229
230 initialized_ = false;
231 }
232
233 void
234 js::ForOfPIC::Chain::eraseChain()
235 {
236 // Should never need to clear the chain of a disabled stub.
237 JS_ASSERT(!disabled_);
238
239 // Free all stubs.
240 Stub *stub = stubs_;
241 while (stub) {
242 Stub *next = stub->next();
243 js_delete(stub);
244 stub = next;
245 }
246 stubs_ = nullptr;
247 }
248
249
250 // Trace the pointers stored directly on the stub.
251 void
252 js::ForOfPIC::Chain::mark(JSTracer *trc)
253 {
254 if (!initialized_ || disabled_)
255 return;
256
257 gc::MarkObject(trc, &arrayProto_, "ForOfPIC Array.prototype.");
258 gc::MarkObject(trc, &arrayIteratorProto_, "ForOfPIC ArrayIterator.prototype.");
259
260 gc::MarkShape(trc, &arrayProtoShape_, "ForOfPIC Array.prototype shape.");
261 gc::MarkShape(trc, &arrayIteratorProtoShape_, "ForOfPIC ArrayIterator.prototype shape.");
262
263 gc::MarkValue(trc, &canonicalIteratorFunc_, "ForOfPIC ArrayValues builtin.");
264 gc::MarkValue(trc, &canonicalNextFunc_, "ForOfPIC ArrayIterator.prototype.next builtin.");
265
266 // Free all the stubs in the chain.
267 while (stubs_)
268 removeStub(stubs_, nullptr);
269 }
270
271 void
272 js::ForOfPIC::Chain::sweep(FreeOp *fop)
273 {
274 // Free all the stubs in the chain.
275 while (stubs_) {
276 Stub *next = stubs_->next();
277 fop->delete_(stubs_);
278 stubs_ = next;
279 }
280 fop->delete_(this);
281 }
282
283 static void
284 ForOfPIC_finalize(FreeOp *fop, JSObject *obj)
285 {
286 if (ForOfPIC::Chain *chain = ForOfPIC::fromJSObject(obj))
287 chain->sweep(fop);
288 }
289
290 static void
291 ForOfPIC_traceObject(JSTracer *trc, JSObject *obj)
292 {
293 if (ForOfPIC::Chain *chain = ForOfPIC::fromJSObject(obj))
294 chain->mark(trc);
295 }
296
297 const Class ForOfPIC::jsclass = {
298 "ForOfPIC", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
299 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
300 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ForOfPIC_finalize,
301 nullptr, /* call */
302 nullptr, /* hasInstance */
303 nullptr, /* construct */
304 ForOfPIC_traceObject
305 };
306
307 /* static */ JSObject *
308 js::ForOfPIC::createForOfPICObject(JSContext *cx, Handle<GlobalObject*> global)
309 {
310 assertSameCompartment(cx, global);
311 JSObject *obj = NewObjectWithGivenProto(cx, &ForOfPIC::jsclass, nullptr, global);
312 if (!obj)
313 return nullptr;
314 ForOfPIC::Chain *chain = cx->new_<ForOfPIC::Chain>();
315 if (!chain)
316 return nullptr;
317 obj->setPrivate(chain);
318 return obj;
319 }
320
321 /* static */ js::ForOfPIC::Chain *
322 js::ForOfPIC::create(JSContext *cx)
323 {
324 JS_ASSERT(!cx->global()->getForOfPICObject());
325 Rooted<GlobalObject *> global(cx, cx->global());
326 JSObject *obj = GlobalObject::getOrCreateForOfPICObject(cx, global);
327 if (!obj)
328 return nullptr;
329 return fromJSObject(obj);
330 }

mercurial