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