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 #ifndef vm_NativeObject_h
8 #define vm_NativeObject_h
9 
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/Maybe.h"
13 
14 #include <algorithm>
15 #include <stdint.h>
16 
17 #include "jsfriendapi.h"
18 #include "NamespaceImports.h"
19 
20 #include "gc/Barrier.h"
21 #include "gc/Marking.h"
22 #include "gc/MaybeRooted.h"
23 #include "gc/ZoneAllocator.h"
24 #include "js/shadow/Object.h"  // JS::shadow::Object
25 #include "js/shadow/Zone.h"    // JS::shadow::Zone
26 #include "js/Value.h"
27 #include "vm/GetterSetter.h"
28 #include "vm/JSObject.h"
29 #include "vm/PropertyResult.h"
30 #include "vm/Shape.h"
31 #include "vm/StringType.h"
32 
33 namespace js {
34 
35 class Shape;
36 class TenuringTracer;
37 
38 /*
39  * To really poison a set of values, using 'magic' or 'undefined' isn't good
40  * enough since often these will just be ignored by buggy code (see bug 629974)
41  * in debug builds and crash in release builds. Instead, we use a safe-for-crash
42  * pointer.
43  */
Debug_SetValueRangeToCrashOnTouch(Value * beg,Value * end)44 static MOZ_ALWAYS_INLINE void Debug_SetValueRangeToCrashOnTouch(Value* beg,
45                                                                 Value* end) {
46 #ifdef DEBUG
47   for (Value* v = beg; v != end; ++v) {
48     *v = js::PoisonedObjectValue(0x48);
49   }
50 #endif
51 }
52 
Debug_SetValueRangeToCrashOnTouch(Value * vec,size_t len)53 static MOZ_ALWAYS_INLINE void Debug_SetValueRangeToCrashOnTouch(Value* vec,
54                                                                 size_t len) {
55 #ifdef DEBUG
56   Debug_SetValueRangeToCrashOnTouch(vec, vec + len);
57 #endif
58 }
59 
Debug_SetValueRangeToCrashOnTouch(GCPtrValue * vec,size_t len)60 static MOZ_ALWAYS_INLINE void Debug_SetValueRangeToCrashOnTouch(GCPtrValue* vec,
61                                                                 size_t len) {
62 #ifdef DEBUG
63   Debug_SetValueRangeToCrashOnTouch((Value*)vec, len);
64 #endif
65 }
66 
Debug_SetSlotRangeToCrashOnTouch(HeapSlot * vec,uint32_t len)67 static MOZ_ALWAYS_INLINE void Debug_SetSlotRangeToCrashOnTouch(HeapSlot* vec,
68                                                                uint32_t len) {
69 #ifdef DEBUG
70   Debug_SetValueRangeToCrashOnTouch((Value*)vec, len);
71 #endif
72 }
73 
Debug_SetSlotRangeToCrashOnTouch(HeapSlot * begin,HeapSlot * end)74 static MOZ_ALWAYS_INLINE void Debug_SetSlotRangeToCrashOnTouch(HeapSlot* begin,
75                                                                HeapSlot* end) {
76 #ifdef DEBUG
77   Debug_SetValueRangeToCrashOnTouch((Value*)begin, end - begin);
78 #endif
79 }
80 
81 class ArrayObject;
82 
83 /*
84  * ES6 20130308 draft 8.4.2.4 ArraySetLength.
85  *
86  * |id| must be "length", |desc| is the new non-accessor descriptor, and
87  * |result| receives an error code if the change is invalid.
88  */
89 extern bool ArraySetLength(JSContext* cx, Handle<ArrayObject*> obj, HandleId id,
90                            Handle<PropertyDescriptor> desc,
91                            ObjectOpResult& result);
92 
93 /*
94  * [SMDOC] NativeObject Elements layout
95  *
96  * Elements header used for native objects. The elements component of such
97  * objects offers an efficient representation for all or some of the indexed
98  * properties of the object, using a flat array of Values rather than a shape
99  * hierarchy stored in the object's slots. This structure is immediately
100  * followed by an array of elements, with the elements member in an object
101  * pointing to the beginning of that array (the end of this structure). See
102  * below for usage of this structure.
103  *
104  * The sets of properties represented by an object's elements and slots
105  * are disjoint. The elements contain only indexed properties, while the slots
106  * can contain both named and indexed properties; any indexes in the slots are
107  * distinct from those in the elements. If isIndexed() is false for an object,
108  * all indexed properties (if any) are stored in the dense elements.
109  *
110  * Indexes will be stored in the object's slots instead of its elements in
111  * the following case:
112  *  - there are more than MIN_SPARSE_INDEX slots total and the load factor
113  *    (COUNT / capacity) is less than 0.25
114  *  - a property is defined that has non-default property attributes.
115  *
116  * We track these pieces of metadata for dense elements:
117  *  - The length property as a uint32_t, accessible for array objects with
118  *    ArrayObject::{length,setLength}().  This is unused for non-arrays.
119  *  - The number of element slots (capacity), gettable with
120  *    getDenseCapacity().
121  *  - The array's initialized length, accessible with
122  *    getDenseInitializedLength().
123  *
124  * Holes in the array are represented by MagicValue(JS_ELEMENTS_HOLE) values.
125  * These indicate indexes which are not dense properties of the array. The
126  * property may, however, be held by the object's properties.
127  *
128  * The capacity and length of an object's elements are almost entirely
129  * unrelated!  In general the length may be greater than, less than, or equal
130  * to the capacity.  The first case occurs with |new Array(100)|.  The length
131  * is 100, but the capacity remains 0 (indices below length and above capacity
132  * must be treated as holes) until elements between capacity and length are
133  * set.  The other two cases are common, depending upon the number of elements
134  * in an array and the underlying allocator used for element storage.
135  *
136  * The only case in which the capacity and length of an object's elements are
137  * related is when the object is an array with non-writable length.  In this
138  * case the capacity is always less than or equal to the length.  This permits
139  * JIT code to optimize away the check for non-writable length when assigning
140  * to possibly out-of-range elements: such code already has to check for
141  * |index < capacity|, and fallback code checks for non-writable length.
142  *
143  * The initialized length of an object specifies the number of elements that
144  * have been initialized. All elements above the initialized length are
145  * holes in the object, and the memory for all elements between the initialized
146  * length and capacity is left uninitialized. The initialized length is some
147  * value less than or equal to both the object's length and the object's
148  * capacity.
149  *
150  * There is flexibility in exactly the value the initialized length must hold,
151  * e.g. if an array has length 5, capacity 10, completely empty, it is valid
152  * for the initialized length to be any value between zero and 5, as long as
153  * the in memory values below the initialized length have been initialized with
154  * a hole value. However, in such cases we want to keep the initialized length
155  * as small as possible: if the object is known to have no hole values below
156  * its initialized length, then it is "packed" and can be accessed much faster
157  * by JIT code.
158  *
159  * Elements do not track property creation order, so enumerating the elements
160  * of an object does not necessarily visit indexes in the order they were
161  * created.
162  *
163  *
164  * [SMDOC] NativeObject shifted elements optimization
165  *
166  * Shifted elements
167  * ----------------
168  * It's pretty common to use an array as a queue, like this:
169  *
170  *    while (arr.length > 0)
171  *        foo(arr.shift());
172  *
173  * To ensure we don't get quadratic behavior on this, elements can be 'shifted'
174  * in memory. tryShiftDenseElements does this by incrementing elements_ to point
175  * to the next element and moving the ObjectElements header in memory (so it's
176  * stored where the shifted Value used to be).
177  *
178  * Shifted elements can be moved when we grow the array, when the array is
179  * made non-extensible (for simplicity, shifted elements are not supported on
180  * objects that are non-extensible, have copy-on-write elements, or on arrays
181  * with non-writable length).
182  */
183 class ObjectElements {
184  public:
185   enum Flags : uint16_t {
186     // (0x1 is unused)
187 
188     // Present only if these elements correspond to an array with
189     // non-writable length; never present for non-arrays.
190     NONWRITABLE_ARRAY_LENGTH = 0x2,
191 
192     // (0x4 is unused)
193 
194     // For TypedArrays only: this TypedArray's storage is mapping shared
195     // memory.  This is a static property of the TypedArray, set when it
196     // is created and never changed.
197     SHARED_MEMORY = 0x8,
198 
199     // These elements are not extensible. If this flag is set, the object's
200     // Shape must also have the NotExtensible flag. This exists on
201     // ObjectElements in addition to Shape to simplify JIT code.
202     NOT_EXTENSIBLE = 0x10,
203 
204     // These elements are set to integrity level "sealed". If this flag is
205     // set, the NOT_EXTENSIBLE flag must be set as well.
206     SEALED = 0x20,
207 
208     // These elements are set to integrity level "frozen". If this flag is
209     // set, the SEALED flag must be set as well.
210     //
211     // This flag must only be set if the Shape has the FrozenElements flag.
212     // The Shape flag ensures a shape guard can be used to guard against frozen
213     // elements. The ObjectElements flag is convenient for JIT code and
214     // ObjectElements assertions.
215     FROZEN = 0x40,
216 
217     // If this flag is not set, the elements are guaranteed to contain no hole
218     // values (the JS_ELEMENTS_HOLE MagicValue) in [0, initializedLength).
219     NON_PACKED = 0x80,
220 
221     // If this flag is not set, there's definitely no for-in iterator that
222     // covers these dense elements so elements can be deleted without calling
223     // SuppressDeletedProperty. This is used by fast paths for various Array
224     // builtins. See also NativeObject::denseElementsMaybeInIteration.
225     MAYBE_IN_ITERATION = 0x100,
226   };
227 
228   // The flags word stores both the flags and the number of shifted elements.
229   // Allow shifting 2047 elements before actually moving the elements.
230   static const size_t NumShiftedElementsBits = 11;
231   static const size_t MaxShiftedElements = (1 << NumShiftedElementsBits) - 1;
232   static const size_t NumShiftedElementsShift = 32 - NumShiftedElementsBits;
233   static const size_t FlagsMask = (1 << NumShiftedElementsShift) - 1;
234   static_assert(MaxShiftedElements == 2047,
235                 "MaxShiftedElements should match the comment");
236 
237  private:
238   friend class ::JSObject;
239   friend class ArrayObject;
240   friend class NativeObject;
241   friend class TenuringTracer;
242 
243   friend bool js::SetIntegrityLevel(JSContext* cx, HandleObject obj,
244                                     IntegrityLevel level);
245 
246   friend bool ArraySetLength(JSContext* cx, Handle<ArrayObject*> obj,
247                              HandleId id, Handle<PropertyDescriptor> desc,
248                              ObjectOpResult& result);
249 
250   // The NumShiftedElementsBits high bits of this are used to store the
251   // number of shifted elements, the other bits are available for the flags.
252   // See Flags enum above.
253   uint32_t flags;
254 
255   /*
256    * Number of initialized elements. This is <= the capacity, and for arrays
257    * is <= the length. Memory for elements above the initialized length is
258    * uninitialized, but values between the initialized length and the proper
259    * length are conceptually holes.
260    */
261   uint32_t initializedLength;
262 
263   /* Number of allocated slots. */
264   uint32_t capacity;
265 
266   /* 'length' property of array objects, unused for other objects. */
267   uint32_t length;
268 
hasNonwritableArrayLength()269   bool hasNonwritableArrayLength() const {
270     return flags & NONWRITABLE_ARRAY_LENGTH;
271   }
setNonwritableArrayLength()272   void setNonwritableArrayLength() {
273     // See ArrayObject::setNonWritableLength.
274     MOZ_ASSERT(capacity == initializedLength);
275     MOZ_ASSERT(numShiftedElements() == 0);
276     flags |= NONWRITABLE_ARRAY_LENGTH;
277   }
278 
addShiftedElements(uint32_t count)279   void addShiftedElements(uint32_t count) {
280     MOZ_ASSERT(count < capacity);
281     MOZ_ASSERT(count < initializedLength);
282     MOZ_ASSERT(!(
283         flags & (NONWRITABLE_ARRAY_LENGTH | NOT_EXTENSIBLE | SEALED | FROZEN)));
284     uint32_t numShifted = numShiftedElements() + count;
285     MOZ_ASSERT(numShifted <= MaxShiftedElements);
286     flags = (numShifted << NumShiftedElementsShift) | (flags & FlagsMask);
287     capacity -= count;
288     initializedLength -= count;
289   }
unshiftShiftedElements(uint32_t count)290   void unshiftShiftedElements(uint32_t count) {
291     MOZ_ASSERT(count > 0);
292     MOZ_ASSERT(!(
293         flags & (NONWRITABLE_ARRAY_LENGTH | NOT_EXTENSIBLE | SEALED | FROZEN)));
294     uint32_t numShifted = numShiftedElements();
295     MOZ_ASSERT(count <= numShifted);
296     numShifted -= count;
297     flags = (numShifted << NumShiftedElementsShift) | (flags & FlagsMask);
298     capacity += count;
299     initializedLength += count;
300   }
clearShiftedElements()301   void clearShiftedElements() {
302     flags &= FlagsMask;
303     MOZ_ASSERT(numShiftedElements() == 0);
304   }
305 
markNonPacked()306   void markNonPacked() { flags |= NON_PACKED; }
307 
markMaybeInIteration()308   void markMaybeInIteration() { flags |= MAYBE_IN_ITERATION; }
maybeInIteration()309   bool maybeInIteration() { return flags & MAYBE_IN_ITERATION; }
310 
setNotExtensible()311   void setNotExtensible() {
312     MOZ_ASSERT(!isNotExtensible());
313     flags |= NOT_EXTENSIBLE;
314   }
isNotExtensible()315   bool isNotExtensible() { return flags & NOT_EXTENSIBLE; }
316 
seal()317   void seal() {
318     MOZ_ASSERT(isNotExtensible());
319     MOZ_ASSERT(!isSealed());
320     MOZ_ASSERT(!isFrozen());
321     flags |= SEALED;
322   }
freeze()323   void freeze() {
324     MOZ_ASSERT(isNotExtensible());
325     MOZ_ASSERT(isSealed());
326     MOZ_ASSERT(!isFrozen());
327     flags |= FROZEN;
328   }
329 
isFrozen()330   bool isFrozen() const { return flags & FROZEN; }
331 
332  public:
ObjectElements(uint32_t capacity,uint32_t length)333   constexpr ObjectElements(uint32_t capacity, uint32_t length)
334       : flags(0), initializedLength(0), capacity(capacity), length(length) {}
335 
336   enum class SharedMemory { IsShared };
337 
ObjectElements(uint32_t capacity,uint32_t length,SharedMemory shmem)338   constexpr ObjectElements(uint32_t capacity, uint32_t length,
339                            SharedMemory shmem)
340       : flags(SHARED_MEMORY),
341         initializedLength(0),
342         capacity(capacity),
343         length(length) {}
344 
elements()345   HeapSlot* elements() {
346     return reinterpret_cast<HeapSlot*>(uintptr_t(this) +
347                                        sizeof(ObjectElements));
348   }
elements()349   const HeapSlot* elements() const {
350     return reinterpret_cast<const HeapSlot*>(uintptr_t(this) +
351                                              sizeof(ObjectElements));
352   }
fromElements(HeapSlot * elems)353   static ObjectElements* fromElements(HeapSlot* elems) {
354     return reinterpret_cast<ObjectElements*>(uintptr_t(elems) -
355                                              sizeof(ObjectElements));
356   }
357 
isSharedMemory()358   bool isSharedMemory() const { return flags & SHARED_MEMORY; }
359 
offsetOfFlags()360   static int offsetOfFlags() {
361     return int(offsetof(ObjectElements, flags)) - int(sizeof(ObjectElements));
362   }
offsetOfInitializedLength()363   static int offsetOfInitializedLength() {
364     return int(offsetof(ObjectElements, initializedLength)) -
365            int(sizeof(ObjectElements));
366   }
offsetOfCapacity()367   static int offsetOfCapacity() {
368     return int(offsetof(ObjectElements, capacity)) -
369            int(sizeof(ObjectElements));
370   }
offsetOfLength()371   static int offsetOfLength() {
372     return int(offsetof(ObjectElements, length)) - int(sizeof(ObjectElements));
373   }
374 
375   static void PrepareForPreventExtensions(JSContext* cx, NativeObject* obj);
376   static void PreventExtensions(NativeObject* obj);
377   [[nodiscard]] static bool FreezeOrSeal(JSContext* cx, HandleNativeObject obj,
378                                          IntegrityLevel level);
379 
isSealed()380   bool isSealed() const { return flags & SEALED; }
381 
isPacked()382   bool isPacked() const { return !(flags & NON_PACKED); }
383 
elementAttributes()384   JS::PropertyAttributes elementAttributes() const {
385     if (isFrozen()) {
386       return {JS::PropertyAttribute::Enumerable};
387     }
388     if (isSealed()) {
389       return {JS::PropertyAttribute::Enumerable,
390               JS::PropertyAttribute::Writable};
391     }
392     return {JS::PropertyAttribute::Configurable,
393             JS::PropertyAttribute::Enumerable, JS::PropertyAttribute::Writable};
394   }
395 
numShiftedElements()396   uint32_t numShiftedElements() const {
397     uint32_t numShifted = flags >> NumShiftedElementsShift;
398     MOZ_ASSERT_IF(numShifted > 0,
399                   !(flags & (NONWRITABLE_ARRAY_LENGTH | NOT_EXTENSIBLE |
400                              SEALED | FROZEN)));
401     return numShifted;
402   }
403 
numAllocatedElements()404   uint32_t numAllocatedElements() const {
405     return VALUES_PER_HEADER + capacity + numShiftedElements();
406   }
407 
408   // This is enough slots to store an object of this class. See the static
409   // assertion below.
410   static const size_t VALUES_PER_HEADER = 2;
411 };
412 
413 static_assert(ObjectElements::VALUES_PER_HEADER * sizeof(HeapSlot) ==
414                   sizeof(ObjectElements),
415               "ObjectElements doesn't fit in the given number of slots");
416 
417 /*
418  * Slots header used for native objects. The header stores the capacity and the
419  * slot data follows in memory.
420  */
alignas(HeapSlot)421 class alignas(HeapSlot) ObjectSlots {
422   uint32_t capacity_;
423   uint32_t dictionarySlotSpan_;
424 
425  public:
426   static constexpr size_t VALUES_PER_HEADER = 1;
427 
428   static inline size_t allocCount(size_t slotCount) {
429     static_assert(sizeof(ObjectSlots) ==
430                   ObjectSlots::VALUES_PER_HEADER * sizeof(HeapSlot));
431     return slotCount + VALUES_PER_HEADER;
432   }
433 
434   static inline size_t allocSize(size_t slotCount) {
435     return allocCount(slotCount) * sizeof(HeapSlot);
436   }
437 
438   static ObjectSlots* fromSlots(HeapSlot* slots) {
439     MOZ_ASSERT(slots);
440     return reinterpret_cast<ObjectSlots*>(uintptr_t(slots) -
441                                           sizeof(ObjectSlots));
442   }
443 
444   static constexpr size_t offsetOfCapacity() {
445     return offsetof(ObjectSlots, capacity_);
446   }
447   static constexpr size_t offsetOfDictionarySlotSpan() {
448     return offsetof(ObjectSlots, dictionarySlotSpan_);
449   }
450   static constexpr size_t offsetOfSlots() { return sizeof(ObjectSlots); }
451   static constexpr int32_t offsetOfDictionarySlotSpanFromSlots() {
452     return int32_t(offsetOfDictionarySlotSpan()) - int32_t(offsetOfSlots());
453   }
454 
455   constexpr explicit ObjectSlots(uint32_t capacity, uint32_t dictionarySlotSpan)
456       : capacity_(capacity), dictionarySlotSpan_(dictionarySlotSpan) {}
457 
458   uint32_t capacity() const { return capacity_; }
459   uint32_t dictionarySlotSpan() const { return dictionarySlotSpan_; }
460 
461   void setDictionarySlotSpan(uint32_t span) { dictionarySlotSpan_ = span; }
462 
463   HeapSlot* slots() const {
464     return reinterpret_cast<HeapSlot*>(uintptr_t(this) + sizeof(ObjectSlots));
465   }
466 };
467 
468 /*
469  * Shared singletons for objects with no elements.
470  * emptyObjectElementsShared is used only for TypedArrays, when the TA
471  * maps shared memory.
472  */
473 extern HeapSlot* const emptyObjectElements;
474 extern HeapSlot* const emptyObjectElementsShared;
475 
476 /*
477  * Shared singletons for objects with no dynamic slots.
478  */
479 extern HeapSlot* const emptyObjectSlots;
480 extern HeapSlot* const emptyObjectSlotsForDictionaryObject[];
481 
482 class AutoCheckShapeConsistency;
483 class GCMarker;
484 class Shape;
485 
486 class NewObjectCache;
487 
488 // Operations which change an object's dense elements can either succeed, fail,
489 // or be unable to complete. The latter is used when the object's elements must
490 // become sparse instead. The enum below is used for such operations.
491 enum class DenseElementResult { Failure, Success, Incomplete };
492 
493 /*
494  * [SMDOC] NativeObject layout
495  *
496  * NativeObject specifies the internal implementation of a native object.
497  *
498  * Native objects use ShapedObject::shape to record property information. Two
499  * native objects with the same shape are guaranteed to have the same number of
500  * fixed slots.
501  *
502  * Native objects extend the base implementation of an object with storage for
503  * the object's named properties and indexed elements.
504  *
505  * These are stored separately from one another. Objects are followed by a
506  * variable-sized array of values for inline storage, which may be used by
507  * either properties of native objects (fixed slots), by elements (fixed
508  * elements), or by other data for certain kinds of objects, such as
509  * ArrayBufferObjects and TypedArrayObjects.
510  *
511  * Named property storage can be split between fixed slots and a dynamically
512  * allocated array (the slots member). For an object with N fixed slots, shapes
513  * with slots [0..N-1] are stored in the fixed slots, and the remainder are
514  * stored in the dynamic array. If all properties fit in the fixed slots, the
515  * 'slots_' member is nullptr.
516  *
517  * Elements are indexed via the 'elements_' member. This member can point to
518  * either the shared emptyObjectElements and emptyObjectElementsShared
519  * singletons, into the inline value array (the address of the third value, to
520  * leave room for a ObjectElements header;in this case numFixedSlots() is zero)
521  * or to a dynamically allocated array.
522  *
523  * Slots and elements may both be non-empty. The slots may be either names or
524  * indexes; no indexed property will be in both the slots and elements.
525  */
526 class NativeObject : public JSObject {
527  protected:
528   /* Slots for object properties. */
529   js::HeapSlot* slots_;
530 
531   /* Slots for object dense elements. */
532   js::HeapSlot* elements_;
533 
534   friend class ::JSObject;
535 
536  private:
staticAsserts()537   static void staticAsserts() {
538     static_assert(sizeof(NativeObject) == sizeof(JSObject_Slots0),
539                   "native object size must match GC thing size");
540     static_assert(sizeof(NativeObject) == sizeof(JS::shadow::Object),
541                   "shadow interface must match actual implementation");
542     static_assert(sizeof(NativeObject) % sizeof(Value) == 0,
543                   "fixed slots after an object must be aligned");
544 
545     static_assert(offsetOfShape() == offsetof(JS::shadow::Object, shape),
546                   "shadow type must match actual type");
547     static_assert(
548         offsetof(NativeObject, slots_) == offsetof(JS::shadow::Object, slots),
549         "shadow slots must match actual slots");
550     static_assert(
551         offsetof(NativeObject, elements_) == offsetof(JS::shadow::Object, _1),
552         "shadow placeholder must match actual elements");
553 
554     static_assert(MAX_FIXED_SLOTS <= Shape::FIXED_SLOTS_MAX,
555                   "verify numFixedSlots() bitfield is big enough");
556     static_assert(sizeof(NativeObject) + MAX_FIXED_SLOTS * sizeof(Value) ==
557                       JSObject::MAX_BYTE_SIZE,
558                   "inconsistent maximum object size");
559 
560     // Sanity check NativeObject size is what we expect.
561 #ifdef JS_64BIT
562     static_assert(sizeof(NativeObject) == 3 * sizeof(void*));
563 #else
564     static_assert(sizeof(NativeObject) == 4 * sizeof(void*));
565 #endif
566   }
567 
568  public:
getLastProperty()569   PropertyInfoWithKey getLastProperty() const {
570     return shape()->lastProperty();
571   }
572 
getDenseElements()573   HeapSlotArray getDenseElements() const { return HeapSlotArray(elements_); }
574 
getDenseElement(uint32_t idx)575   const Value& getDenseElement(uint32_t idx) const {
576     MOZ_ASSERT(idx < getDenseInitializedLength());
577     return elements_[idx];
578   }
containsDenseElement(uint32_t idx)579   bool containsDenseElement(uint32_t idx) {
580     return idx < getDenseInitializedLength() &&
581            !elements_[idx].isMagic(JS_ELEMENTS_HOLE);
582   }
getDenseInitializedLength()583   uint32_t getDenseInitializedLength() const {
584     return getElementsHeader()->initializedLength;
585   }
getDenseCapacity()586   uint32_t getDenseCapacity() const { return getElementsHeader()->capacity; }
587 
isSharedMemory()588   bool isSharedMemory() const { return getElementsHeader()->isSharedMemory(); }
589 
590   // Update the object's shape, keeping the number of allocated slots in sync
591   // with the object's new slot span.
592   MOZ_ALWAYS_INLINE bool setShapeAndUpdateSlots(JSContext* cx, Shape* newShape);
593 
594   // Optimized version of setShapeAndUpdateSlots for adding a single property
595   // with a slot.
596   MOZ_ALWAYS_INLINE bool setShapeAndUpdateSlotsForNewSlot(JSContext* cx,
597                                                           Shape* newShape,
598                                                           uint32_t slot);
599 
canReuseShapeForNewProperties(Shape * newShape)600   MOZ_ALWAYS_INLINE bool canReuseShapeForNewProperties(Shape* newShape) const {
601     if (shape()->numFixedSlots() != newShape->numFixedSlots()) {
602       return false;
603     }
604     if (shape()->isDictionary() || newShape->isDictionary()) {
605       return false;
606     }
607     if (shape()->base() != newShape->base()) {
608       return false;
609     }
610     MOZ_ASSERT(shape()->getObjectClass() == newShape->getObjectClass());
611     MOZ_ASSERT(shape()->proto() == newShape->proto());
612     MOZ_ASSERT(shape()->realm() == newShape->realm());
613     return shape()->objectFlags() == newShape->objectFlags();
614   }
615 
616   // Newly-created TypedArrays that map a SharedArrayBuffer are
617   // marked as shared by giving them an ObjectElements that has the
618   // ObjectElements::SHARED_MEMORY flag set.
setIsSharedMemory()619   void setIsSharedMemory() {
620     MOZ_ASSERT(elements_ == emptyObjectElements);
621     elements_ = emptyObjectElementsShared;
622   }
623 
624   inline bool isInWholeCellBuffer() const;
625 
626   static inline JS::Result<NativeObject*, JS::OOM> create(
627       JSContext* cx, gc::AllocKind kind, gc::InitialHeap heap,
628       HandleShape shape, gc::AllocSite* site = nullptr);
629 
630 #ifdef DEBUG
631   static void enableShapeConsistencyChecks();
632 #endif
633 
634  protected:
635 #ifdef DEBUG
636   friend class js::AutoCheckShapeConsistency;
637   void checkShapeConsistency();
638 #else
639   void checkShapeConsistency() {}
640 #endif
641 
642   /*
643    * Update the slot span directly for a dictionary object, and allocate
644    * slots to cover the new span if necessary.
645    */
646   bool ensureSlotsForDictionaryObject(JSContext* cx, uint32_t span);
647 
648   [[nodiscard]] static bool toDictionaryMode(JSContext* cx,
649                                              HandleNativeObject obj);
650 
651  private:
652   inline void setEmptyDynamicSlots(uint32_t dictonarySlotSpan);
653 
654   inline void setDictionaryModeSlotSpan(uint32_t span);
655 
656   friend class TenuringTracer;
657 
658   /*
659    * Get internal pointers to the range of values from |start| to |end|
660    * exclusive.
661    */
getSlotRangeUnchecked(uint32_t start,uint32_t end,HeapSlot ** fixedStart,HeapSlot ** fixedEnd,HeapSlot ** slotsStart,HeapSlot ** slotsEnd)662   void getSlotRangeUnchecked(uint32_t start, uint32_t end,
663                              HeapSlot** fixedStart, HeapSlot** fixedEnd,
664                              HeapSlot** slotsStart, HeapSlot** slotsEnd) {
665     MOZ_ASSERT(end >= start);
666 
667     uint32_t fixed = numFixedSlots();
668     if (start < fixed) {
669       if (end <= fixed) {
670         *fixedStart = &fixedSlots()[start];
671         *fixedEnd = &fixedSlots()[end];
672         *slotsStart = *slotsEnd = nullptr;
673       } else {
674         *fixedStart = &fixedSlots()[start];
675         *fixedEnd = &fixedSlots()[fixed];
676         *slotsStart = &slots_[0];
677         *slotsEnd = &slots_[end - fixed];
678       }
679     } else {
680       *fixedStart = *fixedEnd = nullptr;
681       *slotsStart = &slots_[start - fixed];
682       *slotsEnd = &slots_[end - fixed];
683     }
684   }
685 
getSlotRange(uint32_t start,uint32_t end,HeapSlot ** fixedStart,HeapSlot ** fixedEnd,HeapSlot ** slotsStart,HeapSlot ** slotsEnd)686   void getSlotRange(uint32_t start, uint32_t end, HeapSlot** fixedStart,
687                     HeapSlot** fixedEnd, HeapSlot** slotsStart,
688                     HeapSlot** slotsEnd) {
689     MOZ_ASSERT(slotInRange(end, SENTINEL_ALLOWED));
690     getSlotRangeUnchecked(start, end, fixedStart, fixedEnd, slotsStart,
691                           slotsEnd);
692   }
693 
694  protected:
695   friend class DictionaryPropMap;
696   friend class GCMarker;
697   friend class Shape;
698   friend class NewObjectCache;
699 
invalidateSlotRange(uint32_t start,uint32_t end)700   void invalidateSlotRange(uint32_t start, uint32_t end) {
701 #ifdef DEBUG
702     HeapSlot* fixedStart;
703     HeapSlot* fixedEnd;
704     HeapSlot* slotsStart;
705     HeapSlot* slotsEnd;
706     getSlotRange(start, end, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
707     Debug_SetSlotRangeToCrashOnTouch(fixedStart, fixedEnd);
708     Debug_SetSlotRangeToCrashOnTouch(slotsStart, slotsEnd);
709 #endif /* DEBUG */
710   }
711 
712   void initializeSlotRange(uint32_t start, uint32_t end);
713 
714   /*
715    * Initialize the object's slots from a flat array. The caller must ensure
716    * that there are enough slots. This is used during brain transplants.
717    */
718   void initSlots(const Value* vector, uint32_t length);
719 
720 #ifdef DEBUG
721   enum SentinelAllowed{SENTINEL_NOT_ALLOWED, SENTINEL_ALLOWED};
722 
723   /*
724    * Check that slot is in range for the object's allocated slots.
725    * If sentinelAllowed then slot may equal the slot capacity.
726    */
727   bool slotInRange(uint32_t slot,
728                    SentinelAllowed sentinel = SENTINEL_NOT_ALLOWED) const;
729 
730   /*
731    * Check whether a slot is a fixed slot.
732    */
733   bool slotIsFixed(uint32_t slot) const;
734 
735   /*
736    * Check whether the supplied number of fixed slots is correct.
737    */
738   bool isNumFixedSlots(uint32_t nfixed) const;
739 #endif
740 
741   /*
742    * Minimum size for dynamically allocated slots in normal Objects.
743    * ArrayObjects don't use this limit and can have a lower slot capacity,
744    * since they normally don't have a lot of slots.
745    */
746   static const uint32_t SLOT_CAPACITY_MIN = 8 - ObjectSlots::VALUES_PER_HEADER;
747 
fixedSlots()748   HeapSlot* fixedSlots() const {
749     return reinterpret_cast<HeapSlot*>(uintptr_t(this) + sizeof(NativeObject));
750   }
751 
752  public:
753   /* Object allocation may directly initialize slots so this is public. */
initSlots(HeapSlot * slots)754   void initSlots(HeapSlot* slots) {
755     MOZ_ASSERT(slots);
756     slots_ = slots;
757   }
758   inline void initEmptyDynamicSlots();
759 
760   [[nodiscard]] static bool generateNewDictionaryShape(JSContext* cx,
761                                                        HandleNativeObject obj);
762 
763   [[nodiscard]] static bool reshapeForShadowedProp(JSContext* cx,
764                                                    HandleNativeObject obj);
765 
766   // The maximum number of slots in an object.
767   // |MAX_SLOTS_COUNT * sizeof(JS::Value)| shouldn't overflow
768   // int32_t (see slotsSizeMustNotOverflow).
769   static const uint32_t MAX_SLOTS_COUNT = (1 << 28) - 1;
770 
slotsSizeMustNotOverflow()771   static void slotsSizeMustNotOverflow() {
772     static_assert(
773         NativeObject::MAX_SLOTS_COUNT <= INT32_MAX / sizeof(JS::Value),
774         "every caller of this method requires that a slot "
775         "number (or slot count) count multiplied by "
776         "sizeof(Value) can't overflow uint32_t (and sometimes "
777         "int32_t, too)");
778   }
779 
numFixedSlots()780   uint32_t numFixedSlots() const {
781     return reinterpret_cast<const JS::shadow::Object*>(this)->numFixedSlots();
782   }
783 
784   // Get the number of fixed slots when the shape pointer may have been
785   // forwarded by a moving GC. You need to use this rather that
786   // numFixedSlots() in a trace hook if you access an object that is not the
787   // object being traced, since it may have a stale shape pointer.
788   inline uint32_t numFixedSlotsMaybeForwarded() const;
789 
790   // Get the private when the ObjectGroup pointer may have been forwarded by a
791   // moving GC.
792   inline void* getPrivateMaybeForwarded() const;
793 
numUsedFixedSlots()794   uint32_t numUsedFixedSlots() const {
795     uint32_t nslots = shape()->slotSpan();
796     return std::min(nslots, numFixedSlots());
797   }
798 
slotSpan()799   uint32_t slotSpan() const {
800     if (inDictionaryMode()) {
801       return dictionaryModeSlotSpan();
802     }
803     MOZ_ASSERT(getSlotsHeader()->dictionarySlotSpan() == 0);
804     return shape()->slotSpan();
805   }
806 
dictionaryModeSlotSpan()807   uint32_t dictionaryModeSlotSpan() const {
808     MOZ_ASSERT(inDictionaryMode());
809     return getSlotsHeader()->dictionarySlotSpan();
810   }
811 
812   /* Whether a slot is at a fixed offset from this object. */
isFixedSlot(size_t slot)813   bool isFixedSlot(size_t slot) { return slot < numFixedSlots(); }
814 
815   /* Index into the dynamic slots array to use for a dynamic slot. */
dynamicSlotIndex(size_t slot)816   size_t dynamicSlotIndex(size_t slot) {
817     MOZ_ASSERT(slot >= numFixedSlots());
818     return slot - numFixedSlots();
819   }
820 
821   // Native objects are never proxies. Call isExtensible instead.
822   bool nonProxyIsExtensible() const = delete;
823 
isExtensible()824   bool isExtensible() const { return !hasFlag(ObjectFlag::NotExtensible); }
825 
826   /*
827    * Whether there may be indexed properties on this object, excluding any in
828    * the object's elements.
829    */
isIndexed()830   bool isIndexed() const { return hasFlag(ObjectFlag::Indexed); }
831 
hasInterestingSymbol()832   bool hasInterestingSymbol() const {
833     return hasFlag(ObjectFlag::HasInterestingSymbol);
834   }
835 
setHadGetterSetterChange(JSContext * cx,HandleNativeObject obj)836   static bool setHadGetterSetterChange(JSContext* cx, HandleNativeObject obj) {
837     return setFlag(cx, obj, ObjectFlag::HadGetterSetterChange);
838   }
hadGetterSetterChange()839   bool hadGetterSetterChange() const {
840     return hasFlag(ObjectFlag::HadGetterSetterChange);
841   }
842 
843   /*
844    * Grow or shrink slots immediately before changing the slot span.
845    * The number of allocated slots is not stored explicitly, and changes to
846    * the slots must track changes in the slot span.
847    */
848   bool growSlots(JSContext* cx, uint32_t oldCapacity, uint32_t newCapacity);
849   void shrinkSlots(JSContext* cx, uint32_t oldCapacity, uint32_t newCapacity);
850 
851   bool allocateSlots(JSContext* cx, uint32_t newCapacity);
852 
853   /*
854    * This method is static because it's called from JIT code. On OOM, returns
855    * false without leaving a pending exception on the context.
856    */
857   static bool growSlotsPure(JSContext* cx, NativeObject* obj,
858                             uint32_t newCapacity);
859 
860   /*
861    * Like growSlotsPure but for dense elements. This will return
862    * false if we failed to allocate a dense element for some reason (OOM, too
863    * many dense elements, non-writable array length, etc).
864    */
865   static bool addDenseElementPure(JSContext* cx, NativeObject* obj);
866 
hasDynamicSlots()867   bool hasDynamicSlots() const { return getSlotsHeader()->capacity(); }
868 
869   /* Compute the number of dynamic slots required for this object. */
870   MOZ_ALWAYS_INLINE uint32_t calculateDynamicSlots() const;
871 
872   MOZ_ALWAYS_INLINE uint32_t numDynamicSlots() const;
873 
empty()874   bool empty() const { return shape()->propMapLength() == 0; }
875 
876   mozilla::Maybe<PropertyInfo> lookup(JSContext* cx, jsid id);
lookup(JSContext * cx,PropertyName * name)877   mozilla::Maybe<PropertyInfo> lookup(JSContext* cx, PropertyName* name) {
878     return lookup(cx, NameToId(name));
879   }
880 
contains(JSContext * cx,jsid id)881   bool contains(JSContext* cx, jsid id) { return lookup(cx, id).isSome(); }
contains(JSContext * cx,PropertyName * name)882   bool contains(JSContext* cx, PropertyName* name) {
883     return lookup(cx, name).isSome();
884   }
contains(JSContext * cx,jsid id,PropertyInfo prop)885   bool contains(JSContext* cx, jsid id, PropertyInfo prop) {
886     mozilla::Maybe<PropertyInfo> found = lookup(cx, id);
887     return found.isSome() && *found == prop;
888   }
889 
890   /* Contextless; can be called from other pure code. */
891   mozilla::Maybe<PropertyInfo> lookupPure(jsid id);
lookupPure(PropertyName * name)892   mozilla::Maybe<PropertyInfo> lookupPure(PropertyName* name) {
893     return lookupPure(NameToId(name));
894   }
895 
containsPure(jsid id)896   bool containsPure(jsid id) { return lookupPure(id).isSome(); }
containsPure(PropertyName * name)897   bool containsPure(PropertyName* name) { return containsPure(NameToId(name)); }
containsPure(jsid id,PropertyInfo prop)898   bool containsPure(jsid id, PropertyInfo prop) {
899     mozilla::Maybe<PropertyInfo> found = lookupPure(id);
900     return found.isSome() && *found == prop;
901   }
902 
903  private:
904   /*
905    * Allocate and free an object slot.
906    *
907    * FIXME: bug 593129 -- slot allocation should be done by object methods
908    * after calling object-parameter-free shape methods, avoiding coupling
909    * logic across the object vs. shape module wall.
910    */
911   static bool allocDictionarySlot(JSContext* cx, HandleNativeObject obj,
912                                   uint32_t* slotp);
913 
914   void freeDictionarySlot(uint32_t slot);
915 
916   static MOZ_ALWAYS_INLINE bool maybeConvertToDictionaryForAdd(
917       JSContext* cx, HandleNativeObject obj);
918 
919  public:
920   // Add a new property. Must only be used when the |id| is not already present
921   // in the object's shape. Checks for non-extensibility must be done by the
922   // callers.
923   static bool addProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
924                           PropertyFlags flags, uint32_t* slotOut);
925 
addProperty(JSContext * cx,HandleNativeObject obj,HandlePropertyName name,PropertyFlags flags,uint32_t * slotOut)926   static bool addProperty(JSContext* cx, HandleNativeObject obj,
927                           HandlePropertyName name, PropertyFlags flags,
928                           uint32_t* slotOut) {
929     RootedId id(cx, NameToId(name));
930     return addProperty(cx, obj, id, flags, slotOut);
931   }
932 
933   static bool addPropertyInReservedSlot(JSContext* cx, HandleNativeObject obj,
934                                         HandleId id, uint32_t slot,
935                                         PropertyFlags flags);
addPropertyInReservedSlot(JSContext * cx,HandleNativeObject obj,HandlePropertyName name,uint32_t slot,PropertyFlags flags)936   static bool addPropertyInReservedSlot(JSContext* cx, HandleNativeObject obj,
937                                         HandlePropertyName name, uint32_t slot,
938                                         PropertyFlags flags) {
939     RootedId id(cx, NameToId(name));
940     return addPropertyInReservedSlot(cx, obj, id, slot, flags);
941   }
942 
943   static bool addCustomDataProperty(JSContext* cx, HandleNativeObject obj,
944                                     HandleId id, PropertyFlags flags);
945 
946   // Change a property with key |id| in this object. The object must already
947   // have a property (stored in the shape tree) with this |id|.
948   static bool changeProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
949                              PropertyFlags flags, uint32_t* slotOut);
950 
951   static bool changeCustomDataPropAttributes(JSContext* cx,
952                                              HandleNativeObject obj,
953                                              HandleId id, PropertyFlags flags);
954 
955   // Remove the property named by id from this object.
956   static bool removeProperty(JSContext* cx, HandleNativeObject obj,
957                              HandleId id);
958 
959   static bool freezeOrSealProperties(JSContext* cx, HandleNativeObject obj,
960                                      IntegrityLevel level);
961 
962  protected:
963   static bool changeNumFixedSlotsAfterSwap(JSContext* cx,
964                                            HandleNativeObject obj,
965                                            uint32_t nfixed);
966 
967   [[nodiscard]] static bool fillInAfterSwap(JSContext* cx,
968                                             HandleNativeObject obj,
969                                             NativeObject* old,
970                                             HandleValueVector values,
971                                             void* priv);
972 
973  public:
974   // Return true if this object has been converted from shared-immutable
975   // shapes to object-owned dictionary shapes.
inDictionaryMode()976   bool inDictionaryMode() const { return shape()->isDictionary(); }
977 
getSlot(uint32_t slot)978   const Value& getSlot(uint32_t slot) const {
979     MOZ_ASSERT(slotInRange(slot));
980     uint32_t fixed = numFixedSlots();
981     if (slot < fixed) {
982       return fixedSlots()[slot];
983     }
984     return slots_[slot - fixed];
985   }
986 
getSlotAddressUnchecked(uint32_t slot)987   const HeapSlot* getSlotAddressUnchecked(uint32_t slot) const {
988     uint32_t fixed = numFixedSlots();
989     if (slot < fixed) {
990       return fixedSlots() + slot;
991     }
992     return slots_ + (slot - fixed);
993   }
994 
getSlotAddressUnchecked(uint32_t slot)995   HeapSlot* getSlotAddressUnchecked(uint32_t slot) {
996     uint32_t fixed = numFixedSlots();
997     if (slot < fixed) {
998       return fixedSlots() + slot;
999     }
1000     return slots_ + (slot - fixed);
1001   }
1002 
getSlotAddress(uint32_t slot)1003   HeapSlot* getSlotAddress(uint32_t slot) {
1004     /*
1005      * This can be used to get the address of the end of the slots for the
1006      * object, which may be necessary when fetching zero-length arrays of
1007      * slots (e.g. for callObjVarArray).
1008      */
1009     MOZ_ASSERT(slotInRange(slot, SENTINEL_ALLOWED));
1010     return getSlotAddressUnchecked(slot);
1011   }
1012 
getSlotAddress(uint32_t slot)1013   const HeapSlot* getSlotAddress(uint32_t slot) const {
1014     /*
1015      * This can be used to get the address of the end of the slots for the
1016      * object, which may be necessary when fetching zero-length arrays of
1017      * slots (e.g. for callObjVarArray).
1018      */
1019     MOZ_ASSERT(slotInRange(slot, SENTINEL_ALLOWED));
1020     return getSlotAddressUnchecked(slot);
1021   }
1022 
getSlotRef(uint32_t slot)1023   MOZ_ALWAYS_INLINE HeapSlot& getSlotRef(uint32_t slot) {
1024     MOZ_ASSERT(slotInRange(slot));
1025     return *getSlotAddress(slot);
1026   }
1027 
getSlotRef(uint32_t slot)1028   MOZ_ALWAYS_INLINE const HeapSlot& getSlotRef(uint32_t slot) const {
1029     MOZ_ASSERT(slotInRange(slot));
1030     return *getSlotAddress(slot);
1031   }
1032 
1033   // Check requirements on values stored to this object.
checkStoredValue(const Value & v)1034   MOZ_ALWAYS_INLINE void checkStoredValue(const Value& v) {
1035     MOZ_ASSERT(IsObjectValueInCompartment(v, compartment()));
1036     MOZ_ASSERT(AtomIsMarked(zoneFromAnyThread(), v));
1037     MOZ_ASSERT_IF(v.isMagic() && v.whyMagic() == JS_ELEMENTS_HOLE,
1038                   !denseElementsArePacked());
1039   }
1040 
setSlot(uint32_t slot,const Value & value)1041   MOZ_ALWAYS_INLINE void setSlot(uint32_t slot, const Value& value) {
1042     MOZ_ASSERT(slotInRange(slot));
1043     checkStoredValue(value);
1044     getSlotRef(slot).set(this, HeapSlot::Slot, slot, value);
1045   }
1046 
initSlot(uint32_t slot,const Value & value)1047   MOZ_ALWAYS_INLINE void initSlot(uint32_t slot, const Value& value) {
1048     MOZ_ASSERT(getSlot(slot).isUndefined());
1049     MOZ_ASSERT(slotInRange(slot));
1050     checkStoredValue(value);
1051     initSlotUnchecked(slot, value);
1052   }
1053 
initSlotUnchecked(uint32_t slot,const Value & value)1054   MOZ_ALWAYS_INLINE void initSlotUnchecked(uint32_t slot, const Value& value) {
1055     getSlotAddressUnchecked(slot)->init(this, HeapSlot::Slot, slot, value);
1056   }
1057 
1058   // Returns the GetterSetter for an accessor property.
getGetterSetter(uint32_t slot)1059   GetterSetter* getGetterSetter(uint32_t slot) const {
1060     return getSlot(slot).toGCThing()->as<GetterSetter>();
1061   }
getGetterSetter(PropertyInfo prop)1062   GetterSetter* getGetterSetter(PropertyInfo prop) const {
1063     MOZ_ASSERT(prop.isAccessorProperty());
1064     return getGetterSetter(prop.slot());
1065   }
1066 
1067   // Returns the (possibly nullptr) getter or setter object. |prop| and |slot|
1068   // must be (for) an accessor property.
getGetter(uint32_t slot)1069   JSObject* getGetter(uint32_t slot) const {
1070     return getGetterSetter(slot)->getter();
1071   }
getGetter(PropertyInfo prop)1072   JSObject* getGetter(PropertyInfo prop) const {
1073     return getGetterSetter(prop)->getter();
1074   }
getSetter(PropertyInfo prop)1075   JSObject* getSetter(PropertyInfo prop) const {
1076     return getGetterSetter(prop)->setter();
1077   }
1078 
1079   // Returns true if the property has a non-nullptr getter or setter object.
1080   // |prop| can be any property.
hasGetter(PropertyInfo prop)1081   bool hasGetter(PropertyInfo prop) const {
1082     return prop.isAccessorProperty() && getGetter(prop);
1083   }
hasSetter(PropertyInfo prop)1084   bool hasSetter(PropertyInfo prop) const {
1085     return prop.isAccessorProperty() && getSetter(prop);
1086   }
1087 
1088   // If the property has a non-nullptr getter/setter, return it as ObjectValue.
1089   // Else return |undefined|. |prop| must be an accessor property.
getGetterValue(PropertyInfo prop)1090   Value getGetterValue(PropertyInfo prop) const {
1091     MOZ_ASSERT(prop.isAccessorProperty());
1092     if (JSObject* getterObj = getGetter(prop)) {
1093       return ObjectValue(*getterObj);
1094     }
1095     return UndefinedValue();
1096   }
getSetterValue(PropertyInfo prop)1097   Value getSetterValue(PropertyInfo prop) const {
1098     MOZ_ASSERT(prop.isAccessorProperty());
1099     if (JSObject* setterObj = getSetter(prop)) {
1100       return ObjectValue(*setterObj);
1101     }
1102     return UndefinedValue();
1103   }
1104 
1105   // MAX_FIXED_SLOTS is the biggest number of fixed slots our GC
1106   // size classes will give an object.
1107   static constexpr uint32_t MAX_FIXED_SLOTS =
1108       JS::shadow::Object::MAX_FIXED_SLOTS;
1109 
1110  protected:
1111   MOZ_ALWAYS_INLINE bool updateSlotsForSpan(JSContext* cx, size_t oldSpan,
1112                                             size_t newSpan);
1113 
1114  private:
prepareElementRangeForOverwrite(size_t start,size_t end)1115   void prepareElementRangeForOverwrite(size_t start, size_t end) {
1116     MOZ_ASSERT(end <= getDenseInitializedLength());
1117     for (size_t i = start; i < end; i++) {
1118       elements_[i].destroy();
1119     }
1120   }
1121 
1122   /*
1123    * Trigger the write barrier on a range of slots that will no longer be
1124    * reachable.
1125    */
prepareSlotRangeForOverwrite(size_t start,size_t end)1126   void prepareSlotRangeForOverwrite(size_t start, size_t end) {
1127     for (size_t i = start; i < end; i++) {
1128       getSlotAddressUnchecked(i)->destroy();
1129     }
1130   }
1131 
1132   inline void shiftDenseElementsUnchecked(uint32_t count);
1133 
1134  public:
getReservedSlot(uint32_t index)1135   MOZ_ALWAYS_INLINE const Value& getReservedSlot(uint32_t index) const {
1136     MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
1137     return getSlot(index);
1138   }
1139 
getReservedSlotRef(uint32_t index)1140   MOZ_ALWAYS_INLINE const HeapSlot& getReservedSlotRef(uint32_t index) const {
1141     MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
1142     return getSlotRef(index);
1143   }
1144 
getReservedSlotRef(uint32_t index)1145   MOZ_ALWAYS_INLINE HeapSlot& getReservedSlotRef(uint32_t index) {
1146     MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
1147     return getSlotRef(index);
1148   }
1149 
initReservedSlot(uint32_t index,const Value & v)1150   MOZ_ALWAYS_INLINE void initReservedSlot(uint32_t index, const Value& v) {
1151     MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
1152     initSlot(index, v);
1153   }
1154 
setReservedSlot(uint32_t index,const Value & v)1155   MOZ_ALWAYS_INLINE void setReservedSlot(uint32_t index, const Value& v) {
1156     MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
1157     setSlot(index, v);
1158   }
1159 
1160   // For slots which are known to always be fixed, due to the way they are
1161   // allocated.
1162 
getFixedSlotRef(uint32_t slot)1163   HeapSlot& getFixedSlotRef(uint32_t slot) {
1164     MOZ_ASSERT(slotIsFixed(slot));
1165     return fixedSlots()[slot];
1166   }
1167 
getFixedSlot(uint32_t slot)1168   const Value& getFixedSlot(uint32_t slot) const {
1169     MOZ_ASSERT(slotIsFixed(slot));
1170     return fixedSlots()[slot];
1171   }
1172 
setFixedSlot(uint32_t slot,const Value & value)1173   void setFixedSlot(uint32_t slot, const Value& value) {
1174     MOZ_ASSERT(slotIsFixed(slot));
1175     checkStoredValue(value);
1176     fixedSlots()[slot].set(this, HeapSlot::Slot, slot, value);
1177   }
1178 
initFixedSlot(uint32_t slot,const Value & value)1179   void initFixedSlot(uint32_t slot, const Value& value) {
1180     MOZ_ASSERT(slotIsFixed(slot));
1181     checkStoredValue(value);
1182     fixedSlots()[slot].init(this, HeapSlot::Slot, slot, value);
1183   }
1184 
1185   /*
1186    * Calculate the number of dynamic slots to allocate to cover the properties
1187    * in an object with the given number of fixed slots and slot span.
1188    */
1189   static MOZ_ALWAYS_INLINE uint32_t calculateDynamicSlots(uint32_t nfixed,
1190                                                           uint32_t span,
1191                                                           const JSClass* clasp);
1192   static MOZ_ALWAYS_INLINE uint32_t calculateDynamicSlots(Shape* shape);
1193 
getSlotsHeader()1194   ObjectSlots* getSlotsHeader() const { return ObjectSlots::fromSlots(slots_); }
1195 
1196   /* Elements accessors. */
1197 
1198   // The maximum size, in sizeof(Value), of the allocation used for an
1199   // object's dense elements.  (This includes space used to store an
1200   // ObjectElements instance.)
1201   // |MAX_DENSE_ELEMENTS_ALLOCATION * sizeof(JS::Value)| shouldn't overflow
1202   // int32_t (see elementsSizeMustNotOverflow).
1203   static const uint32_t MAX_DENSE_ELEMENTS_ALLOCATION = (1 << 28) - 1;
1204 
1205   // The maximum number of usable dense elements in an object.
1206   static const uint32_t MAX_DENSE_ELEMENTS_COUNT =
1207       MAX_DENSE_ELEMENTS_ALLOCATION - ObjectElements::VALUES_PER_HEADER;
1208 
elementsSizeMustNotOverflow()1209   static void elementsSizeMustNotOverflow() {
1210     static_assert(
1211         NativeObject::MAX_DENSE_ELEMENTS_COUNT <= INT32_MAX / sizeof(JS::Value),
1212         "every caller of this method require that an element "
1213         "count multiplied by sizeof(Value) can't overflow "
1214         "uint32_t (and sometimes int32_t ,too)");
1215   }
1216 
getElementsHeader()1217   ObjectElements* getElementsHeader() const {
1218     return ObjectElements::fromElements(elements_);
1219   }
1220 
1221   // Returns a pointer to the first element, including shifted elements.
unshiftedElements()1222   inline HeapSlot* unshiftedElements() const {
1223     return elements_ - getElementsHeader()->numShiftedElements();
1224   }
1225 
1226   // Like getElementsHeader, but returns a pointer to the unshifted header.
1227   // This is mainly useful for free()ing dynamic elements: the pointer
1228   // returned here is the one we got from malloc.
getUnshiftedElementsHeader()1229   void* getUnshiftedElementsHeader() const {
1230     return ObjectElements::fromElements(unshiftedElements());
1231   }
1232 
unshiftedIndex(uint32_t index)1233   uint32_t unshiftedIndex(uint32_t index) const {
1234     return index + getElementsHeader()->numShiftedElements();
1235   }
1236 
1237   /* Accessors for elements. */
ensureElements(JSContext * cx,uint32_t capacity)1238   bool ensureElements(JSContext* cx, uint32_t capacity) {
1239     MOZ_ASSERT(isExtensible());
1240     if (capacity > getDenseCapacity()) {
1241       return growElements(cx, capacity);
1242     }
1243     return true;
1244   }
1245 
1246   // Try to shift |count| dense elements, see the "Shifted elements" comment.
1247   inline bool tryShiftDenseElements(uint32_t count);
1248 
1249   // Try to make space for |count| dense elements at the start of the array.
1250   bool tryUnshiftDenseElements(uint32_t count);
1251 
1252   // Move the elements header and all shifted elements to the start of the
1253   // allocated elements space, so that numShiftedElements is 0 afterwards.
1254   void moveShiftedElements();
1255 
1256   // If this object has many shifted elements call moveShiftedElements.
1257   void maybeMoveShiftedElements();
1258 
1259   static bool goodElementsAllocationAmount(JSContext* cx, uint32_t reqAllocated,
1260                                            uint32_t length,
1261                                            uint32_t* goodAmount);
1262   bool growElements(JSContext* cx, uint32_t newcap);
1263   void shrinkElements(JSContext* cx, uint32_t cap);
1264 
1265  private:
1266   // Run a post write barrier that encompasses multiple contiguous elements in a
1267   // single step.
1268   inline void elementsRangePostWriteBarrier(uint32_t start, uint32_t count);
1269 
1270  public:
1271   void shrinkCapacityToInitializedLength(JSContext* cx);
1272 
1273  private:
setDenseInitializedLengthInternal(uint32_t length)1274   void setDenseInitializedLengthInternal(uint32_t length) {
1275     MOZ_ASSERT(length <= getDenseCapacity());
1276     MOZ_ASSERT(!denseElementsAreFrozen());
1277     prepareElementRangeForOverwrite(length,
1278                                     getElementsHeader()->initializedLength);
1279     getElementsHeader()->initializedLength = length;
1280   }
1281 
1282  public:
setDenseInitializedLength(uint32_t length)1283   void setDenseInitializedLength(uint32_t length) {
1284     MOZ_ASSERT(isExtensible());
1285     setDenseInitializedLengthInternal(length);
1286   }
1287 
setDenseInitializedLengthMaybeNonExtensible(JSContext * cx,uint32_t length)1288   void setDenseInitializedLengthMaybeNonExtensible(JSContext* cx,
1289                                                    uint32_t length) {
1290     setDenseInitializedLengthInternal(length);
1291     if (!isExtensible()) {
1292       shrinkCapacityToInitializedLength(cx);
1293     }
1294   }
1295 
1296   inline void ensureDenseInitializedLength(uint32_t index, uint32_t extra);
1297 
setDenseElement(uint32_t index,const Value & val)1298   void setDenseElement(uint32_t index, const Value& val) {
1299     // Note: Streams code can call this for the internal ListObject type with
1300     // MagicValue(JS_WRITABLESTREAM_CLOSE_RECORD).
1301     MOZ_ASSERT_IF(val.isMagic(), val.whyMagic() != JS_ELEMENTS_HOLE);
1302     setDenseElementUnchecked(index, val);
1303   }
1304 
initDenseElement(uint32_t index,const Value & val)1305   void initDenseElement(uint32_t index, const Value& val) {
1306     MOZ_ASSERT(!val.isMagic(JS_ELEMENTS_HOLE));
1307     initDenseElementUnchecked(index, val);
1308   }
1309 
1310  private:
1311   // Note: 'Unchecked' here means we don't assert |val| isn't the hole
1312   // MagicValue.
initDenseElementUnchecked(uint32_t index,const Value & val)1313   void initDenseElementUnchecked(uint32_t index, const Value& val) {
1314     MOZ_ASSERT(index < getDenseInitializedLength());
1315     MOZ_ASSERT(isExtensible());
1316     checkStoredValue(val);
1317     elements_[index].init(this, HeapSlot::Element, unshiftedIndex(index), val);
1318   }
setDenseElementUnchecked(uint32_t index,const Value & val)1319   void setDenseElementUnchecked(uint32_t index, const Value& val) {
1320     MOZ_ASSERT(index < getDenseInitializedLength());
1321     MOZ_ASSERT(!denseElementsAreFrozen());
1322     checkStoredValue(val);
1323     elements_[index].set(this, HeapSlot::Element, unshiftedIndex(index), val);
1324   }
1325 
1326   // Mark the dense elements as possibly containing holes.
1327   inline void markDenseElementsNotPacked();
1328 
1329  public:
1330   inline void initDenseElementHole(uint32_t index);
1331   inline void setDenseElementHole(uint32_t index);
1332   inline void removeDenseElementForSparseIndex(uint32_t index);
1333 
1334   inline void copyDenseElements(uint32_t dstStart, const Value* src,
1335                                 uint32_t count);
1336 
1337   inline void initDenseElements(const Value* src, uint32_t count);
1338   inline void initDenseElements(NativeObject* src, uint32_t srcStart,
1339                                 uint32_t count);
1340 
1341   // Store the Values in the range [begin, end) as elements of this array.
1342   //
1343   // Preconditions: This must be a boring ArrayObject with dense initialized
1344   // length 0: no shifted elements, no frozen elements, no fixed "length", not
1345   // indexed, not inextensible, not copy-on-write. Existing capacity is
1346   // optional.
1347   //
1348   // This runs write barriers but does not update types. `end - begin` must
1349   // return the size of the range, which must be >= 0 and fit in an int32_t.
1350   template <typename Iter>
1351   [[nodiscard]] inline bool initDenseElementsFromRange(JSContext* cx,
1352                                                        Iter begin, Iter end);
1353 
1354   inline void moveDenseElements(uint32_t dstStart, uint32_t srcStart,
1355                                 uint32_t count);
1356   inline void reverseDenseElementsNoPreBarrier(uint32_t length);
1357 
1358   inline DenseElementResult setOrExtendDenseElements(JSContext* cx,
1359                                                      uint32_t start,
1360                                                      const Value* vp,
1361                                                      uint32_t count);
1362 
denseElementsAreSealed()1363   bool denseElementsAreSealed() const {
1364     return getElementsHeader()->isSealed();
1365   }
denseElementsAreFrozen()1366   bool denseElementsAreFrozen() const {
1367     return hasFlag(ObjectFlag::FrozenElements);
1368   }
1369 
denseElementsArePacked()1370   bool denseElementsArePacked() const {
1371     return getElementsHeader()->isPacked();
1372   }
1373 
markDenseElementsMaybeInIteration()1374   void markDenseElementsMaybeInIteration() {
1375     getElementsHeader()->markMaybeInIteration();
1376   }
1377 
1378   // Return whether the object's dense elements might be in the midst of for-in
1379   // iteration. We rely on this to be able to safely delete or move dense array
1380   // elements without worrying about updating in-progress iterators.
1381   // See bug 690622.
1382   //
1383   // Note that it's fine to return false if this object is on the prototype of
1384   // another object: SuppressDeletedProperty only suppresses properties deleted
1385   // from the iterated object itself.
1386   inline bool denseElementsHaveMaybeInIterationFlag();
1387   inline bool denseElementsMaybeInIteration();
1388 
1389   // Ensures that the object can hold at least index + extra elements. This
1390   // returns DenseElement_Success on success, DenseElement_Failed on failure
1391   // to grow the array, or DenseElement_Incomplete when the object is too
1392   // sparse to grow (this includes the case of index + extra overflow). In
1393   // the last two cases the object is kept intact.
1394   inline DenseElementResult ensureDenseElements(JSContext* cx, uint32_t index,
1395                                                 uint32_t extra);
1396 
1397   inline DenseElementResult extendDenseElements(JSContext* cx,
1398                                                 uint32_t requiredCapacity,
1399                                                 uint32_t extra);
1400 
1401   /* Small objects are dense, no matter what. */
1402   static const uint32_t MIN_SPARSE_INDEX = 1000;
1403 
1404   /*
1405    * Element storage for an object will be sparse if fewer than 1/8 indexes
1406    * are filled in.
1407    */
1408   static const unsigned SPARSE_DENSITY_RATIO = 8;
1409 
1410   /*
1411    * Check if after growing the object's elements will be too sparse.
1412    * newElementsHint is an estimated number of elements to be added.
1413    */
1414   bool willBeSparseElements(uint32_t requiredCapacity,
1415                             uint32_t newElementsHint);
1416 
1417   /*
1418    * After adding a sparse index to obj, see if it should be converted to use
1419    * dense elements.
1420    */
1421   static DenseElementResult maybeDensifySparseElements(JSContext* cx,
1422                                                        HandleNativeObject obj);
1423   static bool densifySparseElements(JSContext* cx, HandleNativeObject obj);
1424 
fixedElements()1425   inline HeapSlot* fixedElements() const {
1426     static_assert(2 * sizeof(Value) == sizeof(ObjectElements),
1427                   "when elements are stored inline, the first two "
1428                   "slots will hold the ObjectElements header");
1429     return &fixedSlots()[2];
1430   }
1431 
1432 #ifdef DEBUG
1433   bool canHaveNonEmptyElements();
1434 #endif
1435 
setEmptyElements()1436   void setEmptyElements() { elements_ = emptyObjectElements; }
1437 
1438   void setFixedElements(uint32_t numShifted = 0) {
1439     MOZ_ASSERT(canHaveNonEmptyElements());
1440     elements_ = fixedElements() + numShifted;
1441   }
1442 
hasDynamicElements()1443   inline bool hasDynamicElements() const {
1444     /*
1445      * Note: for objects with zero fixed slots this could potentially give
1446      * a spurious 'true' result, if the end of this object is exactly
1447      * aligned with the end of its arena and dynamic slots are allocated
1448      * immediately afterwards. Such cases cannot occur for dense arrays
1449      * (which have at least two fixed slots) and can only result in a leak.
1450      */
1451     return !hasEmptyElements() && !hasFixedElements();
1452   }
1453 
hasFixedElements()1454   inline bool hasFixedElements() const {
1455     return unshiftedElements() == fixedElements();
1456   }
1457 
hasEmptyElements()1458   inline bool hasEmptyElements() const {
1459     return elements_ == emptyObjectElements ||
1460            elements_ == emptyObjectElementsShared;
1461   }
1462 
1463   /*
1464    * Get a pointer to the unused data in the object's allocation immediately
1465    * following this object, for use with objects which allocate a larger size
1466    * class than they need and store non-elements data inline.
1467    */
1468   inline uint8_t* fixedData(size_t nslots) const;
1469 
1470   inline void privatePreWriteBarrier(HeapSlot* pprivate);
1471 
1472   /* Private data accessors. */
1473 
1474  private:
privateRef(uint32_t nfixed)1475   HeapSlot& privateRef(uint32_t nfixed) const {
1476     /*
1477      * The private field of an object is used to hold a pointer by storing it as
1478      * a PrivateValue(). Private fields are stored immediately after the last
1479      * fixed slot of the object.
1480      */
1481     MOZ_ASSERT(isNumFixedSlots(nfixed));
1482     MOZ_ASSERT(hasPrivate());
1483     return fixedSlots()[nfixed];
1484   }
1485 
1486  public:
hasPrivate()1487   bool hasPrivate() const { return getClass()->hasPrivate(); }
1488 
getPrivate()1489   void* getPrivate() const { return getPrivate(numFixedSlots()); }
getPrivate(uint32_t nfixed)1490   void* getPrivate(uint32_t nfixed) const {
1491     return privateRef(nfixed).toPrivate();
1492   }
1493 
initPrivate(void * data)1494   void initPrivate(void* data) {
1495     uint32_t nfixed = numFixedSlots();
1496     privateRef(nfixed).unbarrieredSet(PrivateValue(data));
1497   }
1498 
setPrivate(void * data)1499   void setPrivate(void* data) { setPrivate(numFixedSlots(), data); }
setPrivate(uint32_t nfixed,void * data)1500   void setPrivate(uint32_t nfixed, void* data) {
1501     HeapSlot* pprivate = &privateRef(nfixed);
1502     privatePreWriteBarrier(pprivate);
1503     setPrivateUnbarriered(nfixed, data);
1504   }
1505 
setPrivateGCThing(gc::Cell * cell)1506   void setPrivateGCThing(gc::Cell* cell) {
1507 #ifdef DEBUG
1508     if (IsMarkedBlack(this)) {
1509       JS::AssertCellIsNotGray(cell);
1510     }
1511 #endif
1512     uint32_t nfixed = numFixedSlots();
1513     HeapSlot* pprivate = &privateRef(nfixed);
1514     Cell* prev = static_cast<gc::Cell*>(pprivate->toPrivate());
1515     privatePreWriteBarrier(pprivate);
1516     setPrivateUnbarriered(nfixed, cell);
1517     gc::PostWriteBarrierCell(this, prev, cell);
1518   }
1519 
setPrivateUnbarriered(void * data)1520   void setPrivateUnbarriered(void* data) {
1521     setPrivateUnbarriered(numFixedSlots(), data);
1522   }
setPrivateUnbarriered(uint32_t nfixed,void * data)1523   void setPrivateUnbarriered(uint32_t nfixed, void* data) {
1524     privateRef(nfixed).unbarrieredSet(PrivateValue(data));
1525   }
1526 
1527   /* Return the allocKind we would use if we were to tenure this object. */
1528   inline js::gc::AllocKind allocKindForTenure() const;
1529 
1530   // Native objects are never wrappers, so a native object always has a realm
1531   // and global.
realm()1532   JS::Realm* realm() const { return nonCCWRealm(); }
1533   inline js::GlobalObject& global() const;
1534 
1535   /* JIT Accessors */
offsetOfElements()1536   static size_t offsetOfElements() { return offsetof(NativeObject, elements_); }
offsetOfFixedElements()1537   static size_t offsetOfFixedElements() {
1538     return sizeof(NativeObject) + sizeof(ObjectElements);
1539   }
1540 
getFixedSlotOffset(size_t slot)1541   static constexpr size_t getFixedSlotOffset(size_t slot) {
1542     MOZ_ASSERT(slot < MAX_FIXED_SLOTS);
1543     return sizeof(NativeObject) + slot * sizeof(Value);
1544   }
getPrivateDataOffset(size_t nfixed)1545   static constexpr size_t getPrivateDataOffset(size_t nfixed) {
1546     return getFixedSlotOffset(nfixed);
1547   }
getFixedSlotIndexFromOffset(size_t offset)1548   static constexpr size_t getFixedSlotIndexFromOffset(size_t offset) {
1549     MOZ_ASSERT(offset >= sizeof(NativeObject));
1550     offset -= sizeof(NativeObject);
1551     MOZ_ASSERT(offset % sizeof(Value) == 0);
1552     MOZ_ASSERT(offset / sizeof(Value) < MAX_FIXED_SLOTS);
1553     return offset / sizeof(Value);
1554   }
getDynamicSlotIndexFromOffset(size_t offset)1555   static constexpr size_t getDynamicSlotIndexFromOffset(size_t offset) {
1556     MOZ_ASSERT(offset % sizeof(Value) == 0);
1557     return offset / sizeof(Value);
1558   }
offsetOfSlots()1559   static size_t offsetOfSlots() { return offsetof(NativeObject, slots_); }
1560 };
1561 
privatePreWriteBarrier(HeapSlot * pprivate)1562 inline void NativeObject::privatePreWriteBarrier(HeapSlot* pprivate) {
1563   JS::shadow::Zone* shadowZone = this->shadowZoneFromAnyThread();
1564   if (shadowZone->needsIncrementalBarrier() && pprivate->get().toPrivate() &&
1565       getClass()->hasTrace()) {
1566     getClass()->doTrace(shadowZone->barrierTracer(), this);
1567   }
1568 }
1569 
1570 /*** Standard internal methods **********************************************/
1571 
1572 /*
1573  * These functions should follow the algorithms in ES6 draft rev 29 section 9.1
1574  * ("Ordinary Object Internal Methods"). It's an ongoing project.
1575  *
1576  * Many native objects are not "ordinary" in ES6, so these functions also have
1577  * to serve some of the special needs of Functions (9.2, 9.3, 9.4.1), Arrays
1578  * (9.4.2), Strings (9.4.3), and so on.
1579  */
1580 
1581 extern bool NativeDefineProperty(JSContext* cx, HandleNativeObject obj,
1582                                  HandleId id,
1583                                  Handle<JS::PropertyDescriptor> desc,
1584                                  ObjectOpResult& result);
1585 
1586 extern bool NativeDefineDataProperty(JSContext* cx, HandleNativeObject obj,
1587                                      HandleId id, HandleValue value,
1588                                      unsigned attrs, ObjectOpResult& result);
1589 
1590 /* If the result out-param is omitted, throw on failure. */
1591 
1592 extern bool NativeDefineAccessorProperty(JSContext* cx, HandleNativeObject obj,
1593                                          HandleId id, HandleObject getter,
1594                                          HandleObject setter, unsigned attrs);
1595 
1596 extern bool NativeDefineDataProperty(JSContext* cx, HandleNativeObject obj,
1597                                      HandleId id, HandleValue value,
1598                                      unsigned attrs);
1599 
1600 extern bool NativeDefineDataProperty(JSContext* cx, HandleNativeObject obj,
1601                                      PropertyName* name, HandleValue value,
1602                                      unsigned attrs);
1603 
1604 extern bool NativeHasProperty(JSContext* cx, HandleNativeObject obj,
1605                               HandleId id, bool* foundp);
1606 
1607 extern bool NativeGetOwnPropertyDescriptor(
1608     JSContext* cx, HandleNativeObject obj, HandleId id,
1609     MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
1610 
1611 extern bool NativeGetProperty(JSContext* cx, HandleNativeObject obj,
1612                               HandleValue receiver, HandleId id,
1613                               MutableHandleValue vp);
1614 
1615 extern bool NativeGetPropertyNoGC(JSContext* cx, NativeObject* obj,
1616                                   const Value& receiver, jsid id, Value* vp);
1617 
NativeGetProperty(JSContext * cx,HandleNativeObject obj,HandleId id,MutableHandleValue vp)1618 inline bool NativeGetProperty(JSContext* cx, HandleNativeObject obj,
1619                               HandleId id, MutableHandleValue vp) {
1620   RootedValue receiver(cx, ObjectValue(*obj));
1621   return NativeGetProperty(cx, obj, receiver, id, vp);
1622 }
1623 
1624 extern bool NativeGetElement(JSContext* cx, HandleNativeObject obj,
1625                              HandleValue receiver, int32_t index,
1626                              MutableHandleValue vp);
1627 
1628 bool GetSparseElementHelper(JSContext* cx, HandleArrayObject obj,
1629                             int32_t int_id, MutableHandleValue result);
1630 
1631 bool SetPropertyByDefining(JSContext* cx, HandleId id, HandleValue v,
1632                            HandleValue receiver, ObjectOpResult& result);
1633 
1634 bool SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id,
1635                         HandleValue v, HandleValue receiver,
1636                         ObjectOpResult& result);
1637 
1638 bool AddOrUpdateSparseElementHelper(JSContext* cx, HandleArrayObject obj,
1639                                     int32_t int_id, HandleValue v, bool strict);
1640 
1641 /*
1642  * Indicates whether an assignment operation is qualified (`x.y = 0`) or
1643  * unqualified (`y = 0`). In strict mode, the latter is an error if no such
1644  * variable already exists.
1645  *
1646  * Used as an argument to NativeSetProperty.
1647  */
1648 enum QualifiedBool { Unqualified = 0, Qualified = 1 };
1649 
1650 template <QualifiedBool Qualified>
1651 extern bool NativeSetProperty(JSContext* cx, HandleNativeObject obj,
1652                               HandleId id, HandleValue v, HandleValue receiver,
1653                               ObjectOpResult& result);
1654 
1655 extern bool NativeSetElement(JSContext* cx, HandleNativeObject obj,
1656                              uint32_t index, HandleValue v,
1657                              HandleValue receiver, ObjectOpResult& result);
1658 
1659 extern bool NativeDeleteProperty(JSContext* cx, HandleNativeObject obj,
1660                                  HandleId id, ObjectOpResult& result);
1661 
1662 /*** SpiderMonkey nonstandard internal methods ******************************/
1663 
1664 template <AllowGC allowGC>
1665 extern bool NativeLookupOwnProperty(
1666     JSContext* cx, typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
1667     typename MaybeRooted<jsid, allowGC>::HandleType id, PropertyResult* propp);
1668 
1669 /*
1670  * Get a property from `receiver`, after having already done a lookup and found
1671  * the property on a native object `obj`.
1672  *
1673  * `prop` must be present in obj's shape.
1674  */
1675 extern bool NativeGetExistingProperty(JSContext* cx, HandleObject receiver,
1676                                       HandleNativeObject obj, HandleId id,
1677                                       PropertyInfo prop, MutableHandleValue vp);
1678 
1679 /* * */
1680 
1681 extern bool GetNameBoundInEnvironment(JSContext* cx, HandleObject env,
1682                                       HandleId id, MutableHandleValue vp);
1683 
1684 } /* namespace js */
1685 
1686 template <>
1687 inline bool JSObject::is<js::NativeObject>() const {
1688   return getClass()->isNativeObject();
1689 }
1690 
1691 namespace js {
1692 
1693 // Alternate to JSObject::as<NativeObject>() that tolerates null pointers.
MaybeNativeObject(JSObject * obj)1694 inline NativeObject* MaybeNativeObject(JSObject* obj) {
1695   return obj ? &obj->as<NativeObject>() : nullptr;
1696 }
1697 
1698 // Defined in NativeObject-inl.h.
1699 bool IsPackedArray(JSObject* obj);
1700 
1701 // Initialize an object's reserved slot with a private value pointing to
1702 // malloc-allocated memory and associate the memory with the object.
1703 //
1704 // This call should be matched with a call to JSFreeOp::free_/delete_ in the
1705 // object's finalizer to free the memory and update the memory accounting.
1706 
InitReservedSlot(NativeObject * obj,uint32_t slot,void * ptr,size_t nbytes,MemoryUse use)1707 inline void InitReservedSlot(NativeObject* obj, uint32_t slot, void* ptr,
1708                              size_t nbytes, MemoryUse use) {
1709   AddCellMemory(obj, nbytes, use);
1710   obj->initReservedSlot(slot, PrivateValue(ptr));
1711 }
1712 template <typename T>
InitReservedSlot(NativeObject * obj,uint32_t slot,T * ptr,MemoryUse use)1713 inline void InitReservedSlot(NativeObject* obj, uint32_t slot, T* ptr,
1714                              MemoryUse use) {
1715   InitReservedSlot(obj, slot, ptr, sizeof(T), use);
1716 }
1717 
1718 // Initialize an object's private slot with a pointer to malloc-allocated memory
1719 // and associate the memory with the object.
1720 //
1721 // This call should be matched with a call to JSFreeOp::free_/delete_ in the
1722 // object's finalizer to free the memory and update the memory accounting.
1723 
InitObjectPrivate(NativeObject * obj,void * ptr,size_t nbytes,MemoryUse use)1724 inline void InitObjectPrivate(NativeObject* obj, void* ptr, size_t nbytes,
1725                               MemoryUse use) {
1726   AddCellMemory(obj, nbytes, use);
1727   obj->initPrivate(ptr);
1728 }
1729 template <typename T>
InitObjectPrivate(NativeObject * obj,T * ptr,MemoryUse use)1730 inline void InitObjectPrivate(NativeObject* obj, T* ptr, MemoryUse use) {
1731   InitObjectPrivate(obj, ptr, sizeof(T), use);
1732 }
1733 
1734 }  // namespace js
1735 
1736 #endif /* vm_NativeObject_h */
1737