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/. */
9 #include "gc/GCContext.h"
10 #include "vm/GlobalObject.h"
11 #include "vm/JSContext.h"
12 #include "vm/JSObject.h"
14 #include "vm/SelfHosting.h"
16 #include "gc/GCContext-inl.h"
17 #include "vm/JSContext-inl.h"
18 #include "vm/JSObject-inl.h"
22 template <typename Category
>
23 void PICChain
<Category
>::addStub(JSObject
* obj
, CatStub
* stub
) {
25 MOZ_ASSERT(!stub
->next());
27 AddCellMemory(obj
, sizeof(CatStub
), MemoryUse::ForOfPICStub
);
34 CatStub
* cur
= stubs_
;
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()));
51 // Get the canonical ArrayIterator.prototype
52 Rooted
<NativeObject
*> arrayIteratorProto(
53 cx
, GlobalObject::getOrCreateArrayIteratorPrototype(cx
, cx
->global()));
54 if (!arrayIteratorProto
) {
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.
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.
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()) {
75 // Get the referred value, and ensure it holds the canonical ArrayValues
77 Value iterator
= arrayProto
->getSlot(iterProp
->slot());
79 if (!IsFunctionObject(iterator
, &iterFun
)) {
82 if (!IsSelfHostedFunctionWithName(iterFun
, cx
->names().dollar_ArrayValues_
)) {
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()) {
93 // Get the referred value, ensure it holds the canonical ArrayIteratorNext
95 Value next
= arrayIteratorProto
->getSlot(nextProp
->slot());
97 if (!IsFunctionObject(next
, &nextFun
)) {
100 if (!IsSelfHostedFunctionWithName(nextFun
, cx
->names().ArrayIteratorNext
)) {
105 arrayProtoShape_
= arrayProto
->shape();
106 arrayProtoIteratorSlot_
= iterProp
->slot();
107 canonicalIteratorFunc_
= iterator
;
108 arrayIteratorProtoShape_
= arrayIteratorProto
->shape();
109 arrayIteratorProtoNextSlot_
= nextProp
->slot();
110 canonicalNextFunc_
= next
;
114 bool js::ForOfPIC::Chain::tryOptimizeArray(JSContext
* cx
, bool* optimized
) {
115 MOZ_ASSERT(optimized
);
120 // If PIC is not initialized, initialize it.
121 if (!initialize(cx
)) {
124 } else if (!disabled_
&& !isArrayStateStillSane()) {
125 // Otherwise, if array state is no longer sane, reinitialize.
128 if (!initialize(cx
)) {
132 MOZ_ASSERT(initialized_
);
134 // If PIC is disabled, don't bother trying to optimize.
139 // By the time we get here, we should have a sane array state to work with.
140 MOZ_ASSERT(isArrayStateStillSane());
146 bool js::ForOfPIC::Chain::tryOptimizeArray(JSContext
* cx
,
147 Handle
<ArrayObject
*> array
,
149 MOZ_ASSERT(optimized
);
154 // If PIC is not initialized, initialize it.
155 if (!initialize(cx
)) {
158 } else if (!disabled_
&& !isArrayStateStillSane()) {
159 // Otherwise, if array state is no longer sane, reinitialize.
162 if (!initialize(cx
)) {
166 MOZ_ASSERT(initialized_
);
168 // If PIC is disabled, don't bother trying to optimize.
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_
) {
181 // Check if stub already exists.
182 if (hasMatchingStub(array
)) {
187 // Ensure array doesn't define @@iterator directly.
188 if (array
->lookup(cx
, PropertyKey::Symbol(cx
->wellKnownSymbols().iterator
))) {
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
195 if (numStubs() >= MAX_STUBS
) {
199 // Good to optimize now, create stub to add.
200 Rooted
<Shape
*> shape(cx
, array
->shape());
201 Stub
* stub
= cx
->new_
<Stub
>(shape
);
207 addStub(picObject_
, stub
);
213 bool js::ForOfPIC::Chain::tryOptimizeArrayIteratorNext(JSContext
* cx
,
215 MOZ_ASSERT(optimized
);
220 // If PIC is not initialized, initialize it.
221 if (!initialize(cx
)) {
224 } else if (!disabled_
&& !isArrayNextStillSane()) {
225 // Otherwise, if array iterator state is no longer sane, reinitialize.
228 if (!initialize(cx
)) {
232 MOZ_ASSERT(initialized_
);
234 // If PIC is disabled, don't bother trying to optimize.
239 // By the time we get here, we should have a sane iterator state to work with.
240 MOZ_ASSERT(isArrayNextStillSane());
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()) {
260 bool js::ForOfPIC::Chain::isArrayStateStillSane() {
261 // Ensure that canonical Array.prototype has matching shape.
262 if (arrayProto_
->shape() != arrayProtoShape_
) {
266 // Ensure that Array.prototype[@@iterator] contains the
267 // canonical iterator function.
268 if (arrayProto_
->getSlot(arrayProtoIteratorSlot_
) != canonicalIteratorFunc_
) {
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_
);
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_
) {
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()) {
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
) {
340 gcx
->delete_(obj
, this, MemoryUse::ForOfPIC
);
343 void js::ForOfPIC::Chain::freeAllStubs(JS::GCContext
* gcx
) {
346 Stub
* next
= stub
->next();
347 gcx
->delete_(picObject_
, stub
, MemoryUse::ForOfPICStub
);
353 static void ForOfPIC_traceObject(JSTracer
* trc
, JSObject
* obj
) {
354 if (ForOfPIC::Chain
* chain
=
355 ForOfPIC::fromJSObject(&obj
->as
<NativeObject
>())) {
360 static const JSClassOps ForOfPICClassOps
= {
361 nullptr, // addProperty
362 nullptr, // delProperty
363 nullptr, // enumerate
364 nullptr, // newEnumerate
366 nullptr, // mayResolve
367 ForOfPIC_finalize
, // finalize
369 nullptr, // construct
370 ForOfPIC_traceObject
, // trace
373 const JSClass
ForOfPICObject::class_
= {
375 JSCLASS_HAS_RESERVED_SLOTS(SlotCount
) | JSCLASS_BACKGROUND_FINALIZE
,
379 NativeObject
* js::ForOfPIC::createForOfPICObject(JSContext
* cx
,
380 Handle
<GlobalObject
*> global
) {
382 ForOfPICObject
* obj
=
383 NewTenuredObjectWithGivenProto
<ForOfPICObject
>(cx
, nullptr);
387 ForOfPIC::Chain
* chain
= cx
->new_
<ForOfPIC::Chain
>(obj
);
391 InitReservedSlot(obj
, ForOfPICObject::ChainSlot
, chain
, MemoryUse::ForOfPIC
);
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
);
402 return fromJSObject(obj
);