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/. */
6 
7 #include "vm/Shape-inl.h"
8 
9 #include "mozilla/MathAlgorithms.h"
10 #include "mozilla/PodOperations.h"
11 
12 #include "gc/FreeOp.h"
13 #include "gc/HashUtil.h"
14 #include "gc/Policy.h"
15 #include "gc/PublicIterators.h"
16 #include "js/friend/WindowProxy.h"  // js::IsWindow
17 #include "js/HashTable.h"
18 #include "js/UniquePtr.h"
19 #include "util/Text.h"
20 #include "vm/JSAtom.h"
21 #include "vm/JSContext.h"
22 #include "vm/JSObject.h"
23 #include "vm/ShapeZone.h"
24 
25 #include "vm/Caches-inl.h"
26 #include "vm/JSContext-inl.h"
27 #include "vm/JSObject-inl.h"
28 #include "vm/NativeObject-inl.h"
29 #include "vm/ObjectFlags-inl.h"
30 #include "vm/Realm-inl.h"
31 
32 using namespace js;
33 
34 using mozilla::CeilingLog2Size;
35 using mozilla::PodZero;
36 
37 using JS::AutoCheckCannotGC;
38 
39 /* static */
replaceShape(JSContext * cx,HandleObject obj,ObjectFlags objectFlags,TaggedProto proto,uint32_t nfixed)40 bool Shape::replaceShape(JSContext* cx, HandleObject obj,
41                          ObjectFlags objectFlags, TaggedProto proto,
42                          uint32_t nfixed) {
43   MOZ_ASSERT(!obj->shape()->isDictionary());
44 
45   Shape* newShape;
46   if (obj->shape()->propMap()) {
47     Rooted<BaseShape*> base(cx, obj->shape()->base());
48     if (proto != base->proto()) {
49       Rooted<TaggedProto> protoRoot(cx, proto);
50       base = BaseShape::get(cx, base->clasp(), base->realm(), protoRoot);
51       if (!base) {
52         return false;
53       }
54     }
55     Rooted<SharedPropMap*> map(cx, obj->shape()->sharedPropMap());
56     uint32_t mapLength = obj->shape()->propMapLength();
57     newShape = SharedShape::getPropMapShape(cx, base, nfixed, map, mapLength,
58                                             objectFlags);
59   } else {
60     newShape = SharedShape::getInitialShape(cx, obj->shape()->getObjectClass(),
61                                             obj->shape()->realm(), proto,
62                                             nfixed, objectFlags);
63   }
64   if (!newShape) {
65     return false;
66   }
67 
68   obj->setShape(newShape);
69   return true;
70 }
71 
72 /* static */
toDictionaryMode(JSContext * cx,HandleNativeObject obj)73 bool js::NativeObject::toDictionaryMode(JSContext* cx, HandleNativeObject obj) {
74   MOZ_ASSERT(!obj->inDictionaryMode());
75   MOZ_ASSERT(cx->isInsideCurrentCompartment(obj));
76 
77   RootedShape shape(cx, obj->shape());
78   uint32_t span = obj->slotSpan();
79 
80   uint32_t mapLength = shape->propMapLength();
81   MOZ_ASSERT(mapLength > 0, "shouldn't convert empty object to dictionary");
82 
83   // Clone the shared property map to an unshared dictionary map.
84   Rooted<SharedPropMap*> map(cx, shape->propMap()->asShared());
85   Rooted<DictionaryPropMap*> dictMap(
86       cx, SharedPropMap::toDictionaryMap(cx, map, mapLength));
87   if (!dictMap) {
88     return false;
89   }
90 
91   // Allocate and use a new dictionary shape.
92   Rooted<BaseShape*> base(cx, shape->base());
93   shape = DictionaryShape::new_(cx, base, shape->objectFlags(),
94                                 shape->numFixedSlots(), dictMap, mapLength);
95   if (!shape) {
96     return false;
97   }
98   obj->setShape(shape);
99 
100   MOZ_ASSERT(obj->inDictionaryMode());
101   obj->setDictionaryModeSlotSpan(span);
102 
103   return true;
104 }
105 
106 namespace js {
107 
108 class MOZ_RAII AutoCheckShapeConsistency {
109 #ifdef DEBUG
110   HandleNativeObject obj_;
111 #endif
112 
113  public:
AutoCheckShapeConsistency(HandleNativeObject obj)114   explicit AutoCheckShapeConsistency(HandleNativeObject obj)
115 #ifdef DEBUG
116       : obj_(obj)
117 #endif
118   {
119   }
120 
121 #ifdef DEBUG
~AutoCheckShapeConsistency()122   ~AutoCheckShapeConsistency() { obj_->checkShapeConsistency(); }
123 #endif
124 };
125 
126 }  // namespace js
127 
128 /* static */ MOZ_ALWAYS_INLINE bool
maybeConvertToDictionaryForAdd(JSContext * cx,HandleNativeObject obj)129 NativeObject::maybeConvertToDictionaryForAdd(JSContext* cx,
130                                              HandleNativeObject obj) {
131   if (obj->inDictionaryMode()) {
132     return true;
133   }
134   SharedPropMap* map = obj->shape()->sharedPropMap();
135   if (!map) {
136     return true;
137   }
138   if (MOZ_LIKELY(!map->shouldConvertToDictionaryForAdd())) {
139     return true;
140   }
141   return toDictionaryMode(cx, obj);
142 }
143 
AssertValidCustomDataProp(NativeObject * obj,PropertyFlags flags)144 static void AssertValidCustomDataProp(NativeObject* obj, PropertyFlags flags) {
145   // We only support custom data properties on ArrayObject and ArgumentsObject.
146   // The mechanism is deprecated so we don't want to add new uses.
147   MOZ_ASSERT(flags.isCustomDataProperty());
148   MOZ_ASSERT(!flags.isAccessorProperty());
149   MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<ArgumentsObject>());
150 }
151 
152 /* static */
addCustomDataProperty(JSContext * cx,HandleNativeObject obj,HandleId id,PropertyFlags flags)153 bool NativeObject::addCustomDataProperty(JSContext* cx, HandleNativeObject obj,
154                                          HandleId id, PropertyFlags flags) {
155   MOZ_ASSERT(!JSID_IS_VOID(id));
156   MOZ_ASSERT(!id.isPrivateName());
157   MOZ_ASSERT(!obj->containsPure(id));
158 
159   AutoCheckShapeConsistency check(obj);
160   AssertValidCustomDataProp(obj, flags);
161 
162   if (!maybeConvertToDictionaryForAdd(cx, obj)) {
163     return false;
164   }
165 
166   ObjectFlags objectFlags = obj->shape()->objectFlags();
167   const JSClass* clasp = obj->shape()->getObjectClass();
168 
169   if (obj->inDictionaryMode()) {
170     // First generate a new dictionary shape so that the map can be mutated
171     // without having to worry about OOM conditions.
172     if (!NativeObject::generateNewDictionaryShape(cx, obj)) {
173       return false;
174     }
175 
176     Rooted<DictionaryPropMap*> map(cx, obj->shape()->dictionaryPropMap());
177     uint32_t mapLength = obj->shape()->propMapLength();
178     if (!DictionaryPropMap::addProperty(cx, clasp, &map, &mapLength, id, flags,
179                                         SHAPE_INVALID_SLOT, &objectFlags)) {
180       return false;
181     }
182 
183     obj->shape()->updateNewDictionaryShape(objectFlags, map, mapLength);
184     return true;
185   }
186 
187   Rooted<SharedPropMap*> map(cx, obj->shape()->sharedPropMap());
188   uint32_t mapLength = obj->shape()->propMapLength();
189   if (!SharedPropMap::addCustomDataProperty(cx, clasp, &map, &mapLength, id,
190                                             flags, &objectFlags)) {
191     return false;
192   }
193 
194   Shape* shape = SharedShape::getPropMapShape(cx, obj->shape()->base(),
195                                               obj->shape()->numFixedSlots(),
196                                               map, mapLength, objectFlags);
197   if (!shape) {
198     return false;
199   }
200 
201   obj->setShape(shape);
202   return true;
203 }
204 
MakeShapeSetForAdd(Shape * shape1,Shape * shape2)205 static ShapeSetForAdd* MakeShapeSetForAdd(Shape* shape1, Shape* shape2) {
206   MOZ_ASSERT(shape1 != shape2);
207   MOZ_ASSERT(shape1->propMapLength() == shape2->propMapLength());
208 
209   auto hash = MakeUnique<ShapeSetForAdd>();
210   if (!hash || !hash->reserve(2)) {
211     return nullptr;
212   }
213 
214   PropertyInfoWithKey prop = shape1->lastProperty();
215   hash->putNewInfallible(ShapeForAddHasher::Lookup(prop.key(), prop.flags()),
216                          shape1);
217 
218   prop = shape2->lastProperty();
219   hash->putNewInfallible(ShapeForAddHasher::Lookup(prop.key(), prop.flags()),
220                          shape2);
221 
222   return hash.release();
223 }
224 
LookupShapeForAdd(Shape * shape,PropertyKey key,PropertyFlags flags,uint32_t * slot)225 static MOZ_ALWAYS_INLINE Shape* LookupShapeForAdd(Shape* shape, PropertyKey key,
226                                                   PropertyFlags flags,
227                                                   uint32_t* slot) {
228   ShapeCachePtr cache = shape->cache();
229 
230   if (cache.isSingleShapeForAdd()) {
231     Shape* newShape = cache.toSingleShapeForAdd();
232     if (newShape->lastPropertyMatchesForAdd(key, flags, slot)) {
233       return newShape;
234     }
235     return nullptr;
236   }
237 
238   if (cache.isShapeSetForAdd()) {
239     ShapeSetForAdd* set = cache.toShapeSetForAdd();
240     ShapeForAddHasher::Lookup lookup(key, flags);
241     if (auto p = set->lookup(lookup)) {
242       Shape* newShape = *p;
243       *slot = newShape->lastProperty().slot();
244       return newShape;
245     }
246     return nullptr;
247   }
248 
249   MOZ_ASSERT(cache.isNone() || cache.isShapeWithProto());
250   return nullptr;
251 }
252 
253 // Add shapes with a non-None ShapeCachePtr to the shapesWithCache list so that
254 // these caches can be discarded on GC.
RegisterShapeCache(JSContext * cx,Shape * shape)255 static bool RegisterShapeCache(JSContext* cx, Shape* shape) {
256   ShapeCachePtr cache = shape->cache();
257   if (!cache.isNone()) {
258     // Already registered this shape.
259     return true;
260   }
261   return cx->zone()->shapeZone().shapesWithCache.append(shape);
262 }
263 
264 /* static */
addProperty(JSContext * cx,HandleNativeObject obj,HandleId id,PropertyFlags flags,uint32_t * slot)265 bool NativeObject::addProperty(JSContext* cx, HandleNativeObject obj,
266                                HandleId id, PropertyFlags flags,
267                                uint32_t* slot) {
268   AutoCheckShapeConsistency check(obj);
269   MOZ_ASSERT(!flags.isCustomDataProperty(),
270              "Use addCustomDataProperty for custom data properties");
271 
272   // The object must not contain a property named |id|. The object must be
273   // extensible, but allow private fields and sparsifying dense elements.
274   MOZ_ASSERT(!JSID_IS_VOID(id));
275   MOZ_ASSERT(!obj->containsPure(id));
276   MOZ_ASSERT_IF(
277       !id.isPrivateName(),
278       obj->isExtensible() ||
279           (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))));
280 
281   if (!maybeConvertToDictionaryForAdd(cx, obj)) {
282     return false;
283   }
284 
285   if (Shape* shape = LookupShapeForAdd(obj->shape(), id, flags, slot)) {
286     return obj->setShapeAndUpdateSlotsForNewSlot(cx, shape, *slot);
287   }
288 
289   if (obj->inDictionaryMode()) {
290     // First generate a new dictionary shape so that the map and shape can be
291     // mutated without having to worry about OOM conditions.
292     if (!NativeObject::generateNewDictionaryShape(cx, obj)) {
293       return false;
294     }
295     if (!allocDictionarySlot(cx, obj, slot)) {
296       return false;
297     }
298 
299     ObjectFlags objectFlags = obj->shape()->objectFlags();
300     const JSClass* clasp = obj->shape()->getObjectClass();
301 
302     Rooted<DictionaryPropMap*> map(cx, obj->shape()->propMap()->asDictionary());
303     uint32_t mapLength = obj->shape()->propMapLength();
304     if (!DictionaryPropMap::addProperty(cx, clasp, &map, &mapLength, id, flags,
305                                         *slot, &objectFlags)) {
306       return false;
307     }
308 
309     obj->shape()->updateNewDictionaryShape(objectFlags, map, mapLength);
310     return true;
311   }
312 
313   ObjectFlags objectFlags = obj->shape()->objectFlags();
314   const JSClass* clasp = obj->shape()->getObjectClass();
315 
316   Rooted<SharedPropMap*> map(cx, obj->shape()->sharedPropMap());
317   uint32_t mapLength = obj->shape()->propMapLength();
318 
319   if (!SharedPropMap::addProperty(cx, clasp, &map, &mapLength, id, flags,
320                                   &objectFlags, slot)) {
321     return false;
322   }
323 
324   bool allocatedNewShape;
325   Shape* newShape = SharedShape::getPropMapShape(
326       cx, obj->shape()->base(), obj->shape()->numFixedSlots(), map, mapLength,
327       objectFlags, &allocatedNewShape);
328   if (!newShape) {
329     return false;
330   }
331 
332   Shape* oldShape = obj->shape();
333   if (!obj->setShapeAndUpdateSlotsForNewSlot(cx, newShape, *slot)) {
334     return false;
335   }
336 
337   // Add the new shape to the old shape's shape cache, to optimize this shape
338   // transition. Don't do this if we just allocated a new shape, because that
339   // suggests this may not be a hot transition that would benefit from the
340   // cache.
341 
342   if (allocatedNewShape) {
343     return true;
344   }
345 
346   if (!RegisterShapeCache(cx, oldShape)) {
347     // Ignore OOM, the cache is just an optimization.
348     return true;
349   }
350 
351   ShapeCachePtr& cache = oldShape->cacheRef();
352   if (cache.isNone() || cache.isShapeWithProto()) {
353     cache.setSingleShapeForAdd(newShape);
354   } else if (cache.isSingleShapeForAdd()) {
355     Shape* prevShape = cache.toSingleShapeForAdd();
356     if (ShapeSetForAdd* set = MakeShapeSetForAdd(prevShape, newShape)) {
357       cache.setShapeSetForAdd(set);
358       AddCellMemory(oldShape, sizeof(ShapeSetForAdd),
359                     MemoryUse::ShapeSetForAdd);
360     }
361   } else {
362     ShapeForAddHasher::Lookup lookup(id, flags);
363     (void)cache.toShapeSetForAdd()->putNew(lookup, newShape);
364   }
365 
366   return true;
367 }
368 
369 /* static */
addPropertyInReservedSlot(JSContext * cx,HandleNativeObject obj,HandleId id,uint32_t slot,PropertyFlags flags)370 bool NativeObject::addPropertyInReservedSlot(JSContext* cx,
371                                              HandleNativeObject obj,
372                                              HandleId id, uint32_t slot,
373                                              PropertyFlags flags) {
374   AutoCheckShapeConsistency check(obj);
375   MOZ_ASSERT(!flags.isCustomDataProperty(),
376              "Use addCustomDataProperty for custom data properties");
377 
378   // The slot must be a reserved slot.
379   MOZ_ASSERT(slot < JSCLASS_RESERVED_SLOTS(obj->getClass()));
380 
381   // The object must not contain a property named |id| and must be extensible.
382   MOZ_ASSERT(!JSID_IS_VOID(id));
383   MOZ_ASSERT(!obj->containsPure(id));
384   MOZ_ASSERT(!id.isPrivateName());
385   MOZ_ASSERT(obj->isExtensible());
386 
387   // The object must not be in dictionary mode. This simplifies the code below.
388   MOZ_ASSERT(!obj->inDictionaryMode());
389 
390   ObjectFlags objectFlags = obj->shape()->objectFlags();
391   const JSClass* clasp = obj->shape()->getObjectClass();
392 
393   Rooted<SharedPropMap*> map(cx, obj->shape()->sharedPropMap());
394   uint32_t mapLength = obj->shape()->propMapLength();
395   if (!SharedPropMap::addPropertyInReservedSlot(cx, clasp, &map, &mapLength, id,
396                                                 flags, slot, &objectFlags)) {
397     return false;
398   }
399 
400   Shape* shape = SharedShape::getPropMapShape(cx, obj->shape()->base(),
401                                               obj->shape()->numFixedSlots(),
402                                               map, mapLength, objectFlags);
403   if (!shape) {
404     return false;
405   }
406   obj->setShape(shape);
407 
408   MOZ_ASSERT(obj->getLastProperty().slot() == slot);
409   return true;
410 }
411 
412 /*
413  * Assert some invariants that should hold when changing properties. It's the
414  * responsibility of the callers to ensure these hold.
415  */
AssertCanChangeFlags(PropertyInfo prop,PropertyFlags flags)416 static void AssertCanChangeFlags(PropertyInfo prop, PropertyFlags flags) {
417 #ifdef DEBUG
418   if (prop.configurable()) {
419     return;
420   }
421 
422   // A non-configurable property must stay non-configurable.
423   MOZ_ASSERT(!flags.configurable());
424 
425   // Reject attempts to turn a non-configurable data property into an accessor
426   // or custom data property.
427   MOZ_ASSERT_IF(prop.isDataProperty(), flags.isDataProperty());
428 
429   // Reject attempts to turn a non-configurable accessor property into a data
430   // property or custom data property.
431   MOZ_ASSERT_IF(prop.isAccessorProperty(), flags.isAccessorProperty());
432 #endif
433 }
434 
AssertValidArrayIndex(NativeObject * obj,jsid id)435 static void AssertValidArrayIndex(NativeObject* obj, jsid id) {
436 #ifdef DEBUG
437   if (obj->is<ArrayObject>()) {
438     ArrayObject* arr = &obj->as<ArrayObject>();
439     uint32_t index;
440     if (IdIsIndex(id, &index)) {
441       MOZ_ASSERT(index < arr->length() || arr->lengthIsWritable());
442     }
443   }
444 #endif
445 }
446 
447 /* static */
changeProperty(JSContext * cx,HandleNativeObject obj,HandleId id,PropertyFlags flags,uint32_t * slotOut)448 bool NativeObject::changeProperty(JSContext* cx, HandleNativeObject obj,
449                                   HandleId id, PropertyFlags flags,
450                                   uint32_t* slotOut) {
451   MOZ_ASSERT(!JSID_IS_VOID(id));
452 
453   AutoCheckShapeConsistency check(obj);
454   AssertValidArrayIndex(obj, id);
455   MOZ_ASSERT(!flags.isCustomDataProperty(),
456              "Use changeCustomDataPropAttributes for custom data properties");
457 
458   Rooted<PropMap*> map(cx, obj->shape()->propMap());
459   uint32_t mapLength = obj->shape()->propMapLength();
460 
461   uint32_t propIndex;
462   Rooted<PropMap*> propMap(cx, map->lookup(cx, mapLength, id, &propIndex));
463   MOZ_ASSERT(propMap);
464 
465   ObjectFlags objectFlags = obj->shape()->objectFlags();
466 
467   PropertyInfo oldProp = propMap->getPropertyInfo(propIndex);
468   AssertCanChangeFlags(oldProp, flags);
469 
470   if (oldProp.isAccessorProperty()) {
471     objectFlags.setFlag(ObjectFlag::HadGetterSetterChange);
472   }
473 
474   // If the property flags are not changing, the only thing we have to do is
475   // update the object flags. This prevents a dictionary mode conversion below.
476   if (oldProp.flags() == flags) {
477     if (objectFlags == obj->shape()->objectFlags()) {
478       *slotOut = oldProp.slot();
479       return true;
480     }
481     if (map->isShared()) {
482       if (!Shape::replaceShape(cx, obj, objectFlags, obj->shape()->proto(),
483                                obj->shape()->numFixedSlots())) {
484         return false;
485       }
486       *slotOut = oldProp.slot();
487       return true;
488     }
489   }
490 
491   const JSClass* clasp = obj->shape()->getObjectClass();
492 
493   if (map->isShared()) {
494     // Fast path for changing the last property in a SharedPropMap. Call
495     // getPrevious to "remove" the last property and then call addProperty
496     // to re-add the last property with the new flags.
497     if (propMap == map && propIndex == mapLength - 1) {
498       MOZ_ASSERT(obj->getLastProperty().key() == id);
499 
500       Rooted<SharedPropMap*> sharedMap(cx, map->asShared());
501       SharedPropMap::getPrevious(&sharedMap, &mapLength);
502 
503       if (oldProp.hasSlot()) {
504         *slotOut = oldProp.slot();
505         if (!SharedPropMap::addPropertyWithKnownSlot(cx, clasp, &sharedMap,
506                                                      &mapLength, id, flags,
507                                                      *slotOut, &objectFlags)) {
508           return false;
509         }
510       } else {
511         if (!SharedPropMap::addProperty(cx, clasp, &sharedMap, &mapLength, id,
512                                         flags, &objectFlags, slotOut)) {
513           return false;
514         }
515       }
516 
517       Shape* newShape = SharedShape::getPropMapShape(
518           cx, obj->shape()->base(), obj->shape()->numFixedSlots(), sharedMap,
519           mapLength, objectFlags);
520       if (!newShape) {
521         return false;
522       }
523       return obj->setShapeAndUpdateSlots(cx, newShape);
524     }
525 
526     // Changing a non-last property. Switch to dictionary mode and relookup
527     // pointers for the new dictionary map.
528     if (!NativeObject::toDictionaryMode(cx, obj)) {
529       return false;
530     }
531     map = obj->shape()->propMap();
532     propMap = map->lookup(cx, mapLength, id, &propIndex);
533     MOZ_ASSERT(propMap);
534   } else {
535     if (!NativeObject::generateNewDictionaryShape(cx, obj)) {
536       return false;
537     }
538   }
539 
540   // The object has a new dictionary shape (see toDictionaryMode and
541   // generateNewDictionaryShape calls above), so we can mutate the map and shape
542   // in place.
543 
544   MOZ_ASSERT(map->isDictionary());
545   MOZ_ASSERT(propMap->isDictionary());
546 
547   uint32_t slot = oldProp.hasSlot() ? oldProp.slot() : SHAPE_INVALID_SLOT;
548   if (slot == SHAPE_INVALID_SLOT) {
549     if (!allocDictionarySlot(cx, obj, &slot)) {
550       return false;
551     }
552   }
553 
554   propMap->asDictionary()->changeProperty(cx, clasp, propIndex, flags, slot,
555                                           &objectFlags);
556   obj->shape()->setObjectFlags(objectFlags);
557 
558   *slotOut = slot;
559   return true;
560 }
561 
562 /* static */
changeCustomDataPropAttributes(JSContext * cx,HandleNativeObject obj,HandleId id,PropertyFlags flags)563 bool NativeObject::changeCustomDataPropAttributes(JSContext* cx,
564                                                   HandleNativeObject obj,
565                                                   HandleId id,
566                                                   PropertyFlags flags) {
567   MOZ_ASSERT(!JSID_IS_VOID(id));
568 
569   AutoCheckShapeConsistency check(obj);
570   AssertValidArrayIndex(obj, id);
571   AssertValidCustomDataProp(obj, flags);
572 
573   Rooted<PropMap*> map(cx, obj->shape()->propMap());
574   uint32_t mapLength = obj->shape()->propMapLength();
575 
576   uint32_t propIndex;
577   Rooted<PropMap*> propMap(cx, map->lookup(cx, mapLength, id, &propIndex));
578   MOZ_ASSERT(propMap);
579 
580   PropertyInfo oldProp = propMap->getPropertyInfo(propIndex);
581   MOZ_ASSERT(oldProp.isCustomDataProperty());
582   AssertCanChangeFlags(oldProp, flags);
583 
584   // If the property flags are not changing, we're done.
585   if (oldProp.flags() == flags) {
586     return true;
587   }
588 
589   const JSClass* clasp = obj->shape()->getObjectClass();
590   ObjectFlags objectFlags = obj->shape()->objectFlags();
591 
592   if (map->isShared()) {
593     // Fast path for changing the last property in a SharedPropMap. Call
594     // getPrevious to "remove" the last property and then call
595     // addCustomDataProperty to re-add the last property with the new flags.
596     if (propMap == map && propIndex == mapLength - 1) {
597       MOZ_ASSERT(obj->getLastProperty().key() == id);
598 
599       Rooted<SharedPropMap*> sharedMap(cx, map->asShared());
600       SharedPropMap::getPrevious(&sharedMap, &mapLength);
601 
602       if (!SharedPropMap::addCustomDataProperty(
603               cx, clasp, &sharedMap, &mapLength, id, flags, &objectFlags)) {
604         return false;
605       }
606 
607       Shape* newShape = SharedShape::getPropMapShape(
608           cx, obj->shape()->base(), obj->shape()->numFixedSlots(), sharedMap,
609           mapLength, objectFlags);
610       if (!newShape) {
611         return false;
612       }
613       obj->setShape(newShape);
614       return true;
615     }
616 
617     // Changing a non-last property. Switch to dictionary mode and relookup
618     // pointers for the new dictionary map.
619     if (!NativeObject::toDictionaryMode(cx, obj)) {
620       return false;
621     }
622     map = obj->shape()->propMap();
623     propMap = map->lookup(cx, mapLength, id, &propIndex);
624     MOZ_ASSERT(propMap);
625   } else {
626     if (!NativeObject::generateNewDictionaryShape(cx, obj)) {
627       return false;
628     }
629   }
630 
631   // The object has a new dictionary shape (see toDictionaryMode and
632   // generateNewDictionaryShape calls above), so we can mutate the map and shape
633   // in place.
634 
635   MOZ_ASSERT(map->isDictionary());
636   MOZ_ASSERT(propMap->isDictionary());
637 
638   propMap->asDictionary()->changePropertyFlags(cx, clasp, propIndex, flags,
639                                                &objectFlags);
640   obj->shape()->setObjectFlags(objectFlags);
641   return true;
642 }
643 
644 /* static */
removeProperty(JSContext * cx,HandleNativeObject obj,HandleId id)645 bool NativeObject::removeProperty(JSContext* cx, HandleNativeObject obj,
646                                   HandleId id) {
647   AutoCheckShapeConsistency check(obj);
648 
649   Rooted<PropMap*> map(cx, obj->shape()->propMap());
650   uint32_t mapLength = obj->shape()->propMapLength();
651 
652   AutoKeepPropMapTables keep(cx);
653   PropMapTable* table;
654   PropMapTable::Ptr ptr;
655   Rooted<PropMap*> propMap(cx);
656   uint32_t propIndex;
657   if (!PropMap::lookupForRemove(cx, map, mapLength, id, keep, propMap.address(),
658                                 &propIndex, &table, &ptr)) {
659     return false;
660   }
661 
662   if (!propMap) {
663     return true;
664   }
665 
666   PropertyInfo prop = propMap->getPropertyInfo(propIndex);
667 
668   // If we're removing an accessor property, ensure the HadGetterSetterChange
669   // object flag is set. This is necessary because the slot holding the
670   // GetterSetter can be changed indirectly by removing the property and then
671   // adding it back with a different GetterSetter value but the same shape.
672   if (prop.isAccessorProperty() && !obj->hadGetterSetterChange()) {
673     if (!NativeObject::setHadGetterSetterChange(cx, obj)) {
674       return false;
675     }
676   }
677 
678   if (map->isShared()) {
679     // Fast path for removing the last property from a SharedPropMap. In this
680     // case we can just call getPrevious and then look up a shape for the
681     // resulting map/mapLength.
682     if (propMap == map && propIndex == mapLength - 1) {
683       MOZ_ASSERT(obj->getLastProperty().key() == id);
684 
685       Rooted<SharedPropMap*> sharedMap(cx, map->asShared());
686       SharedPropMap::getPrevious(&sharedMap, &mapLength);
687 
688       Shape* shape = obj->shape();
689       Shape* newShape;
690       if (sharedMap) {
691         newShape = SharedShape::getPropMapShape(
692             cx, shape->base(), shape->numFixedSlots(), sharedMap, mapLength,
693             shape->objectFlags());
694       } else {
695         newShape = SharedShape::getInitialShape(
696             cx, shape->getObjectClass(), shape->realm(), shape->proto(),
697             shape->numFixedSlots(), shape->objectFlags());
698       }
699       if (!newShape) {
700         return false;
701       }
702 
703       if (prop.hasSlot()) {
704         obj->setSlot(prop.slot(), UndefinedValue());
705       }
706       MOZ_ALWAYS_TRUE(obj->setShapeAndUpdateSlots(cx, newShape));
707       return true;
708     }
709 
710     // Removing a non-last property. Switch to dictionary mode and relookup
711     // pointers for the new dictionary map.
712     if (!NativeObject::toDictionaryMode(cx, obj)) {
713       return false;
714     }
715     map = obj->shape()->propMap();
716     if (!PropMap::lookupForRemove(cx, map, mapLength, id, keep,
717                                   propMap.address(), &propIndex, &table,
718                                   &ptr)) {
719       return false;
720     }
721   } else {
722     if (!NativeObject::generateNewDictionaryShape(cx, obj)) {
723       return false;
724     }
725   }
726 
727   // The object has a new dictionary shape (see toDictionaryMode and
728   // generateNewDictionaryShape calls above), so we can mutate the map and shape
729   // in place.
730 
731   MOZ_ASSERT(map->isDictionary());
732   MOZ_ASSERT(table);
733   MOZ_ASSERT(prop == ptr->propertyInfo());
734 
735   Rooted<DictionaryPropMap*> dictMap(cx, map->asDictionary());
736 
737   // If the property has a slot, free its slot number.
738   if (prop.hasSlot()) {
739     obj->freeDictionarySlot(prop.slot());
740   }
741 
742   DictionaryPropMap::removeProperty(cx, &dictMap, &mapLength, table, ptr);
743 
744   obj->shape()->updateNewDictionaryShape(obj->shape()->objectFlags(), dictMap,
745                                          mapLength);
746   return true;
747 }
748 
749 /* static */
densifySparseElements(JSContext * cx,HandleNativeObject obj)750 bool NativeObject::densifySparseElements(JSContext* cx,
751                                          HandleNativeObject obj) {
752   AutoCheckShapeConsistency check(obj);
753   MOZ_ASSERT(obj->inDictionaryMode());
754 
755   // First generate a new dictionary shape so that the shape and map can then
756   // be updated infallibly.
757   if (!NativeObject::generateNewDictionaryShape(cx, obj)) {
758     return false;
759   }
760 
761   Rooted<DictionaryPropMap*> map(cx, obj->shape()->propMap()->asDictionary());
762   uint32_t mapLength = obj->shape()->propMapLength();
763 
764   DictionaryPropMap::densifyElements(cx, &map, &mapLength, obj);
765 
766   // All indexed properties on the object are now dense. Clear the indexed
767   // flag so that we will not start using sparse indexes again if we need
768   // to grow the object.
769   ObjectFlags objectFlags = obj->shape()->objectFlags();
770   objectFlags.clearFlag(ObjectFlag::Indexed);
771 
772   obj->shape()->updateNewDictionaryShape(objectFlags, map, mapLength);
773   return true;
774 }
775 
776 // static
freezeOrSealProperties(JSContext * cx,HandleNativeObject obj,IntegrityLevel level)777 bool NativeObject::freezeOrSealProperties(JSContext* cx, HandleNativeObject obj,
778                                           IntegrityLevel level) {
779   uint32_t mapLength = obj->shape()->propMapLength();
780   MOZ_ASSERT(mapLength > 0);
781 
782   const JSClass* clasp = obj->shape()->getObjectClass();
783   ObjectFlags objectFlags = obj->shape()->objectFlags();
784 
785   if (obj->inDictionaryMode()) {
786     // First generate a new dictionary shape so that the map and shape can be
787     // updated infallibly.
788     if (!generateNewDictionaryShape(cx, obj)) {
789       return false;
790     }
791     DictionaryPropMap* map = obj->shape()->dictionaryPropMap();
792     map->freezeOrSealProperties(cx, level, clasp, mapLength, &objectFlags);
793     obj->shape()->updateNewDictionaryShape(objectFlags, map, mapLength);
794     return true;
795   }
796 
797   Rooted<SharedPropMap*> map(cx, obj->shape()->sharedPropMap());
798   if (!SharedPropMap::freezeOrSealProperties(cx, level, clasp, &map, mapLength,
799                                              &objectFlags)) {
800     return false;
801   }
802 
803   Shape* newShape = SharedShape::getPropMapShape(cx, obj->shape()->base(),
804                                                  obj->numFixedSlots(), map,
805                                                  mapLength, objectFlags);
806   if (!newShape) {
807     return false;
808   }
809   MOZ_ASSERT(obj->shape()->slotSpan() == newShape->slotSpan());
810 
811   obj->setShape(newShape);
812   return true;
813 }
814 
815 /* static */
generateNewDictionaryShape(JSContext * cx,HandleNativeObject obj)816 bool NativeObject::generateNewDictionaryShape(JSContext* cx,
817                                               HandleNativeObject obj) {
818   // Clone the current dictionary shape to a new shape. This ensures ICs and
819   // other shape guards are properly invalidated before we start mutating the
820   // map or new shape.
821 
822   MOZ_ASSERT(obj->inDictionaryMode());
823 
824   Rooted<BaseShape*> base(cx, obj->shape()->base());
825   Rooted<DictionaryPropMap*> map(cx, obj->shape()->dictionaryPropMap());
826   uint32_t mapLength = obj->shape()->propMapLength();
827 
828   Shape* shape =
829       DictionaryShape::new_(cx, base, obj->shape()->objectFlags(),
830                             obj->shape()->numFixedSlots(), map, mapLength);
831   if (!shape) {
832     return false;
833   }
834 
835   obj->setShape(shape);
836   return true;
837 }
838 
839 /* static */
setFlag(JSContext * cx,HandleObject obj,ObjectFlag flag)840 bool JSObject::setFlag(JSContext* cx, HandleObject obj, ObjectFlag flag) {
841   MOZ_ASSERT(cx->compartment() == obj->compartment());
842 
843   if (obj->hasFlag(flag)) {
844     return true;
845   }
846 
847   ObjectFlags objectFlags = obj->shape()->objectFlags();
848   objectFlags.setFlag(flag);
849 
850   if (obj->is<NativeObject>() && obj->as<NativeObject>().inDictionaryMode()) {
851     if (!NativeObject::generateNewDictionaryShape(cx, obj.as<NativeObject>())) {
852       return false;
853     }
854     obj->shape()->setObjectFlags(objectFlags);
855     return true;
856   }
857 
858   return Shape::replaceShape(cx, obj, objectFlags, obj->shape()->proto(),
859                              obj->shape()->numFixedSlots());
860 }
861 
862 /* static */
setProtoUnchecked(JSContext * cx,HandleObject obj,Handle<TaggedProto> proto)863 bool JSObject::setProtoUnchecked(JSContext* cx, HandleObject obj,
864                                  Handle<TaggedProto> proto) {
865   MOZ_ASSERT(cx->compartment() == obj->compartment());
866   MOZ_ASSERT_IF(proto.isObject(), proto.toObject()->isUsedAsPrototype());
867 
868   if (obj->shape()->proto() == proto) {
869     return true;
870   }
871 
872   if (obj->is<NativeObject>() && obj->as<NativeObject>().inDictionaryMode()) {
873     HandleNativeObject nobj = obj.as<NativeObject>();
874     Rooted<BaseShape*> nbase(
875         cx, BaseShape::get(cx, nobj->getClass(), nobj->realm(), proto));
876     if (!nbase) {
877       return false;
878     }
879 
880     if (!NativeObject::generateNewDictionaryShape(cx, nobj)) {
881       return false;
882     }
883 
884     nobj->shape()->setBase(nbase);
885     return true;
886   }
887 
888   return Shape::replaceShape(cx, obj, obj->shape()->objectFlags(), proto,
889                              obj->shape()->numFixedSlots());
890 }
891 
892 /* static */
changeNumFixedSlotsAfterSwap(JSContext * cx,HandleNativeObject obj,uint32_t nfixed)893 bool NativeObject::changeNumFixedSlotsAfterSwap(JSContext* cx,
894                                                 HandleNativeObject obj,
895                                                 uint32_t nfixed) {
896   MOZ_ASSERT(nfixed != obj->shape()->numFixedSlots());
897 
898   if (obj->inDictionaryMode()) {
899     if (!NativeObject::generateNewDictionaryShape(cx, obj)) {
900       return false;
901     }
902     obj->shape()->setNumFixedSlots(nfixed);
903     return true;
904   }
905 
906   return Shape::replaceShape(cx, obj, obj->shape()->objectFlags(),
907                              obj->shape()->proto(), nfixed);
908 }
909 
BaseShape(const JSClass * clasp,JS::Realm * realm,TaggedProto proto)910 BaseShape::BaseShape(const JSClass* clasp, JS::Realm* realm, TaggedProto proto)
911     : TenuredCellWithNonGCPointer(clasp), realm_(realm), proto_(proto) {
912   MOZ_ASSERT(JS::StringIsASCII(clasp->name));
913 
914   MOZ_ASSERT_IF(proto.isObject(),
915                 compartment() == proto.toObject()->compartment());
916   MOZ_ASSERT_IF(proto.isObject(), proto.toObject()->isUsedAsPrototype());
917 
918   // Windows may not appear on prototype chains.
919   MOZ_ASSERT_IF(proto.isObject(), !IsWindow(proto.toObject()));
920 
921 #ifdef DEBUG
922   if (GlobalObject* global = realm->unsafeUnbarrieredMaybeGlobal()) {
923     AssertTargetIsNotGray(global);
924   }
925 #endif
926 }
927 
928 /* static */
get(JSContext * cx,const JSClass * clasp,JS::Realm * realm,Handle<TaggedProto> proto)929 BaseShape* BaseShape::get(JSContext* cx, const JSClass* clasp, JS::Realm* realm,
930                           Handle<TaggedProto> proto) {
931   auto& table = cx->zone()->shapeZone().baseShapes;
932 
933   using Lookup = BaseShapeHasher::Lookup;
934 
935   auto p = MakeDependentAddPtr(cx, table, Lookup(clasp, realm, proto));
936   if (p) {
937     return *p;
938   }
939 
940   BaseShape* nbase = Allocate<BaseShape>(cx);
941   if (!nbase) {
942     return nullptr;
943   }
944   new (nbase) BaseShape(clasp, realm, proto);
945 
946   if (!p.add(cx, table, Lookup(clasp, realm, proto), nbase)) {
947     return nullptr;
948   }
949 
950   return nbase;
951 }
952 
new_(JSContext * cx,Handle<BaseShape * > base,ObjectFlags objectFlags,uint32_t nfixed,Handle<SharedPropMap * > map,uint32_t mapLength)953 Shape* SharedShape::new_(JSContext* cx, Handle<BaseShape*> base,
954                          ObjectFlags objectFlags, uint32_t nfixed,
955                          Handle<SharedPropMap*> map, uint32_t mapLength) {
956   Shape* shape = Allocate<Shape>(cx);
957   if (!shape) {
958     ReportOutOfMemory(cx);
959     return nullptr;
960   }
961 
962   new (shape) SharedShape(base, objectFlags, nfixed, map, mapLength);
963   return shape;
964 }
965 
new_(JSContext * cx,Handle<BaseShape * > base,ObjectFlags objectFlags,uint32_t nfixed,Handle<DictionaryPropMap * > map,uint32_t mapLength)966 Shape* DictionaryShape::new_(JSContext* cx, Handle<BaseShape*> base,
967                              ObjectFlags objectFlags, uint32_t nfixed,
968                              Handle<DictionaryPropMap*> map,
969                              uint32_t mapLength) {
970   Shape* shape = Allocate<Shape>(cx);
971   if (!shape) {
972     ReportOutOfMemory(cx);
973     return nullptr;
974   }
975 
976   new (shape) DictionaryShape(base, objectFlags, nfixed, map, mapLength);
977   return shape;
978 }
979 
hash(const Lookup & l)980 MOZ_ALWAYS_INLINE HashNumber ShapeForAddHasher::hash(const Lookup& l) {
981   HashNumber hash = HashPropertyKey(l.key);
982   return mozilla::AddToHash(hash, l.flags.toRaw());
983 }
984 
match(Shape * shape,const Lookup & l)985 MOZ_ALWAYS_INLINE bool ShapeForAddHasher::match(Shape* shape, const Lookup& l) {
986   uint32_t slot;
987   return shape->lastPropertyMatchesForAdd(l.key, l.flags, &slot);
988 }
989 
990 #ifdef DEBUG
dump(js::GenericPrinter & out) const991 void Shape::dump(js::GenericPrinter& out) const {
992   out.printf("shape @ 0x%p\n", this);
993   out.printf("base: 0x%p\n", base());
994   out.printf("mapLength: %u\n", propMapLength());
995   out.printf("dictionary: %s\n", isDictionary() ? "yes" : "no");
996   if (propMap_) {
997     out.printf("map:\n");
998     propMap_->dump(out);
999   } else {
1000     out.printf("map: (none)\n");
1001   }
1002 }
1003 
dump() const1004 void Shape::dump() const {
1005   Fprinter out(stderr);
1006   dump(out);
1007 }
1008 #endif  // DEBUG
1009 
1010 /* static */
getInitialShape(JSContext * cx,const JSClass * clasp,JS::Realm * realm,TaggedProto proto,size_t nfixed,ObjectFlags objectFlags)1011 Shape* SharedShape::getInitialShape(JSContext* cx, const JSClass* clasp,
1012                                     JS::Realm* realm, TaggedProto proto,
1013                                     size_t nfixed, ObjectFlags objectFlags) {
1014   MOZ_ASSERT(cx->compartment() == realm->compartment());
1015   MOZ_ASSERT_IF(proto.isObject(),
1016                 cx->isInsideCurrentCompartment(proto.toObject()));
1017 
1018   if (proto.isObject()) {
1019     if (proto.toObject()->isUsedAsPrototype()) {
1020       // Use the cache on the prototype's shape to get to the initial shape.
1021       // This cache has a hit rate of 80-90% on typical workloads and is faster
1022       // than the HashSet lookup below.
1023       JSObject* protoObj = proto.toObject();
1024       Shape* protoObjShape = protoObj->shape();
1025       if (protoObjShape->cache().isShapeWithProto()) {
1026         Shape* shape = protoObjShape->cache().toShapeWithProto();
1027         if (shape->numFixedSlots() == nfixed &&
1028             shape->objectFlags() == objectFlags &&
1029             shape->getObjectClass() == clasp && shape->realm() == realm &&
1030             shape->proto() == proto) {
1031 #ifdef DEBUG
1032           // Verify the table lookup below would have resulted in the same
1033           // shape.
1034           using Lookup = InitialShapeHasher::Lookup;
1035           Lookup lookup(clasp, realm, proto, nfixed, objectFlags);
1036           auto p = realm->zone()->shapeZone().initialShapes.lookup(lookup);
1037           MOZ_ASSERT(*p == shape);
1038 #endif
1039           return shape;
1040         }
1041       }
1042     } else {
1043       RootedObject protoObj(cx, proto.toObject());
1044       if (!JSObject::setIsUsedAsPrototype(cx, protoObj)) {
1045         return nullptr;
1046       }
1047       // Ensure the proto object has a unique id to prevent OOM crashes below.
1048       uint64_t unused;
1049       if (!cx->zone()->getOrCreateUniqueId(protoObj, &unused)) {
1050         ReportOutOfMemory(cx);
1051         return nullptr;
1052       }
1053       proto = TaggedProto(protoObj);
1054     }
1055   }
1056 
1057   auto& table = realm->zone()->shapeZone().initialShapes;
1058 
1059   using Lookup = InitialShapeHasher::Lookup;
1060   auto ptr = MakeDependentAddPtr(
1061       cx, table, Lookup(clasp, realm, proto, nfixed, objectFlags));
1062   if (ptr) {
1063     // Cache the result of this lookup on the prototype's shape.
1064     if (proto.isObject()) {
1065       JSObject* protoObj = proto.toObject();
1066       Shape* protoShape = protoObj->shape();
1067       if ((protoShape->cache().isShapeWithProto() ||
1068            protoShape->cache().isNone()) &&
1069           RegisterShapeCache(cx, protoShape)) {
1070         protoShape->cacheRef().setShapeWithProto(*ptr);
1071       }
1072     }
1073     return *ptr;
1074   }
1075 
1076   Rooted<TaggedProto> protoRoot(cx, proto);
1077   Rooted<BaseShape*> nbase(cx, BaseShape::get(cx, clasp, realm, protoRoot));
1078   if (!nbase) {
1079     return nullptr;
1080   }
1081 
1082   RootedShape shape(
1083       cx, SharedShape::new_(cx, nbase, objectFlags, nfixed, nullptr, 0));
1084   if (!shape) {
1085     return nullptr;
1086   }
1087 
1088   Lookup lookup(clasp, realm, protoRoot, nfixed, objectFlags);
1089   if (!ptr.add(cx, table, lookup, shape)) {
1090     return nullptr;
1091   }
1092 
1093   return shape;
1094 }
1095 
1096 /* static */
getInitialShape(JSContext * cx,const JSClass * clasp,JS::Realm * realm,TaggedProto proto,gc::AllocKind kind,ObjectFlags objectFlags)1097 Shape* SharedShape::getInitialShape(JSContext* cx, const JSClass* clasp,
1098                                     JS::Realm* realm, TaggedProto proto,
1099                                     gc::AllocKind kind,
1100                                     ObjectFlags objectFlags) {
1101   return getInitialShape(cx, clasp, realm, proto, GetGCKindSlots(kind, clasp),
1102                          objectFlags);
1103 }
1104 
1105 /* static */
getPropMapShape(JSContext * cx,BaseShape * base,size_t nfixed,Handle<SharedPropMap * > map,uint32_t mapLength,ObjectFlags objectFlags,bool * allocatedNewShape)1106 Shape* SharedShape::getPropMapShape(JSContext* cx, BaseShape* base,
1107                                     size_t nfixed, Handle<SharedPropMap*> map,
1108                                     uint32_t mapLength, ObjectFlags objectFlags,
1109                                     bool* allocatedNewShape) {
1110   MOZ_ASSERT(cx->compartment() == base->compartment());
1111   MOZ_ASSERT_IF(base->proto().isObject(),
1112                 cx->isInsideCurrentCompartment(base->proto().toObject()));
1113   MOZ_ASSERT_IF(base->proto().isObject(),
1114                 base->proto().toObject()->isUsedAsPrototype());
1115   MOZ_ASSERT(map);
1116   MOZ_ASSERT(mapLength > 0);
1117 
1118   auto& table = cx->zone()->shapeZone().propMapShapes;
1119 
1120   using Lookup = PropMapShapeHasher::Lookup;
1121   auto ptr = MakeDependentAddPtr(
1122       cx, table, Lookup(base, nfixed, map, mapLength, objectFlags));
1123   if (ptr) {
1124     if (allocatedNewShape) {
1125       *allocatedNewShape = false;
1126     }
1127     return *ptr;
1128   }
1129 
1130   Rooted<BaseShape*> baseRoot(cx, base);
1131   RootedShape shape(
1132       cx, SharedShape::new_(cx, baseRoot, objectFlags, nfixed, map, mapLength));
1133   if (!shape) {
1134     return nullptr;
1135   }
1136 
1137   Lookup lookup(baseRoot, nfixed, map, mapLength, objectFlags);
1138   if (!ptr.add(cx, table, lookup, shape)) {
1139     return nullptr;
1140   }
1141 
1142   if (allocatedNewShape) {
1143     *allocatedNewShape = true;
1144   }
1145 
1146   return shape;
1147 }
1148 
1149 /* static */
getInitialOrPropMapShape(JSContext * cx,const JSClass * clasp,JS::Realm * realm,TaggedProto proto,size_t nfixed,Handle<SharedPropMap * > map,uint32_t mapLength,ObjectFlags objectFlags)1150 Shape* SharedShape::getInitialOrPropMapShape(
1151     JSContext* cx, const JSClass* clasp, JS::Realm* realm, TaggedProto proto,
1152     size_t nfixed, Handle<SharedPropMap*> map, uint32_t mapLength,
1153     ObjectFlags objectFlags) {
1154   if (!map) {
1155     MOZ_ASSERT(mapLength == 0);
1156     return getInitialShape(cx, clasp, realm, proto, nfixed, objectFlags);
1157   }
1158 
1159   Rooted<TaggedProto> protoRoot(cx, proto);
1160   BaseShape* nbase = BaseShape::get(cx, clasp, realm, protoRoot);
1161   if (!nbase) {
1162     return nullptr;
1163   }
1164 
1165   return getPropMapShape(cx, nbase, nfixed, map, mapLength, objectFlags);
1166 }
1167 
invalidateEntriesForShape(Shape * shape)1168 void NewObjectCache::invalidateEntriesForShape(Shape* shape) {
1169   const JSClass* clasp = shape->getObjectClass();
1170 
1171   gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
1172   if (CanChangeToBackgroundAllocKind(kind, clasp)) {
1173     kind = ForegroundToBackgroundAllocKind(kind);
1174   }
1175 
1176   EntryIndex entry;
1177   for (RealmsInZoneIter realm(shape->zone()); !realm.done(); realm.next()) {
1178     if (GlobalObject* global = realm->unsafeUnbarrieredMaybeGlobal()) {
1179       if (lookupGlobal(clasp, global, kind, &entry)) {
1180         PodZero(&entries[entry]);
1181       }
1182     }
1183   }
1184 
1185   JSObject* proto = shape->proto().toObject();
1186   if (!proto->is<GlobalObject>() && lookupProto(clasp, proto, kind, &entry)) {
1187     PodZero(&entries[entry]);
1188   }
1189 }
1190 
1191 /* static */
insertInitialShape(JSContext * cx,HandleShape shape)1192 void SharedShape::insertInitialShape(JSContext* cx, HandleShape shape) {
1193   using Lookup = InitialShapeHasher::Lookup;
1194   Lookup lookup(shape->getObjectClass(), shape->realm(), shape->proto(),
1195                 shape->numFixedSlots(), shape->objectFlags());
1196 
1197   auto& table = cx->zone()->shapeZone().initialShapes;
1198   InitialShapeSet::Ptr p = table.lookup(lookup);
1199   MOZ_ASSERT(p);
1200 
1201   // The metadata callback can end up causing redundant changes of the initial
1202   // shape.
1203   Shape* initialShape = *p;
1204   if (initialShape == shape) {
1205     return;
1206   }
1207 
1208   MOZ_ASSERT(initialShape->numFixedSlots() == shape->numFixedSlots());
1209   MOZ_ASSERT(initialShape->base() == shape->base());
1210   MOZ_ASSERT(initialShape->objectFlags() == shape->objectFlags());
1211 
1212   table.replaceKey(p, lookup, shape.get());
1213 
1214   // Purge the prototype's shape cache entry.
1215   if (shape->proto().isObject()) {
1216     JSObject* protoObj = shape->proto().toObject();
1217     if (protoObj->shape()->cache().isShapeWithProto()) {
1218       protoObj->shape()->cacheRef().setNone();
1219     }
1220   }
1221 
1222   /*
1223    * This affects the shape that will be produced by the various NewObject
1224    * methods, so clear any cache entry referring to the old shape. This is
1225    * not required for correctness: the NewObject must always check for a
1226    * nativeEmpty() result and generate the appropriate properties if found.
1227    * Clearing the cache entry avoids this duplicate regeneration.
1228    *
1229    * Clearing is not necessary when this context is running off
1230    * thread, as it will not use the new object cache for allocations.
1231    */
1232   if (!cx->isHelperThreadContext()) {
1233     cx->caches().newObjectCache.invalidateEntriesForShape(shape);
1234   }
1235 }
1236 
size(mozilla::MallocSizeOf mallocSizeOf) const1237 JS::ubi::Node::Size JS::ubi::Concrete<js::Shape>::size(
1238     mozilla::MallocSizeOf mallocSizeOf) const {
1239   Size size = js::gc::Arena::thingSize(get().asTenured().getAllocKind());
1240 
1241   if (get().cache().isShapeSetForAdd()) {
1242     ShapeSetForAdd* set = get().cache().toShapeSetForAdd();
1243     size += set->shallowSizeOfIncludingThis(mallocSizeOf);
1244   }
1245 
1246   return size;
1247 }
1248 
size(mozilla::MallocSizeOf mallocSizeOf) const1249 JS::ubi::Node::Size JS::ubi::Concrete<js::BaseShape>::size(
1250     mozilla::MallocSizeOf mallocSizeOf) const {
1251   return js::gc::Arena::thingSize(get().asTenured().getAllocKind());
1252 }
1253