Backed out changeset 8f976ed899d7 (bug 1847231) for causing bc failures on browser_se...
[gecko.git] / js / src / vm / PIC.cpp
blob7ef83bd5a691871e0a4519efac744449206a2a08
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/. */
7 #include "vm/PIC.h"
9 #include "gc/GCContext.h"
10 #include "vm/GlobalObject.h"
11 #include "vm/JSContext.h"
12 #include "vm/JSObject.h"
13 #include "vm/Realm.h"
14 #include "vm/SelfHosting.h"
16 #include "gc/GCContext-inl.h"
17 #include "vm/JSContext-inl.h"
18 #include "vm/JSObject-inl.h"
20 using namespace js;
22 template <typename Category>
23 void PICChain<Category>::addStub(JSObject* obj, CatStub* stub) {
24 MOZ_ASSERT(stub);
25 MOZ_ASSERT(!stub->next());
27 AddCellMemory(obj, sizeof(CatStub), MemoryUse::ForOfPICStub);
29 if (!stubs_) {
30 stubs_ = stub;
31 return;
34 CatStub* cur = stubs_;
35 while (cur->next()) {
36 cur = cur->next();
38 cur->append(stub);
41 bool js::ForOfPIC::Chain::initialize(JSContext* cx) {
42 MOZ_ASSERT(!initialized_);
44 // Get the canonical Array.prototype
45 Rooted<NativeObject*> arrayProto(
46 cx, GlobalObject::getOrCreateArrayPrototype(cx, cx->global()));
47 if (!arrayProto) {
48 return false;
51 // Get the canonical ArrayIterator.prototype
52 Rooted<NativeObject*> arrayIteratorProto(
53 cx, GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global()));
54 if (!arrayIteratorProto) {
55 return false;
58 // From this point on, we can't fail. Set initialized and fill the fields
59 // for the canonical Array.prototype and ArrayIterator.prototype objects.
60 initialized_ = true;
61 arrayProto_ = arrayProto;
62 arrayIteratorProto_ = arrayIteratorProto;
64 // Shortcut returns below means Array for-of will never be optimizable,
65 // do set disabled_ now, and clear it later when we succeed.
66 disabled_ = true;
68 // Look up Array.prototype[@@iterator], ensure it's a slotful shape.
69 mozilla::Maybe<PropertyInfo> iterProp = arrayProto->lookup(
70 cx, PropertyKey::Symbol(cx->wellKnownSymbols().iterator));
71 if (iterProp.isNothing() || !iterProp->isDataProperty()) {
72 return true;
75 // Get the referred value, and ensure it holds the canonical ArrayValues
76 // function.
77 Value iterator = arrayProto->getSlot(iterProp->slot());
78 JSFunction* iterFun;
79 if (!IsFunctionObject(iterator, &iterFun)) {
80 return true;
82 if (!IsSelfHostedFunctionWithName(iterFun, cx->names().dollar_ArrayValues_)) {
83 return true;
86 // Look up the 'next' value on ArrayIterator.prototype
87 mozilla::Maybe<PropertyInfo> nextProp =
88 arrayIteratorProto->lookup(cx, cx->names().next);
89 if (nextProp.isNothing() || !nextProp->isDataProperty()) {
90 return true;
93 // Get the referred value, ensure it holds the canonical ArrayIteratorNext
94 // function.
95 Value next = arrayIteratorProto->getSlot(nextProp->slot());
96 JSFunction* nextFun;
97 if (!IsFunctionObject(next, &nextFun)) {
98 return true;
100 if (!IsSelfHostedFunctionWithName(nextFun, cx->names().ArrayIteratorNext)) {
101 return true;
104 disabled_ = false;
105 arrayProtoShape_ = arrayProto->shape();
106 arrayProtoIteratorSlot_ = iterProp->slot();
107 canonicalIteratorFunc_ = iterator;
108 arrayIteratorProtoShape_ = arrayIteratorProto->shape();
109 arrayIteratorProtoNextSlot_ = nextProp->slot();
110 canonicalNextFunc_ = next;
111 return true;
114 bool js::ForOfPIC::Chain::tryOptimizeArray(JSContext* cx, bool* optimized) {
115 MOZ_ASSERT(optimized);
117 *optimized = false;
119 if (!initialized_) {
120 // If PIC is not initialized, initialize it.
121 if (!initialize(cx)) {
122 return false;
124 } else if (!disabled_ && !isArrayStateStillSane()) {
125 // Otherwise, if array state is no longer sane, reinitialize.
126 reset(cx);
128 if (!initialize(cx)) {
129 return false;
132 MOZ_ASSERT(initialized_);
134 // If PIC is disabled, don't bother trying to optimize.
135 if (disabled_) {
136 return true;
139 // By the time we get here, we should have a sane array state to work with.
140 MOZ_ASSERT(isArrayStateStillSane());
142 *optimized = true;
143 return true;
146 bool js::ForOfPIC::Chain::tryOptimizeArray(JSContext* cx,
147 Handle<ArrayObject*> array,
148 bool* optimized) {
149 MOZ_ASSERT(optimized);
151 *optimized = false;
153 if (!initialized_) {
154 // If PIC is not initialized, initialize it.
155 if (!initialize(cx)) {
156 return false;
158 } else if (!disabled_ && !isArrayStateStillSane()) {
159 // Otherwise, if array state is no longer sane, reinitialize.
160 reset(cx);
162 if (!initialize(cx)) {
163 return false;
166 MOZ_ASSERT(initialized_);
168 // If PIC is disabled, don't bother trying to optimize.
169 if (disabled_) {
170 return true;
173 // By the time we get here, we should have a sane array state to work with.
174 MOZ_ASSERT(isArrayStateStillSane());
176 // Ensure array's prototype is the actual Array.prototype
177 if (array->staticPrototype() != arrayProto_) {
178 return true;
181 // Check if stub already exists.
182 if (hasMatchingStub(array)) {
183 *optimized = true;
184 return true;
187 // Ensure array doesn't define @@iterator directly.
188 if (array->lookup(cx, PropertyKey::Symbol(cx->wellKnownSymbols().iterator))) {
189 return true;
192 // If the number of stubs is about to exceed the limit, throw away entire
193 // existing cache before adding new stubs. We shouldn't really have heavy
194 // churn on these.
195 if (numStubs() >= MAX_STUBS) {
196 eraseChain(cx);
199 // Good to optimize now, create stub to add.
200 Rooted<Shape*> shape(cx, array->shape());
201 Stub* stub = cx->new_<Stub>(shape);
202 if (!stub) {
203 return false;
206 // Add the stub.
207 addStub(picObject_, stub);
209 *optimized = true;
210 return true;
213 bool js::ForOfPIC::Chain::tryOptimizeArrayIteratorNext(JSContext* cx,
214 bool* optimized) {
215 MOZ_ASSERT(optimized);
217 *optimized = false;
219 if (!initialized_) {
220 // If PIC is not initialized, initialize it.
221 if (!initialize(cx)) {
222 return false;
224 } else if (!disabled_ && !isArrayNextStillSane()) {
225 // Otherwise, if array iterator state is no longer sane, reinitialize.
226 reset(cx);
228 if (!initialize(cx)) {
229 return false;
232 MOZ_ASSERT(initialized_);
234 // If PIC is disabled, don't bother trying to optimize.
235 if (disabled_) {
236 return true;
239 // By the time we get here, we should have a sane iterator state to work with.
240 MOZ_ASSERT(isArrayNextStillSane());
242 *optimized = true;
243 return true;
246 bool js::ForOfPIC::Chain::hasMatchingStub(ArrayObject* obj) {
247 // Ensure PIC is initialized and not disabled.
248 MOZ_ASSERT(initialized_ && !disabled_);
250 // Check if there is a matching stub.
251 for (Stub* stub = stubs(); stub != nullptr; stub = stub->next()) {
252 if (stub->shape() == obj->shape()) {
253 return true;
257 return false;
260 bool js::ForOfPIC::Chain::isArrayStateStillSane() {
261 // Ensure that canonical Array.prototype has matching shape.
262 if (arrayProto_->shape() != arrayProtoShape_) {
263 return false;
266 // Ensure that Array.prototype[@@iterator] contains the
267 // canonical iterator function.
268 if (arrayProto_->getSlot(arrayProtoIteratorSlot_) != canonicalIteratorFunc_) {
269 return false;
272 // Chain to isArrayNextStillSane.
273 return isArrayNextStillSane();
276 void js::ForOfPIC::Chain::reset(JSContext* cx) {
277 // Should never reset a disabled_ stub.
278 MOZ_ASSERT(!disabled_);
280 // Erase the chain.
281 eraseChain(cx);
283 arrayProto_ = nullptr;
284 arrayIteratorProto_ = nullptr;
286 arrayProtoShape_ = nullptr;
287 arrayProtoIteratorSlot_ = -1;
288 canonicalIteratorFunc_ = UndefinedValue();
290 arrayIteratorProtoShape_ = nullptr;
291 arrayIteratorProtoNextSlot_ = -1;
292 canonicalNextFunc_ = UndefinedValue();
294 initialized_ = false;
297 void js::ForOfPIC::Chain::eraseChain(JSContext* cx) {
298 // Should never need to clear the chain of a disabled stub.
299 MOZ_ASSERT(!disabled_);
300 freeAllStubs(cx->gcContext());
303 // Trace the pointers stored directly on the stub.
304 void js::ForOfPIC::Chain::trace(JSTracer* trc) {
305 TraceEdge(trc, &picObject_, "ForOfPIC object");
307 if (!initialized_ || disabled_) {
308 return;
311 TraceEdge(trc, &arrayProto_, "ForOfPIC Array.prototype.");
312 TraceEdge(trc, &arrayIteratorProto_, "ForOfPIC ArrayIterator.prototype.");
314 TraceEdge(trc, &arrayProtoShape_, "ForOfPIC Array.prototype shape.");
315 TraceEdge(trc, &arrayIteratorProtoShape_,
316 "ForOfPIC ArrayIterator.prototype shape.");
318 TraceEdge(trc, &canonicalIteratorFunc_, "ForOfPIC ArrayValues builtin.");
319 TraceEdge(trc, &canonicalNextFunc_,
320 "ForOfPIC ArrayIterator.prototype.next builtin.");
322 for (Stub* stub = stubs_; stub; stub = stub->next()) {
323 stub->trace(trc);
327 void js::ForOfPIC::Stub::trace(JSTracer* trc) {
328 TraceEdge(trc, &shape_, "ForOfPIC::Stub::shape_");
331 static void ForOfPIC_finalize(JS::GCContext* gcx, JSObject* obj) {
332 if (ForOfPIC::Chain* chain =
333 ForOfPIC::fromJSObject(&obj->as<NativeObject>())) {
334 chain->finalize(gcx, obj);
338 void js::ForOfPIC::Chain::finalize(JS::GCContext* gcx, JSObject* obj) {
339 freeAllStubs(gcx);
340 gcx->delete_(obj, this, MemoryUse::ForOfPIC);
343 void js::ForOfPIC::Chain::freeAllStubs(JS::GCContext* gcx) {
344 Stub* stub = stubs_;
345 while (stub) {
346 Stub* next = stub->next();
347 gcx->delete_(picObject_, stub, MemoryUse::ForOfPICStub);
348 stub = next;
350 stubs_ = nullptr;
353 static void ForOfPIC_traceObject(JSTracer* trc, JSObject* obj) {
354 if (ForOfPIC::Chain* chain =
355 ForOfPIC::fromJSObject(&obj->as<NativeObject>())) {
356 chain->trace(trc);
360 static const JSClassOps ForOfPICClassOps = {
361 nullptr, // addProperty
362 nullptr, // delProperty
363 nullptr, // enumerate
364 nullptr, // newEnumerate
365 nullptr, // resolve
366 nullptr, // mayResolve
367 ForOfPIC_finalize, // finalize
368 nullptr, // call
369 nullptr, // construct
370 ForOfPIC_traceObject, // trace
373 const JSClass ForOfPICObject::class_ = {
374 "ForOfPIC",
375 JSCLASS_HAS_RESERVED_SLOTS(SlotCount) | JSCLASS_BACKGROUND_FINALIZE,
376 &ForOfPICClassOps};
378 /* static */
379 NativeObject* js::ForOfPIC::createForOfPICObject(JSContext* cx,
380 Handle<GlobalObject*> global) {
381 cx->check(global);
382 ForOfPICObject* obj =
383 NewTenuredObjectWithGivenProto<ForOfPICObject>(cx, nullptr);
384 if (!obj) {
385 return nullptr;
387 ForOfPIC::Chain* chain = cx->new_<ForOfPIC::Chain>(obj);
388 if (!chain) {
389 return nullptr;
391 InitReservedSlot(obj, ForOfPICObject::ChainSlot, chain, MemoryUse::ForOfPIC);
392 return obj;
395 /* static */ js::ForOfPIC::Chain* js::ForOfPIC::create(JSContext* cx) {
396 MOZ_ASSERT(!cx->global()->getForOfPICObject());
397 Rooted<GlobalObject*> global(cx, cx->global());
398 NativeObject* obj = GlobalObject::getOrCreateForOfPICObject(cx, global);
399 if (!obj) {
400 return nullptr;
402 return fromJSObject(obj);