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 // Get the canonical Iterator.prototype
59 Rooted
<NativeObject
*> iteratorProto(
60 cx
, MaybeNativeObject(
61 GlobalObject::getOrCreateIteratorPrototype(cx
, cx
->global())));
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.
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.
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()) {
89 // Get the referred value, and ensure it holds the canonical ArrayValues
91 Value iterator
= arrayProto
->getSlot(iterProp
->slot());
93 if (!IsFunctionObject(iterator
, &iterFun
)) {
96 if (!IsSelfHostedFunctionWithName(iterFun
, cx
->names().dollar_ArrayValues_
)) {
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()) {
107 // Get the referred value, ensure it holds the canonical ArrayIteratorNext
109 Value next
= arrayIteratorProto
->getSlot(nextProp
->slot());
111 if (!IsFunctionObject(next
, &nextFun
)) {
114 if (!IsSelfHostedFunctionWithName(nextFun
, cx
->names().ArrayIteratorNext
)) {
118 // Ensure ArrayIterator.prototype doesn't define a "return" property
119 if (arrayIteratorProto
->lookup(cx
, cx
->names().return_
).isSome()) {
123 // Ensure ArrayIterator.prototype's prototype is Iterator.prototype
124 if (arrayIteratorProto
->staticPrototype() != iteratorProto
) {
128 // Ensure Iterator.prototype doesn't define a "return" property
129 if (iteratorProto
->lookup(cx
, cx
->names().return_
).isSome()) {
133 // Ensure Iterator.prototype's prototype is Object.prototype
134 if (iteratorProto
->staticPrototype() != objectProto
) {
138 // Ensure Object.prototype doesn't define a "return" property
139 if (objectProto
->lookup(cx
, cx
->names().return_
).isSome()) {
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();
155 bool js::ForOfPIC::Chain::tryOptimizeArray(JSContext
* cx
, bool* optimized
) {
156 MOZ_ASSERT(optimized
);
161 // If PIC is not initialized, initialize it.
162 if (!initialize(cx
)) {
165 } else if (!disabled_
&& !isArrayStateStillSane()) {
166 // Otherwise, if array state is no longer sane, reinitialize.
169 if (!initialize(cx
)) {
173 MOZ_ASSERT(initialized_
);
175 // If PIC is disabled, don't bother trying to optimize.
180 // By the time we get here, we should have a sane array state to work with.
181 MOZ_ASSERT(isArrayStateStillSane());
187 bool js::ForOfPIC::Chain::tryOptimizeArray(JSContext
* cx
,
188 Handle
<ArrayObject
*> array
,
190 MOZ_ASSERT(optimized
);
195 // If PIC is not initialized, initialize it.
196 if (!initialize(cx
)) {
199 } else if (!disabled_
&& !isArrayStateStillSane()) {
200 // Otherwise, if array state is no longer sane, reinitialize.
203 if (!initialize(cx
)) {
207 MOZ_ASSERT(initialized_
);
209 // If PIC is disabled, don't bother trying to optimize.
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_
) {
222 // Check if stub already exists.
223 if (hasMatchingStub(array
)) {
228 // Ensure array doesn't define @@iterator directly.
229 if (array
->lookup(cx
, PropertyKey::Symbol(cx
->wellKnownSymbols().iterator
))) {
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
236 if (numStubs() >= MAX_STUBS
) {
240 // Good to optimize now, create stub to add.
241 Rooted
<Shape
*> shape(cx
, array
->shape());
242 Stub
* stub
= cx
->new_
<Stub
>(shape
);
248 addStub(picObject_
, stub
);
254 bool js::ForOfPIC::Chain::tryOptimizeArrayIteratorNext(JSContext
* cx
,
256 MOZ_ASSERT(optimized
);
261 // If PIC is not initialized, initialize it.
262 if (!initialize(cx
)) {
265 } else if (!disabled_
&& !isArrayIteratorStateStillSane()) {
266 // Otherwise, if array iterator state is no longer sane, reinitialize.
269 if (!initialize(cx
)) {
273 MOZ_ASSERT(initialized_
);
275 // If PIC is disabled, don't bother trying to optimize.
280 // By the time we get here, we should have a sane iterator state to work with.
281 MOZ_ASSERT(isArrayIteratorStateStillSane());
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()) {
301 bool js::ForOfPIC::Chain::isArrayStateStillSane() {
302 // Ensure that canonical Array.prototype has matching shape.
303 if (arrayProto_
->shape() != arrayProtoShape_
) {
307 // Ensure that Array.prototype[@@iterator] contains the
308 // canonical iterator function.
309 if (arrayProto_
->getSlot(arrayProtoIteratorSlot_
) != canonicalIteratorFunc_
) {
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_
);
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_
) {
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()) {
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
) {
390 gcx
->delete_(obj
, this, MemoryUse::ForOfPIC
);
393 void js::ForOfPIC::Chain::freeAllStubs(JS::GCContext
* gcx
) {
396 Stub
* next
= stub
->next();
397 gcx
->delete_(picObject_
, stub
, MemoryUse::ForOfPICStub
);
403 static void ForOfPIC_traceObject(JSTracer
* trc
, JSObject
* obj
) {
404 if (ForOfPIC::Chain
* chain
=
405 ForOfPIC::fromJSObject(&obj
->as
<NativeObject
>())) {
410 static const JSClassOps ForOfPICClassOps
= {
411 nullptr, // addProperty
412 nullptr, // delProperty
413 nullptr, // enumerate
414 nullptr, // newEnumerate
416 nullptr, // mayResolve
417 ForOfPIC_finalize
, // finalize
419 nullptr, // construct
420 ForOfPIC_traceObject
, // trace
423 const JSClass
ForOfPICObject::class_
= {
425 JSCLASS_HAS_RESERVED_SLOTS(SlotCount
) | JSCLASS_BACKGROUND_FINALIZE
,
429 NativeObject
* js::ForOfPIC::createForOfPICObject(JSContext
* cx
,
430 Handle
<GlobalObject
*> global
) {
432 ForOfPICObject
* obj
=
433 NewTenuredObjectWithGivenProto
<ForOfPICObject
>(cx
, nullptr);
437 ForOfPIC::Chain
* chain
= cx
->new_
<ForOfPIC::Chain
>(obj
);
441 InitReservedSlot(obj
, ForOfPICObject::ChainSlot
, chain
, MemoryUse::ForOfPIC
);
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
);
452 return fromJSObject(obj
);