Bug 1885337 - Part 1: Implement to/from hex methods. r=dminor
[gecko.git] / js / src / vm / PIC.cpp
blobb43a46a2094a18e524f8a8b7867f273dcf732dd2
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 // Get the canonical Iterator.prototype
59 Rooted<NativeObject*> iteratorProto(
60 cx, MaybeNativeObject(
61 GlobalObject::getOrCreateIteratorPrototype(cx, cx->global())));
62 if (!iteratorProto) {
63 return false;
66 Rooted<NativeObject*> objectProto(
67 cx, MaybeNativeObject(&cx->global()->getObjectPrototype()));
68 MOZ_ASSERT(objectProto);
70 // From this point on, we can't fail. Set initialized and fill the fields
71 // for the canonical Array.prototype and ArrayIterator.prototype objects.
72 initialized_ = true;
73 arrayProto_ = arrayProto;
74 arrayIteratorProto_ = arrayIteratorProto;
75 iteratorProto_ = iteratorProto;
76 objectProto_ = objectProto;
78 // Shortcut returns below means Array for-of will never be optimizable,
79 // do set disabled_ now, and clear it later when we succeed.
80 disabled_ = true;
82 // Look up Array.prototype[@@iterator], ensure it's a slotful shape.
83 mozilla::Maybe<PropertyInfo> iterProp = arrayProto->lookup(
84 cx, PropertyKey::Symbol(cx->wellKnownSymbols().iterator));
85 if (iterProp.isNothing() || !iterProp->isDataProperty()) {
86 return true;
89 // Get the referred value, and ensure it holds the canonical ArrayValues
90 // function.
91 Value iterator = arrayProto->getSlot(iterProp->slot());
92 JSFunction* iterFun;
93 if (!IsFunctionObject(iterator, &iterFun)) {
94 return true;
96 if (!IsSelfHostedFunctionWithName(iterFun, cx->names().dollar_ArrayValues_)) {
97 return true;
100 // Look up the 'next' value on ArrayIterator.prototype
101 mozilla::Maybe<PropertyInfo> nextProp =
102 arrayIteratorProto->lookup(cx, cx->names().next);
103 if (nextProp.isNothing() || !nextProp->isDataProperty()) {
104 return true;
107 // Get the referred value, ensure it holds the canonical ArrayIteratorNext
108 // function.
109 Value next = arrayIteratorProto->getSlot(nextProp->slot());
110 JSFunction* nextFun;
111 if (!IsFunctionObject(next, &nextFun)) {
112 return true;
114 if (!IsSelfHostedFunctionWithName(nextFun, cx->names().ArrayIteratorNext)) {
115 return true;
118 // Ensure ArrayIterator.prototype doesn't define a "return" property
119 if (arrayIteratorProto->lookup(cx, cx->names().return_).isSome()) {
120 return true;
123 // Ensure ArrayIterator.prototype's prototype is Iterator.prototype
124 if (arrayIteratorProto->staticPrototype() != iteratorProto) {
125 return true;
128 // Ensure Iterator.prototype doesn't define a "return" property
129 if (iteratorProto->lookup(cx, cx->names().return_).isSome()) {
130 return true;
133 // Ensure Iterator.prototype's prototype is Object.prototype
134 if (iteratorProto->staticPrototype() != objectProto) {
135 return true;
138 // Ensure Object.prototype doesn't define a "return" property
139 if (objectProto->lookup(cx, cx->names().return_).isSome()) {
140 return true;
143 disabled_ = false;
144 arrayProtoShape_ = arrayProto->shape();
145 arrayProtoIteratorSlot_ = iterProp->slot();
146 canonicalIteratorFunc_ = iterator;
147 arrayIteratorProtoShape_ = arrayIteratorProto->shape();
148 arrayIteratorProtoNextSlot_ = nextProp->slot();
149 canonicalNextFunc_ = next;
150 iteratorProtoShape_ = iteratorProto->shape();
151 objectProtoShape_ = objectProto->shape();
152 return true;
155 bool js::ForOfPIC::Chain::tryOptimizeArray(JSContext* cx, bool* optimized) {
156 MOZ_ASSERT(optimized);
158 *optimized = false;
160 if (!initialized_) {
161 // If PIC is not initialized, initialize it.
162 if (!initialize(cx)) {
163 return false;
165 } else if (!disabled_ && !isArrayStateStillSane()) {
166 // Otherwise, if array state is no longer sane, reinitialize.
167 reset(cx);
169 if (!initialize(cx)) {
170 return false;
173 MOZ_ASSERT(initialized_);
175 // If PIC is disabled, don't bother trying to optimize.
176 if (disabled_) {
177 return true;
180 // By the time we get here, we should have a sane array state to work with.
181 MOZ_ASSERT(isArrayStateStillSane());
183 *optimized = true;
184 return true;
187 bool js::ForOfPIC::Chain::tryOptimizeArray(JSContext* cx,
188 Handle<ArrayObject*> array,
189 bool* optimized) {
190 MOZ_ASSERT(optimized);
192 *optimized = false;
194 if (!initialized_) {
195 // If PIC is not initialized, initialize it.
196 if (!initialize(cx)) {
197 return false;
199 } else if (!disabled_ && !isArrayStateStillSane()) {
200 // Otherwise, if array state is no longer sane, reinitialize.
201 reset(cx);
203 if (!initialize(cx)) {
204 return false;
207 MOZ_ASSERT(initialized_);
209 // If PIC is disabled, don't bother trying to optimize.
210 if (disabled_) {
211 return true;
214 // By the time we get here, we should have a sane array state to work with.
215 MOZ_ASSERT(isArrayStateStillSane());
217 // Ensure array's prototype is the actual Array.prototype
218 if (array->staticPrototype() != arrayProto_) {
219 return true;
222 // Check if stub already exists.
223 if (hasMatchingStub(array)) {
224 *optimized = true;
225 return true;
228 // Ensure array doesn't define @@iterator directly.
229 if (array->lookup(cx, PropertyKey::Symbol(cx->wellKnownSymbols().iterator))) {
230 return true;
233 // If the number of stubs is about to exceed the limit, throw away entire
234 // existing cache before adding new stubs. We shouldn't really have heavy
235 // churn on these.
236 if (numStubs() >= MAX_STUBS) {
237 eraseChain(cx);
240 // Good to optimize now, create stub to add.
241 Rooted<Shape*> shape(cx, array->shape());
242 Stub* stub = cx->new_<Stub>(shape);
243 if (!stub) {
244 return false;
247 // Add the stub.
248 addStub(picObject_, stub);
250 *optimized = true;
251 return true;
254 bool js::ForOfPIC::Chain::tryOptimizeArrayIteratorNext(JSContext* cx,
255 bool* optimized) {
256 MOZ_ASSERT(optimized);
258 *optimized = false;
260 if (!initialized_) {
261 // If PIC is not initialized, initialize it.
262 if (!initialize(cx)) {
263 return false;
265 } else if (!disabled_ && !isArrayIteratorStateStillSane()) {
266 // Otherwise, if array iterator state is no longer sane, reinitialize.
267 reset(cx);
269 if (!initialize(cx)) {
270 return false;
273 MOZ_ASSERT(initialized_);
275 // If PIC is disabled, don't bother trying to optimize.
276 if (disabled_) {
277 return true;
280 // By the time we get here, we should have a sane iterator state to work with.
281 MOZ_ASSERT(isArrayIteratorStateStillSane());
283 *optimized = true;
284 return true;
287 bool js::ForOfPIC::Chain::hasMatchingStub(ArrayObject* obj) {
288 // Ensure PIC is initialized and not disabled.
289 MOZ_ASSERT(initialized_ && !disabled_);
291 // Check if there is a matching stub.
292 for (Stub* stub = stubs(); stub != nullptr; stub = stub->next()) {
293 if (stub->shape() == obj->shape()) {
294 return true;
298 return false;
301 bool js::ForOfPIC::Chain::isArrayStateStillSane() {
302 // Ensure that canonical Array.prototype has matching shape.
303 if (arrayProto_->shape() != arrayProtoShape_) {
304 return false;
307 // Ensure that Array.prototype[@@iterator] contains the
308 // canonical iterator function.
309 if (arrayProto_->getSlot(arrayProtoIteratorSlot_) != canonicalIteratorFunc_) {
310 return false;
313 // Chain to isArrayIteratorStateStillSane.
314 return isArrayIteratorStateStillSane();
317 void js::ForOfPIC::Chain::reset(JSContext* cx) {
318 // Should never reset a disabled_ stub.
319 MOZ_ASSERT(!disabled_);
321 // Erase the chain.
322 eraseChain(cx);
324 arrayProto_ = nullptr;
325 arrayIteratorProto_ = nullptr;
326 iteratorProto_ = nullptr;
327 objectProto_ = nullptr;
329 arrayProtoShape_ = nullptr;
330 arrayProtoIteratorSlot_ = -1;
331 canonicalIteratorFunc_ = UndefinedValue();
333 arrayIteratorProtoShape_ = nullptr;
334 arrayIteratorProtoNextSlot_ = -1;
335 canonicalNextFunc_ = UndefinedValue();
337 iteratorProtoShape_ = nullptr;
338 objectProtoShape_ = nullptr;
340 initialized_ = false;
343 void js::ForOfPIC::Chain::eraseChain(JSContext* cx) {
344 // Should never need to clear the chain of a disabled stub.
345 MOZ_ASSERT(!disabled_);
346 freeAllStubs(cx->gcContext());
349 // Trace the pointers stored directly on the stub.
350 void js::ForOfPIC::Chain::trace(JSTracer* trc) {
351 TraceEdge(trc, &picObject_, "ForOfPIC object");
353 if (!initialized_ || disabled_) {
354 return;
357 TraceEdge(trc, &arrayProto_, "ForOfPIC Array.prototype.");
358 TraceEdge(trc, &arrayIteratorProto_, "ForOfPIC ArrayIterator.prototype.");
359 TraceEdge(trc, &iteratorProto_, "ForOfPIC Iterator.prototype.");
360 TraceEdge(trc, &objectProto_, "ForOfPIC Object.prototype.");
362 TraceEdge(trc, &arrayProtoShape_, "ForOfPIC Array.prototype shape.");
363 TraceEdge(trc, &arrayIteratorProtoShape_,
364 "ForOfPIC ArrayIterator.prototype shape.");
365 TraceEdge(trc, &iteratorProtoShape_, "ForOfPIC Iterator.prototype shape.");
366 TraceEdge(trc, &objectProtoShape_, "ForOfPIC Object.prototype shape.");
368 TraceEdge(trc, &canonicalIteratorFunc_, "ForOfPIC ArrayValues builtin.");
369 TraceEdge(trc, &canonicalNextFunc_,
370 "ForOfPIC ArrayIterator.prototype.next builtin.");
372 for (Stub* stub = stubs_; stub; stub = stub->next()) {
373 stub->trace(trc);
377 void js::ForOfPIC::Stub::trace(JSTracer* trc) {
378 TraceEdge(trc, &shape_, "ForOfPIC::Stub::shape_");
381 static void ForOfPIC_finalize(JS::GCContext* gcx, JSObject* obj) {
382 if (ForOfPIC::Chain* chain =
383 ForOfPIC::fromJSObject(&obj->as<NativeObject>())) {
384 chain->finalize(gcx, obj);
388 void js::ForOfPIC::Chain::finalize(JS::GCContext* gcx, JSObject* obj) {
389 freeAllStubs(gcx);
390 gcx->delete_(obj, this, MemoryUse::ForOfPIC);
393 void js::ForOfPIC::Chain::freeAllStubs(JS::GCContext* gcx) {
394 Stub* stub = stubs_;
395 while (stub) {
396 Stub* next = stub->next();
397 gcx->delete_(picObject_, stub, MemoryUse::ForOfPICStub);
398 stub = next;
400 stubs_ = nullptr;
403 static void ForOfPIC_traceObject(JSTracer* trc, JSObject* obj) {
404 if (ForOfPIC::Chain* chain =
405 ForOfPIC::fromJSObject(&obj->as<NativeObject>())) {
406 chain->trace(trc);
410 static const JSClassOps ForOfPICClassOps = {
411 nullptr, // addProperty
412 nullptr, // delProperty
413 nullptr, // enumerate
414 nullptr, // newEnumerate
415 nullptr, // resolve
416 nullptr, // mayResolve
417 ForOfPIC_finalize, // finalize
418 nullptr, // call
419 nullptr, // construct
420 ForOfPIC_traceObject, // trace
423 const JSClass ForOfPICObject::class_ = {
424 "ForOfPIC",
425 JSCLASS_HAS_RESERVED_SLOTS(SlotCount) | JSCLASS_BACKGROUND_FINALIZE,
426 &ForOfPICClassOps};
428 /* static */
429 NativeObject* js::ForOfPIC::createForOfPICObject(JSContext* cx,
430 Handle<GlobalObject*> global) {
431 cx->check(global);
432 ForOfPICObject* obj =
433 NewTenuredObjectWithGivenProto<ForOfPICObject>(cx, nullptr);
434 if (!obj) {
435 return nullptr;
437 ForOfPIC::Chain* chain = cx->new_<ForOfPIC::Chain>(obj);
438 if (!chain) {
439 return nullptr;
441 InitReservedSlot(obj, ForOfPICObject::ChainSlot, chain, MemoryUse::ForOfPIC);
442 return obj;
445 /* static */ js::ForOfPIC::Chain* js::ForOfPIC::create(JSContext* cx) {
446 MOZ_ASSERT(!cx->global()->getForOfPICObject());
447 Rooted<GlobalObject*> global(cx, cx->global());
448 NativeObject* obj = GlobalObject::getOrCreateForOfPICObject(cx, global);
449 if (!obj) {
450 return nullptr;
452 return fromJSObject(obj);