1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef vm_NativeObject_h
8 #define vm_NativeObject_h
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/Maybe.h"
17 #include "NamespaceImports.h"
19 #include "gc/Barrier.h"
20 #include "gc/MaybeRooted.h"
21 #include "gc/ZoneAllocator.h"
22 #include "js/shadow/Object.h" // JS::shadow::Object
23 #include "js/shadow/Zone.h" // JS::shadow::Zone
25 #include "vm/GetterSetter.h"
26 #include "vm/JSAtom.h"
27 #include "vm/JSObject.h"
29 #include "vm/StringType.h"
39 #ifdef ENABLE_RECORD_TUPLE
40 // Defined in vm/RecordTupleShared.{h,cpp}. We cannot include that file
41 // because it causes circular dependencies.
42 extern bool IsExtendedPrimitiveWrapper(const JSObject
& obj
);
46 * To really poison a set of values, using 'magic' or 'undefined' isn't good
47 * enough since often these will just be ignored by buggy code (see bug 629974)
48 * in debug builds and crash in release builds. Instead, we use a safe-for-crash
51 static MOZ_ALWAYS_INLINE
void Debug_SetValueRangeToCrashOnTouch(Value
* beg
,
54 for (Value
* v
= beg
; v
!= end
; ++v
) {
55 *v
= js::PoisonedObjectValue(0x48);
60 static MOZ_ALWAYS_INLINE
void Debug_SetValueRangeToCrashOnTouch(Value
* vec
,
63 Debug_SetValueRangeToCrashOnTouch(vec
, vec
+ len
);
67 static MOZ_ALWAYS_INLINE
void Debug_SetValueRangeToCrashOnTouch(
68 GCPtr
<Value
>* vec
, size_t len
) {
70 Debug_SetValueRangeToCrashOnTouch((Value
*)vec
, len
);
74 static MOZ_ALWAYS_INLINE
void Debug_SetSlotRangeToCrashOnTouch(HeapSlot
* vec
,
77 Debug_SetValueRangeToCrashOnTouch((Value
*)vec
, len
);
81 static MOZ_ALWAYS_INLINE
void Debug_SetSlotRangeToCrashOnTouch(HeapSlot
* begin
,
84 Debug_SetValueRangeToCrashOnTouch((Value
*)begin
, end
- begin
);
91 * ES6 20130308 draft 8.4.2.4 ArraySetLength.
93 * |id| must be "length", |desc| is the new non-accessor descriptor, and
94 * |result| receives an error code if the change is invalid.
96 extern bool ArraySetLength(JSContext
* cx
, Handle
<ArrayObject
*> obj
, HandleId id
,
97 Handle
<PropertyDescriptor
> desc
,
98 ObjectOpResult
& result
);
101 * [SMDOC] NativeObject Elements layout
103 * Elements header used for native objects. The elements component of such
104 * objects offers an efficient representation for all or some of the indexed
105 * properties of the object, using a flat array of Values rather than a shape
106 * hierarchy stored in the object's slots. This structure is immediately
107 * followed by an array of elements, with the elements member in an object
108 * pointing to the beginning of that array (the end of this structure). See
109 * below for usage of this structure.
111 * The sets of properties represented by an object's elements and slots
112 * are disjoint. The elements contain only indexed properties, while the slots
113 * can contain both named and indexed properties; any indexes in the slots are
114 * distinct from those in the elements. If isIndexed() is false for an object,
115 * all indexed properties (if any) are stored in the dense elements.
117 * Indexes will be stored in the object's slots instead of its elements in
118 * the following case:
119 * - there are more than MIN_SPARSE_INDEX slots total and the load factor
120 * (COUNT / capacity) is less than 0.25
121 * - a property is defined that has non-default property attributes.
123 * We track these pieces of metadata for dense elements:
124 * - The length property as a uint32_t, accessible for array objects with
125 * ArrayObject::{length,setLength}(). This is unused for non-arrays.
126 * - The number of element slots (capacity), gettable with
127 * getDenseCapacity().
128 * - The array's initialized length, accessible with
129 * getDenseInitializedLength().
131 * Holes in the array are represented by MagicValue(JS_ELEMENTS_HOLE) values.
132 * These indicate indexes which are not dense properties of the array. The
133 * property may, however, be held by the object's properties.
135 * The capacity and length of an object's elements are almost entirely
136 * unrelated! In general the length may be greater than, less than, or equal
137 * to the capacity. The first case occurs with |new Array(100)|. The length
138 * is 100, but the capacity remains 0 (indices below length and above capacity
139 * must be treated as holes) until elements between capacity and length are
140 * set. The other two cases are common, depending upon the number of elements
141 * in an array and the underlying allocator used for element storage.
143 * The only case in which the capacity and length of an object's elements are
144 * related is when the object is an array with non-writable length. In this
145 * case the capacity is always less than or equal to the length. This permits
146 * JIT code to optimize away the check for non-writable length when assigning
147 * to possibly out-of-range elements: such code already has to check for
148 * |index < capacity|, and fallback code checks for non-writable length.
150 * The initialized length of an object specifies the number of elements that
151 * have been initialized. All elements above the initialized length are
152 * holes in the object, and the memory for all elements between the initialized
153 * length and capacity is left uninitialized. The initialized length is some
154 * value less than or equal to both the object's length and the object's
157 * There is flexibility in exactly the value the initialized length must hold,
158 * e.g. if an array has length 5, capacity 10, completely empty, it is valid
159 * for the initialized length to be any value between zero and 5, as long as
160 * the in memory values below the initialized length have been initialized with
161 * a hole value. However, in such cases we want to keep the initialized length
162 * as small as possible: if the object is known to have no hole values below
163 * its initialized length, then it is "packed" and can be accessed much faster
166 * Elements do not track property creation order, so enumerating the elements
167 * of an object does not necessarily visit indexes in the order they were
171 * [SMDOC] NativeObject shifted elements optimization
175 * It's pretty common to use an array as a queue, like this:
177 * while (arr.length > 0)
180 * To ensure we don't get quadratic behavior on this, elements can be 'shifted'
181 * in memory. tryShiftDenseElements does this by incrementing elements_ to point
182 * to the next element and moving the ObjectElements header in memory (so it's
183 * stored where the shifted Value used to be).
185 * Shifted elements can be moved when we grow the array, when the array is
186 * made non-extensible (for simplicity, shifted elements are not supported on
187 * objects that are non-extensible, have copy-on-write elements, or on arrays
188 * with non-writable length).
190 class ObjectElements
{
192 enum Flags
: uint16_t {
193 // Elements are stored inline in the object allocation.
194 // An object allocated with the FIXED flag set can have the flag unset later
195 // if `growElements()` is called to increase the capacity beyond what was
196 // initially allocated. Once the flag is unset, it will remain so for the
197 // rest of the lifetime of the object.
200 // Present only if these elements correspond to an array with
201 // non-writable length; never present for non-arrays.
202 NONWRITABLE_ARRAY_LENGTH
= 0x2,
204 #ifdef ENABLE_RECORD_TUPLE
205 // Records, Tuples and Boxes must be atomized before being hashed. We store
206 // the "is atomized" flag here for tuples, and in fixed slots for records
208 TUPLE_IS_ATOMIZED
= 0x4,
211 // For TypedArrays only: this TypedArray's storage is mapping shared
212 // memory. This is a static property of the TypedArray, set when it
213 // is created and never changed.
216 // These elements are not extensible. If this flag is set, the object's
217 // Shape must also have the NotExtensible flag. This exists on
218 // ObjectElements in addition to Shape to simplify JIT code.
219 NOT_EXTENSIBLE
= 0x10,
221 // These elements are set to integrity level "sealed". If this flag is
222 // set, the NOT_EXTENSIBLE flag must be set as well.
225 // These elements are set to integrity level "frozen". If this flag is
226 // set, the SEALED flag must be set as well.
228 // This flag must only be set if the Shape has the FrozenElements flag.
229 // The Shape flag ensures a shape guard can be used to guard against frozen
230 // elements. The ObjectElements flag is convenient for JIT code and
231 // ObjectElements assertions.
234 // If this flag is not set, the elements are guaranteed to contain no hole
235 // values (the JS_ELEMENTS_HOLE MagicValue) in [0, initializedLength).
238 // If this flag is not set, there's definitely no for-in iterator that
239 // covers these dense elements so elements can be deleted without calling
240 // SuppressDeletedProperty. This is used by fast paths for various Array
241 // builtins. See also NativeObject::denseElementsMaybeInIteration.
242 MAYBE_IN_ITERATION
= 0x100,
245 // The flags word stores both the flags and the number of shifted elements.
246 // Allow shifting 2047 elements before actually moving the elements.
247 static const size_t NumShiftedElementsBits
= 11;
248 static const size_t MaxShiftedElements
= (1 << NumShiftedElementsBits
) - 1;
249 static const size_t NumShiftedElementsShift
= 32 - NumShiftedElementsBits
;
250 static const size_t FlagsMask
= (1 << NumShiftedElementsShift
) - 1;
251 static_assert(MaxShiftedElements
== 2047,
252 "MaxShiftedElements should match the comment");
255 friend class ::JSObject
;
256 friend class ArrayObject
;
257 friend class NativeObject
;
258 friend class gc::TenuringTracer
;
259 #ifdef ENABLE_RECORD_TUPLE
260 friend class TupleType
;
263 friend bool js::SetIntegrityLevel(JSContext
* cx
, HandleObject obj
,
264 IntegrityLevel level
);
266 friend bool ArraySetLength(JSContext
* cx
, Handle
<ArrayObject
*> obj
,
267 HandleId id
, Handle
<PropertyDescriptor
> desc
,
268 ObjectOpResult
& result
);
270 // The NumShiftedElementsBits high bits of this are used to store the
271 // number of shifted elements, the other bits are available for the flags.
272 // See Flags enum above.
276 * Number of initialized elements. This is <= the capacity, and for arrays
277 * is <= the length. Memory for elements above the initialized length is
278 * uninitialized, but values between the initialized length and the proper
279 * length are conceptually holes.
281 uint32_t initializedLength
;
283 /* Number of allocated slots. */
286 /* 'length' property of array objects, unused for other objects. */
289 bool hasNonwritableArrayLength() const {
290 return flags
& NONWRITABLE_ARRAY_LENGTH
;
292 void setNonwritableArrayLength() {
293 // See ArrayObject::setNonWritableLength.
294 MOZ_ASSERT(capacity
== initializedLength
);
295 MOZ_ASSERT(numShiftedElements() == 0);
296 flags
|= NONWRITABLE_ARRAY_LENGTH
;
299 #ifdef ENABLE_RECORD_TUPLE
300 void setTupleIsAtomized() { flags
|= TUPLE_IS_ATOMIZED
; }
302 bool tupleIsAtomized() const { return flags
& TUPLE_IS_ATOMIZED
; }
305 void addShiftedElements(uint32_t count
) {
306 MOZ_ASSERT(count
< capacity
);
307 MOZ_ASSERT(count
< initializedLength
);
309 flags
& (NONWRITABLE_ARRAY_LENGTH
| NOT_EXTENSIBLE
| SEALED
| FROZEN
)));
310 uint32_t numShifted
= numShiftedElements() + count
;
311 MOZ_ASSERT(numShifted
<= MaxShiftedElements
);
312 flags
= (numShifted
<< NumShiftedElementsShift
) | (flags
& FlagsMask
);
314 initializedLength
-= count
;
316 void unshiftShiftedElements(uint32_t count
) {
317 MOZ_ASSERT(count
> 0);
319 flags
& (NONWRITABLE_ARRAY_LENGTH
| NOT_EXTENSIBLE
| SEALED
| FROZEN
)));
320 uint32_t numShifted
= numShiftedElements();
321 MOZ_ASSERT(count
<= numShifted
);
323 flags
= (numShifted
<< NumShiftedElementsShift
) | (flags
& FlagsMask
);
325 initializedLength
+= count
;
327 void clearShiftedElements() {
329 MOZ_ASSERT(numShiftedElements() == 0);
332 void markNonPacked() { flags
|= NON_PACKED
; }
334 void markMaybeInIteration() { flags
|= MAYBE_IN_ITERATION
; }
335 bool maybeInIteration() { return flags
& MAYBE_IN_ITERATION
; }
337 void setNotExtensible() {
338 MOZ_ASSERT(!isNotExtensible());
339 flags
|= NOT_EXTENSIBLE
;
341 bool isNotExtensible() { return flags
& NOT_EXTENSIBLE
; }
344 MOZ_ASSERT(isNotExtensible());
345 MOZ_ASSERT(!isSealed());
346 MOZ_ASSERT(!isFrozen());
350 MOZ_ASSERT(isNotExtensible());
351 MOZ_ASSERT(isSealed());
352 MOZ_ASSERT(!isFrozen());
356 bool isFrozen() const { return flags
& FROZEN
; }
359 constexpr ObjectElements(uint32_t capacity
, uint32_t length
)
360 : flags(0), initializedLength(0), capacity(capacity
), length(length
) {}
362 enum class SharedMemory
{ IsShared
};
364 constexpr ObjectElements(uint32_t capacity
, uint32_t length
,
366 : flags(SHARED_MEMORY
),
367 initializedLength(0),
371 HeapSlot
* elements() {
372 return reinterpret_cast<HeapSlot
*>(uintptr_t(this) +
373 sizeof(ObjectElements
));
375 const HeapSlot
* elements() const {
376 return reinterpret_cast<const HeapSlot
*>(uintptr_t(this) +
377 sizeof(ObjectElements
));
379 static ObjectElements
* fromElements(HeapSlot
* elems
) {
380 return reinterpret_cast<ObjectElements
*>(uintptr_t(elems
) -
381 sizeof(ObjectElements
));
384 bool isSharedMemory() const { return flags
& SHARED_MEMORY
; }
386 static int offsetOfFlags() {
387 return int(offsetof(ObjectElements
, flags
)) - int(sizeof(ObjectElements
));
389 static int offsetOfInitializedLength() {
390 return int(offsetof(ObjectElements
, initializedLength
)) -
391 int(sizeof(ObjectElements
));
393 static int offsetOfCapacity() {
394 return int(offsetof(ObjectElements
, capacity
)) -
395 int(sizeof(ObjectElements
));
397 static int offsetOfLength() {
398 return int(offsetof(ObjectElements
, length
)) - int(sizeof(ObjectElements
));
401 static void PrepareForPreventExtensions(JSContext
* cx
, NativeObject
* obj
);
402 static void PreventExtensions(NativeObject
* obj
);
403 [[nodiscard
]] static bool FreezeOrSeal(JSContext
* cx
,
404 Handle
<NativeObject
*> obj
,
405 IntegrityLevel level
);
407 bool isSealed() const { return flags
& SEALED
; }
409 bool isPacked() const { return !(flags
& NON_PACKED
); }
411 JS::PropertyAttributes
elementAttributes() const {
413 return {JS::PropertyAttribute::Enumerable
};
416 return {JS::PropertyAttribute::Enumerable
,
417 JS::PropertyAttribute::Writable
};
419 return {JS::PropertyAttribute::Configurable
,
420 JS::PropertyAttribute::Enumerable
, JS::PropertyAttribute::Writable
};
423 uint32_t numShiftedElements() const {
424 uint32_t numShifted
= flags
>> NumShiftedElementsShift
;
425 MOZ_ASSERT_IF(numShifted
> 0,
426 !(flags
& (NONWRITABLE_ARRAY_LENGTH
| NOT_EXTENSIBLE
|
431 uint32_t numAllocatedElements() const {
432 return VALUES_PER_HEADER
+ capacity
+ numShiftedElements();
435 // This is enough slots to store an object of this class. See the static
437 static const size_t VALUES_PER_HEADER
= 2;
440 static_assert(ObjectElements::VALUES_PER_HEADER
* sizeof(HeapSlot
) ==
441 sizeof(ObjectElements
),
442 "ObjectElements doesn't fit in the given number of slots");
445 * Slots header used for native objects. The header stores the capacity and the
446 * slot data follows in memory.
448 class alignas(HeapSlot
) ObjectSlots
{
450 uint32_t dictionarySlotSpan_
;
451 uint64_t maybeUniqueId_
;
454 // Special values for maybeUniqueId_ to indicate no unique ID is present.
455 static constexpr uint64_t NoUniqueIdInDynamicSlots
= 0;
456 static constexpr uint64_t NoUniqueIdInSharedEmptySlots
= 1;
457 static constexpr uint64_t LastNoUniqueIdValue
= NoUniqueIdInSharedEmptySlots
;
459 static constexpr size_t VALUES_PER_HEADER
= 2;
461 static inline size_t allocCount(size_t slotCount
) {
462 static_assert(sizeof(ObjectSlots
) ==
463 ObjectSlots::VALUES_PER_HEADER
* sizeof(HeapSlot
));
464 return slotCount
+ VALUES_PER_HEADER
;
467 static inline size_t allocSize(size_t slotCount
) {
468 return allocCount(slotCount
) * sizeof(HeapSlot
);
471 static ObjectSlots
* fromSlots(HeapSlot
* slots
) {
473 return reinterpret_cast<ObjectSlots
*>(uintptr_t(slots
) -
474 sizeof(ObjectSlots
));
477 static constexpr size_t offsetOfCapacity() {
478 return offsetof(ObjectSlots
, capacity_
);
480 static constexpr size_t offsetOfDictionarySlotSpan() {
481 return offsetof(ObjectSlots
, dictionarySlotSpan_
);
483 static constexpr size_t offsetOfMaybeUniqueId() {
484 return offsetof(ObjectSlots
, maybeUniqueId_
);
486 static constexpr size_t offsetOfSlots() { return sizeof(ObjectSlots
); }
488 constexpr explicit ObjectSlots(uint32_t capacity
, uint32_t dictionarySlotSpan
,
489 uint64_t maybeUniqueId
);
491 constexpr uint32_t capacity() const { return capacity_
; }
493 constexpr uint32_t dictionarySlotSpan() const { return dictionarySlotSpan_
; }
495 bool isSharedEmptySlots() const {
496 return maybeUniqueId_
== NoUniqueIdInSharedEmptySlots
;
499 constexpr bool hasUniqueId() const {
500 return maybeUniqueId_
> LastNoUniqueIdValue
;
502 uint64_t uniqueId() const {
503 MOZ_ASSERT(hasUniqueId());
504 return maybeUniqueId_
;
506 uintptr_t maybeUniqueId() const { return hasUniqueId() ? maybeUniqueId_
: 0; }
507 void setUniqueId(uint64_t uid
) {
508 MOZ_ASSERT(uid
> LastNoUniqueIdValue
);
509 MOZ_ASSERT(!isSharedEmptySlots());
510 maybeUniqueId_
= uid
;
513 void setDictionarySlotSpan(uint32_t span
) { dictionarySlotSpan_
= span
; }
515 HeapSlot
* slots() const {
516 return reinterpret_cast<HeapSlot
*>(uintptr_t(this) + sizeof(ObjectSlots
));
521 * Shared singletons for objects with no elements.
522 * emptyObjectElementsShared is used only for TypedArrays, when the TA
523 * maps shared memory.
525 extern HeapSlot
* const emptyObjectElements
;
526 extern HeapSlot
* const emptyObjectElementsShared
;
529 * Shared singletons for objects with no dynamic slots.
531 extern HeapSlot
* const emptyObjectSlots
;
532 extern HeapSlot
* const emptyObjectSlotsForDictionaryObject
[];
534 class AutoCheckShapeConsistency
;
537 // Operations which change an object's dense elements can either succeed, fail,
538 // or be unable to complete. The latter is used when the object's elements must
539 // become sparse instead. The enum below is used for such operations.
540 enum class DenseElementResult
{ Failure
, Success
, Incomplete
};
542 // Stores a slot offset in bytes relative to either the NativeObject* address
543 // (if isFixedSlot) or to NativeObject::slots_ (if !isFixedSlot).
544 class TaggedSlotOffset
{
548 static constexpr size_t OffsetShift
= 1;
549 static constexpr size_t IsFixedSlotFlag
= 0b1;
551 static constexpr size_t MaxOffset
= SHAPE_MAXIMUM_SLOT
* sizeof(Value
);
552 static_assert((uint64_t(MaxOffset
) << OffsetShift
) <= UINT32_MAX
,
553 "maximum slot offset must fit in TaggedSlotOffset");
555 constexpr TaggedSlotOffset() = default;
557 TaggedSlotOffset(uint32_t offset
, bool isFixedSlot
)
558 : bits_((offset
<< OffsetShift
) | isFixedSlot
) {
559 MOZ_ASSERT(offset
<= MaxOffset
);
562 uint32_t offset() const { return bits_
>> OffsetShift
; }
563 bool isFixedSlot() const { return bits_
& IsFixedSlotFlag
; }
565 bool operator==(const TaggedSlotOffset
& other
) const {
566 return bits_
== other
.bits_
;
568 bool operator!=(const TaggedSlotOffset
& other
) const {
569 return !(*this == other
);
574 * [SMDOC] NativeObject layout
576 * NativeObject specifies the internal implementation of a native object.
578 * Native objects use ShapedObject::shape to record property information. Two
579 * native objects with the same shape are guaranteed to have the same number of
582 * Native objects extend the base implementation of an object with storage for
583 * the object's named properties and indexed elements.
585 * These are stored separately from one another. Objects are followed by a
586 * variable-sized array of values for inline storage, which may be used by
587 * either properties of native objects (fixed slots), by elements (fixed
588 * elements), or by other data for certain kinds of objects, such as
589 * ArrayBufferObjects and TypedArrayObjects.
591 * Named property storage can be split between fixed slots and a dynamically
592 * allocated array (the slots member). For an object with N fixed slots, shapes
593 * with slots [0..N-1] are stored in the fixed slots, and the remainder are
594 * stored in the dynamic array. If all properties fit in the fixed slots, the
595 * 'slots_' member is nullptr.
597 * Elements are indexed via the 'elements_' member. This member can point to
598 * either the shared emptyObjectElements and emptyObjectElementsShared
599 * singletons, into the inline value array (the address of the third value, to
600 * leave room for a ObjectElements header;in this case numFixedSlots() is zero)
601 * or to a dynamically allocated array.
603 * Slots and elements may both be non-empty. The slots may be either names or
604 * indexes; no indexed property will be in both the slots and elements.
606 class NativeObject
: public JSObject
{
608 /* Slots for object properties. */
609 js::HeapSlot
* slots_
;
611 /* Slots for object dense elements. */
612 js::HeapSlot
* elements_
;
614 friend class ::JSObject
;
617 static void staticAsserts() {
618 static_assert(sizeof(NativeObject
) == sizeof(JSObject_Slots0
),
619 "native object size must match GC thing size");
620 static_assert(sizeof(NativeObject
) == sizeof(JS::shadow::Object
),
621 "shadow interface must match actual implementation");
622 static_assert(sizeof(NativeObject
) % sizeof(Value
) == 0,
623 "fixed slots after an object must be aligned");
625 static_assert(offsetOfShape() == offsetof(JS::shadow::Object
, shape
),
626 "shadow type must match actual type");
628 offsetof(NativeObject
, slots_
) == offsetof(JS::shadow::Object
, slots
),
629 "shadow slots must match actual slots");
631 offsetof(NativeObject
, elements_
) == offsetof(JS::shadow::Object
, _1
),
632 "shadow placeholder must match actual elements");
634 static_assert(MAX_FIXED_SLOTS
<= Shape::FIXED_SLOTS_MAX
,
635 "verify numFixedSlots() bitfield is big enough");
636 static_assert(sizeof(NativeObject
) + MAX_FIXED_SLOTS
* sizeof(Value
) ==
637 JSObject::MAX_BYTE_SIZE
,
638 "inconsistent maximum object size");
640 // Sanity check NativeObject size is what we expect.
642 static_assert(sizeof(NativeObject
) == 3 * sizeof(void*));
644 static_assert(sizeof(NativeObject
) == 4 * sizeof(void*));
649 NativeShape
* shape() const { return &JSObject::shape()->asNative(); }
650 SharedShape
* sharedShape() const { return &shape()->asShared(); }
651 DictionaryShape
* dictionaryShape() const { return &shape()->asDictionary(); }
653 PropertyInfoWithKey
getLastProperty() const {
654 return shape()->lastProperty();
657 HeapSlotArray
getDenseElements() const { return HeapSlotArray(elements_
); }
659 const Value
& getDenseElement(uint32_t idx
) const {
660 MOZ_ASSERT(idx
< getDenseInitializedLength());
661 return elements_
[idx
];
663 bool containsDenseElement(uint32_t idx
) const {
664 return idx
< getDenseInitializedLength() &&
665 !elements_
[idx
].isMagic(JS_ELEMENTS_HOLE
);
667 uint32_t getDenseInitializedLength() const {
668 return getElementsHeader()->initializedLength
;
670 uint32_t getDenseCapacity() const { return getElementsHeader()->capacity
; }
672 bool isSharedMemory() const { return getElementsHeader()->isSharedMemory(); }
674 // Update the object's shape and allocate slots if needed to match the shape's
676 MOZ_ALWAYS_INLINE
bool setShapeAndAddNewSlots(JSContext
* cx
,
677 SharedShape
* newShape
,
681 // Methods optimized for adding/removing a single slot. Must only be used for
682 // non-dictionary objects.
683 MOZ_ALWAYS_INLINE
bool setShapeAndAddNewSlot(JSContext
* cx
,
684 SharedShape
* newShape
,
686 void setShapeAndRemoveLastSlot(JSContext
* cx
, SharedShape
* newShape
,
689 MOZ_ALWAYS_INLINE
bool canReuseShapeForNewProperties(
690 NativeShape
* newShape
) const {
691 NativeShape
* oldShape
= shape();
692 MOZ_ASSERT(oldShape
->propMapLength() == 0,
693 "object must have no properties");
694 MOZ_ASSERT(newShape
->propMapLength() > 0,
695 "new shape must have at least one property");
696 if (oldShape
->numFixedSlots() != newShape
->numFixedSlots()) {
699 if (oldShape
->isDictionary() || newShape
->isDictionary()) {
702 if (oldShape
->base() != newShape
->base()) {
705 MOZ_ASSERT(oldShape
->getObjectClass() == newShape
->getObjectClass());
706 MOZ_ASSERT(oldShape
->proto() == newShape
->proto());
707 MOZ_ASSERT(oldShape
->realm() == newShape
->realm());
708 // We only handle the common case where the old shape has no object flags
709 // (expected because it's an empty object) and the new shape has just the
710 // HasEnumerable flag that we can copy safely.
711 if (!oldShape
->objectFlags().isEmpty()) {
714 MOZ_ASSERT(newShape
->hasObjectFlag(ObjectFlag::HasEnumerable
));
715 return newShape
->objectFlags() == ObjectFlags({ObjectFlag::HasEnumerable
});
718 // Newly-created TypedArrays that map a SharedArrayBuffer are
719 // marked as shared by giving them an ObjectElements that has the
720 // ObjectElements::SHARED_MEMORY flag set.
721 void setIsSharedMemory() {
722 MOZ_ASSERT(elements_
== emptyObjectElements
);
723 elements_
= emptyObjectElementsShared
;
726 inline bool isInWholeCellBuffer() const;
728 static inline NativeObject
* create(JSContext
* cx
, gc::AllocKind kind
,
729 gc::InitialHeap heap
,
730 Handle
<SharedShape
*> shape
,
731 gc::AllocSite
* site
= nullptr);
734 static void enableShapeConsistencyChecks();
739 friend class js::AutoCheckShapeConsistency
;
740 void checkShapeConsistency();
742 void checkShapeConsistency() {}
745 void maybeFreeDictionaryPropSlots(JSContext
* cx
, DictionaryPropMap
* map
,
748 [[nodiscard
]] static bool toDictionaryMode(JSContext
* cx
,
749 Handle
<NativeObject
*> obj
);
752 inline void setEmptyDynamicSlots(uint32_t dictonarySlotSpan
);
754 inline void setDictionaryModeSlotSpan(uint32_t span
);
756 friend class gc::TenuringTracer
;
758 // Given a slot range from |start| to |end| exclusive, call |fun| with
759 // pointers to the corresponding fixed slot and/or dynamic slot ranges.
760 template <typename Fun
>
761 void forEachSlotRangeUnchecked(uint32_t start
, uint32_t end
, const Fun
& fun
) {
762 MOZ_ASSERT(end
>= start
);
763 uint32_t nfixed
= numFixedSlots();
764 if (start
< nfixed
) {
765 HeapSlot
* fixedStart
= &fixedSlots()[start
];
766 HeapSlot
* fixedEnd
= &fixedSlots()[std::min(nfixed
, end
)];
767 fun(fixedStart
, fixedEnd
);
771 HeapSlot
* dynStart
= &slots_
[start
- nfixed
];
772 HeapSlot
* dynEnd
= &slots_
[end
- nfixed
];
773 fun(dynStart
, dynEnd
);
777 template <typename Fun
>
778 void forEachSlotRange(uint32_t start
, uint32_t end
, const Fun
& fun
) {
779 MOZ_ASSERT(slotInRange(end
, SENTINEL_ALLOWED
));
780 forEachSlotRangeUnchecked(start
, end
, fun
);
784 friend class DictionaryPropMap
;
785 friend class GCMarker
;
788 void invalidateSlotRange(uint32_t start
, uint32_t end
) {
790 forEachSlotRange(start
, end
, [](HeapSlot
* slotsStart
, HeapSlot
* slotsEnd
) {
791 Debug_SetSlotRangeToCrashOnTouch(slotsStart
, slotsEnd
);
796 void initFixedSlots(uint32_t numSlots
) {
797 MOZ_ASSERT(numSlots
== numUsedFixedSlots());
798 HeapSlot
* slots
= fixedSlots();
799 for (uint32_t i
= 0; i
< numSlots
; i
++) {
800 slots
[i
].initAsUndefined();
803 void initDynamicSlots(uint32_t numSlots
) {
804 MOZ_ASSERT(numSlots
== sharedShape()->slotSpan() - numFixedSlots());
805 HeapSlot
* slots
= slots_
;
806 for (uint32_t i
= 0; i
< numSlots
; i
++) {
807 slots
[i
].initAsUndefined();
810 void initSlots(uint32_t nfixed
, uint32_t slotSpan
) {
811 initFixedSlots(std::min(nfixed
, slotSpan
));
812 if (slotSpan
> nfixed
) {
813 initDynamicSlots(slotSpan
- nfixed
);
818 enum SentinelAllowed
{SENTINEL_NOT_ALLOWED
, SENTINEL_ALLOWED
};
821 * Check that slot is in range for the object's allocated slots.
822 * If sentinelAllowed then slot may equal the slot capacity.
824 bool slotInRange(uint32_t slot
,
825 SentinelAllowed sentinel
= SENTINEL_NOT_ALLOWED
) const;
828 * Check whether a slot is a fixed slot.
830 bool slotIsFixed(uint32_t slot
) const;
833 * Check whether the supplied number of fixed slots is correct.
835 bool isNumFixedSlots(uint32_t nfixed
) const;
839 * Minimum size for dynamically allocated slots in normal Objects.
840 * ArrayObjects don't use this limit and can have a lower slot capacity,
841 * since they normally don't have a lot of slots.
843 static const uint32_t SLOT_CAPACITY_MIN
= 8 - ObjectSlots::VALUES_PER_HEADER
;
846 * Minimum size for dynamically allocated elements in normal Objects.
848 static const uint32_t ELEMENT_CAPACITY_MIN
=
849 8 - ObjectElements::VALUES_PER_HEADER
;
851 HeapSlot
* fixedSlots() const {
852 return reinterpret_cast<HeapSlot
*>(uintptr_t(this) + sizeof(NativeObject
));
856 inline void initEmptyDynamicSlots();
858 [[nodiscard
]] static bool generateNewDictionaryShape(
859 JSContext
* cx
, Handle
<NativeObject
*> obj
);
861 // The maximum number of slots in an object.
862 // |MAX_SLOTS_COUNT * sizeof(JS::Value)| shouldn't overflow
863 // int32_t (see slotsSizeMustNotOverflow).
864 static const uint32_t MAX_SLOTS_COUNT
= (1 << 28) - 1;
866 static void slotsSizeMustNotOverflow() {
868 NativeObject::MAX_SLOTS_COUNT
<= INT32_MAX
/ sizeof(JS::Value
),
869 "every caller of this method requires that a slot "
870 "number (or slot count) count multiplied by "
871 "sizeof(Value) can't overflow uint32_t (and sometimes "
875 uint32_t numFixedSlots() const {
876 return reinterpret_cast<const JS::shadow::Object
*>(this)->numFixedSlots();
879 // Get the number of fixed slots when the shape pointer may have been
880 // forwarded by a moving GC. You need to use this rather that
881 // numFixedSlots() in a trace hook if you access an object that is not the
882 // object being traced, since it may have a stale shape pointer.
883 inline uint32_t numFixedSlotsMaybeForwarded() const;
885 uint32_t numUsedFixedSlots() const {
886 uint32_t nslots
= sharedShape()->slotSpan();
887 return std::min(nslots
, numFixedSlots());
890 uint32_t slotSpan() const {
891 if (inDictionaryMode()) {
892 return dictionaryModeSlotSpan();
894 MOZ_ASSERT(getSlotsHeader()->dictionarySlotSpan() == 0);
895 return sharedShape()->slotSpan();
898 uint32_t dictionaryModeSlotSpan() const {
899 MOZ_ASSERT(inDictionaryMode());
900 return getSlotsHeader()->dictionarySlotSpan();
903 /* Whether a slot is at a fixed offset from this object. */
904 bool isFixedSlot(size_t slot
) { return slot
< numFixedSlots(); }
906 /* Index into the dynamic slots array to use for a dynamic slot. */
907 size_t dynamicSlotIndex(size_t slot
) {
908 MOZ_ASSERT(slot
>= numFixedSlots());
909 return slot
- numFixedSlots();
912 // Native objects are never proxies. Call isExtensible instead.
913 bool nonProxyIsExtensible() const = delete;
915 bool isExtensible() const {
916 #ifdef ENABLE_RECORD_TUPLE
917 if (IsExtendedPrimitiveWrapper(*this)) {
921 return !hasFlag(ObjectFlag::NotExtensible
);
925 * Whether there may be indexed properties on this object, excluding any in
926 * the object's elements.
928 bool isIndexed() const { return hasFlag(ObjectFlag::Indexed
); }
930 bool hasInterestingSymbol() const {
931 return hasFlag(ObjectFlag::HasInterestingSymbol
);
934 bool hasEnumerableProperty() const {
935 return hasFlag(ObjectFlag::HasEnumerable
);
938 static bool setHadGetterSetterChange(JSContext
* cx
,
939 Handle
<NativeObject
*> obj
) {
940 return setFlag(cx
, obj
, ObjectFlag::HadGetterSetterChange
);
942 bool hadGetterSetterChange() const {
943 return hasFlag(ObjectFlag::HadGetterSetterChange
);
946 bool allocateInitialSlots(JSContext
* cx
, uint32_t capacity
);
949 * Grow or shrink slots immediately before changing the slot span.
950 * The number of allocated slots is not stored explicitly, and changes to
951 * the slots must track changes in the slot span.
953 bool growSlots(JSContext
* cx
, uint32_t oldCapacity
, uint32_t newCapacity
);
954 bool growSlotsForNewSlot(JSContext
* cx
, uint32_t numFixed
, uint32_t slot
);
955 void shrinkSlots(JSContext
* cx
, uint32_t oldCapacity
, uint32_t newCapacity
);
957 bool allocateSlots(JSContext
* cx
, uint32_t newCapacity
);
960 * This method is static because it's called from JIT code. On OOM, returns
961 * false without leaving a pending exception on the context.
963 static bool growSlotsPure(JSContext
* cx
, NativeObject
* obj
,
964 uint32_t newCapacity
);
967 * Like growSlotsPure but for dense elements. This will return
968 * false if we failed to allocate a dense element for some reason (OOM, too
969 * many dense elements, non-writable array length, etc).
971 static bool addDenseElementPure(JSContext
* cx
, NativeObject
* obj
);
974 * Indicates whether this object has an ObjectSlots allocation attached. The
975 * capacity of this can be zero if it is only used to hold a unique ID.
977 bool hasDynamicSlots() const {
978 return !getSlotsHeader()->isSharedEmptySlots();
981 /* Compute the number of dynamic slots required for this object. */
982 MOZ_ALWAYS_INLINE
uint32_t calculateDynamicSlots() const;
984 MOZ_ALWAYS_INLINE
uint32_t numDynamicSlots() const;
987 uint32_t outOfLineNumDynamicSlots() const;
990 bool empty() const { return shape()->propMapLength() == 0; }
992 mozilla::Maybe
<PropertyInfo
> lookup(JSContext
* cx
, jsid id
);
993 mozilla::Maybe
<PropertyInfo
> lookup(JSContext
* cx
, PropertyName
* name
) {
994 return lookup(cx
, NameToId(name
));
997 bool contains(JSContext
* cx
, jsid id
) { return lookup(cx
, id
).isSome(); }
998 bool contains(JSContext
* cx
, PropertyName
* name
) {
999 return lookup(cx
, name
).isSome();
1001 bool contains(JSContext
* cx
, jsid id
, PropertyInfo prop
) {
1002 mozilla::Maybe
<PropertyInfo
> found
= lookup(cx
, id
);
1003 return found
.isSome() && *found
== prop
;
1006 /* Contextless; can be called from other pure code. */
1007 mozilla::Maybe
<PropertyInfo
> lookupPure(jsid id
);
1008 mozilla::Maybe
<PropertyInfo
> lookupPure(PropertyName
* name
) {
1009 return lookupPure(NameToId(name
));
1012 bool containsPure(jsid id
) { return lookupPure(id
).isSome(); }
1013 bool containsPure(PropertyName
* name
) { return containsPure(NameToId(name
)); }
1014 bool containsPure(jsid id
, PropertyInfo prop
) {
1015 mozilla::Maybe
<PropertyInfo
> found
= lookupPure(id
);
1016 return found
.isSome() && *found
== prop
;
1021 * Allocate and free an object slot.
1023 * FIXME: bug 593129 -- slot allocation should be done by object methods
1024 * after calling object-parameter-free shape methods, avoiding coupling
1025 * logic across the object vs. shape module wall.
1027 static bool allocDictionarySlot(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1030 void freeDictionarySlot(uint32_t slot
);
1032 static MOZ_ALWAYS_INLINE
bool maybeConvertToDictionaryForAdd(
1033 JSContext
* cx
, Handle
<NativeObject
*> obj
);
1036 // Add a new property. Must only be used when the |id| is not already present
1037 // in the object's shape. Checks for non-extensibility must be done by the
1039 static bool addProperty(JSContext
* cx
, Handle
<NativeObject
*> obj
, HandleId id
,
1040 PropertyFlags flags
, uint32_t* slotOut
);
1042 static bool addProperty(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1043 Handle
<PropertyName
*> name
, PropertyFlags flags
,
1044 uint32_t* slotOut
) {
1045 RootedId
id(cx
, NameToId(name
));
1046 return addProperty(cx
, obj
, id
, flags
, slotOut
);
1049 static bool addPropertyInReservedSlot(JSContext
* cx
,
1050 Handle
<NativeObject
*> obj
, HandleId id
,
1051 uint32_t slot
, PropertyFlags flags
);
1052 static bool addPropertyInReservedSlot(JSContext
* cx
,
1053 Handle
<NativeObject
*> obj
,
1054 Handle
<PropertyName
*> name
,
1055 uint32_t slot
, PropertyFlags flags
) {
1056 RootedId
id(cx
, NameToId(name
));
1057 return addPropertyInReservedSlot(cx
, obj
, id
, slot
, flags
);
1060 static bool addCustomDataProperty(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1061 HandleId id
, PropertyFlags flags
);
1063 // Change a property with key |id| in this object. The object must already
1064 // have a property (stored in the shape tree) with this |id|.
1065 static bool changeProperty(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1066 HandleId id
, PropertyFlags flags
,
1069 static bool changeCustomDataPropAttributes(JSContext
* cx
,
1070 Handle
<NativeObject
*> obj
,
1071 HandleId id
, PropertyFlags flags
);
1073 // Remove the property named by id from this object.
1074 static bool removeProperty(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1077 static bool freezeOrSealProperties(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1078 IntegrityLevel level
);
1081 static bool changeNumFixedSlotsAfterSwap(JSContext
* cx
,
1082 Handle
<NativeObject
*> obj
,
1085 // For use from JSObject::swap.
1086 [[nodiscard
]] bool prepareForSwap(JSContext
* cx
,
1087 MutableHandleValueVector slotValuesOut
);
1088 [[nodiscard
]] static bool fixupAfterSwap(JSContext
* cx
,
1089 Handle
<NativeObject
*> obj
,
1091 HandleValueVector slotValues
);
1094 // Return true if this object has been converted from shared-immutable
1095 // shapes to object-owned dictionary shapes.
1096 bool inDictionaryMode() const { return shape()->isDictionary(); }
1098 const Value
& getSlot(uint32_t slot
) const {
1099 MOZ_ASSERT(slotInRange(slot
));
1100 uint32_t fixed
= numFixedSlots();
1102 return fixedSlots()[slot
];
1104 return slots_
[slot
- fixed
];
1107 const HeapSlot
* getSlotAddressUnchecked(uint32_t slot
) const {
1108 uint32_t fixed
= numFixedSlots();
1110 return fixedSlots() + slot
;
1112 return slots_
+ (slot
- fixed
);
1115 HeapSlot
* getSlotAddressUnchecked(uint32_t slot
) {
1116 uint32_t fixed
= numFixedSlots();
1118 return fixedSlots() + slot
;
1120 return slots_
+ (slot
- fixed
);
1123 HeapSlot
* getSlotAddress(uint32_t slot
) {
1125 * This can be used to get the address of the end of the slots for the
1126 * object, which may be necessary when fetching zero-length arrays of
1127 * slots (e.g. for callObjVarArray).
1129 MOZ_ASSERT(slotInRange(slot
, SENTINEL_ALLOWED
));
1130 return getSlotAddressUnchecked(slot
);
1133 const HeapSlot
* getSlotAddress(uint32_t slot
) const {
1135 * This can be used to get the address of the end of the slots for the
1136 * object, which may be necessary when fetching zero-length arrays of
1137 * slots (e.g. for callObjVarArray).
1139 MOZ_ASSERT(slotInRange(slot
, SENTINEL_ALLOWED
));
1140 return getSlotAddressUnchecked(slot
);
1143 MOZ_ALWAYS_INLINE HeapSlot
& getSlotRef(uint32_t slot
) {
1144 MOZ_ASSERT(slotInRange(slot
));
1145 return *getSlotAddress(slot
);
1148 MOZ_ALWAYS_INLINE
const HeapSlot
& getSlotRef(uint32_t slot
) const {
1149 MOZ_ASSERT(slotInRange(slot
));
1150 return *getSlotAddress(slot
);
1153 // Check requirements on values stored to this object.
1154 MOZ_ALWAYS_INLINE
void checkStoredValue(const Value
& v
) {
1155 MOZ_ASSERT(IsObjectValueInCompartment(v
, compartment()));
1156 MOZ_ASSERT(AtomIsMarked(zoneFromAnyThread(), v
));
1157 MOZ_ASSERT_IF(v
.isMagic() && v
.whyMagic() == JS_ELEMENTS_HOLE
,
1158 !denseElementsArePacked());
1161 MOZ_ALWAYS_INLINE
void setSlot(uint32_t slot
, const Value
& value
) {
1162 MOZ_ASSERT(slotInRange(slot
));
1163 checkStoredValue(value
);
1164 getSlotRef(slot
).set(this, HeapSlot::Slot
, slot
, value
);
1167 MOZ_ALWAYS_INLINE
void initSlot(uint32_t slot
, const Value
& value
) {
1168 MOZ_ASSERT(getSlot(slot
).isUndefined());
1169 MOZ_ASSERT(slotInRange(slot
));
1170 checkStoredValue(value
);
1171 initSlotUnchecked(slot
, value
);
1174 MOZ_ALWAYS_INLINE
void initSlotUnchecked(uint32_t slot
, const Value
& value
) {
1175 getSlotAddressUnchecked(slot
)->init(this, HeapSlot::Slot
, slot
, value
);
1178 // Returns the GetterSetter for an accessor property.
1179 GetterSetter
* getGetterSetter(uint32_t slot
) const {
1180 return getSlot(slot
).toGCThing()->as
<GetterSetter
>();
1182 GetterSetter
* getGetterSetter(PropertyInfo prop
) const {
1183 MOZ_ASSERT(prop
.isAccessorProperty());
1184 return getGetterSetter(prop
.slot());
1187 // Returns the (possibly nullptr) getter or setter object. |prop| and |slot|
1188 // must be (for) an accessor property.
1189 JSObject
* getGetter(uint32_t slot
) const {
1190 return getGetterSetter(slot
)->getter();
1192 JSObject
* getGetter(PropertyInfo prop
) const {
1193 return getGetterSetter(prop
)->getter();
1195 JSObject
* getSetter(PropertyInfo prop
) const {
1196 return getGetterSetter(prop
)->setter();
1199 // Returns true if the property has a non-nullptr getter or setter object.
1200 // |prop| can be any property.
1201 bool hasGetter(PropertyInfo prop
) const {
1202 return prop
.isAccessorProperty() && getGetter(prop
);
1204 bool hasSetter(PropertyInfo prop
) const {
1205 return prop
.isAccessorProperty() && getSetter(prop
);
1208 // If the property has a non-nullptr getter/setter, return it as ObjectValue.
1209 // Else return |undefined|. |prop| must be an accessor property.
1210 Value
getGetterValue(PropertyInfo prop
) const {
1211 MOZ_ASSERT(prop
.isAccessorProperty());
1212 if (JSObject
* getterObj
= getGetter(prop
)) {
1213 return ObjectValue(*getterObj
);
1215 return UndefinedValue();
1217 Value
getSetterValue(PropertyInfo prop
) const {
1218 MOZ_ASSERT(prop
.isAccessorProperty());
1219 if (JSObject
* setterObj
= getSetter(prop
)) {
1220 return ObjectValue(*setterObj
);
1222 return UndefinedValue();
1225 [[nodiscard
]] bool setUniqueId(JSContext
* cx
, uint64_t uid
);
1226 inline bool hasUniqueId() const { return getSlotsHeader()->hasUniqueId(); }
1227 inline uint64_t uniqueId() const { return getSlotsHeader()->uniqueId(); }
1228 inline uint64_t maybeUniqueId() const {
1229 return getSlotsHeader()->maybeUniqueId();
1231 bool setOrUpdateUniqueId(JSContext
* cx
, uint64_t uid
);
1233 // MAX_FIXED_SLOTS is the biggest number of fixed slots our GC
1234 // size classes will give an object.
1235 static constexpr uint32_t MAX_FIXED_SLOTS
=
1236 JS::shadow::Object::MAX_FIXED_SLOTS
;
1239 void prepareElementRangeForOverwrite(size_t start
, size_t end
) {
1240 MOZ_ASSERT(end
<= getDenseInitializedLength());
1241 for (size_t i
= start
; i
< end
; i
++) {
1242 elements_
[i
].destroy();
1247 * Trigger the write barrier on a range of slots that will no longer be
1250 void prepareSlotRangeForOverwrite(size_t start
, size_t end
) {
1251 for (size_t i
= start
; i
< end
; i
++) {
1252 getSlotAddressUnchecked(i
)->destroy();
1256 inline void shiftDenseElementsUnchecked(uint32_t count
);
1258 // Like getSlotRef, but optimized for reserved slots. This relies on the fact
1259 // that the first reserved slots (up to MAX_FIXED_SLOTS) are always stored in
1260 // fixed slots. This lets the compiler optimize away the branch below when
1261 // |index| is a constant (after inlining).
1263 // Note: objects that may be swapped have less predictable slot layouts
1264 // because they could have been swapped with an object with fewer fixed slots.
1265 // Fortunately, the only native objects that can be swapped are DOM objects
1266 // and these shouldn't end up here (asserted below).
1267 MOZ_ALWAYS_INLINE HeapSlot
& getReservedSlotRef(uint32_t index
) {
1268 MOZ_ASSERT(index
< JSSLOT_FREE(getClass()));
1269 MOZ_ASSERT(slotIsFixed(index
) == (index
< MAX_FIXED_SLOTS
));
1270 MOZ_ASSERT(!ObjectMayBeSwapped(this));
1271 return index
< MAX_FIXED_SLOTS
? fixedSlots()[index
]
1272 : slots_
[index
- MAX_FIXED_SLOTS
];
1274 MOZ_ALWAYS_INLINE
const HeapSlot
& getReservedSlotRef(uint32_t index
) const {
1275 MOZ_ASSERT(index
< JSSLOT_FREE(getClass()));
1276 MOZ_ASSERT(slotIsFixed(index
) == (index
< MAX_FIXED_SLOTS
));
1277 MOZ_ASSERT(!ObjectMayBeSwapped(this));
1278 return index
< MAX_FIXED_SLOTS
? fixedSlots()[index
]
1279 : slots_
[index
- MAX_FIXED_SLOTS
];
1283 MOZ_ALWAYS_INLINE
const Value
& getReservedSlot(uint32_t index
) const {
1284 return getReservedSlotRef(index
);
1286 MOZ_ALWAYS_INLINE
void initReservedSlot(uint32_t index
, const Value
& v
) {
1287 MOZ_ASSERT(getReservedSlot(index
).isUndefined());
1288 checkStoredValue(v
);
1289 getReservedSlotRef(index
).init(this, HeapSlot::Slot
, index
, v
);
1291 MOZ_ALWAYS_INLINE
void setReservedSlot(uint32_t index
, const Value
& v
) {
1292 checkStoredValue(v
);
1293 getReservedSlotRef(index
).set(this, HeapSlot::Slot
, index
, v
);
1296 // For slots which are known to always be fixed, due to the way they are
1299 HeapSlot
& getFixedSlotRef(uint32_t slot
) {
1300 MOZ_ASSERT(slotIsFixed(slot
));
1301 return fixedSlots()[slot
];
1304 const Value
& getFixedSlot(uint32_t slot
) const {
1305 MOZ_ASSERT(slotIsFixed(slot
));
1306 return fixedSlots()[slot
];
1309 const Value
& getDynamicSlot(uint32_t dynamicSlotIndex
) const {
1310 MOZ_ASSERT(dynamicSlotIndex
< outOfLineNumDynamicSlots());
1311 return slots_
[dynamicSlotIndex
];
1314 void setFixedSlot(uint32_t slot
, const Value
& value
) {
1315 MOZ_ASSERT(slotIsFixed(slot
));
1316 checkStoredValue(value
);
1317 fixedSlots()[slot
].set(this, HeapSlot::Slot
, slot
, value
);
1320 void setDynamicSlot(uint32_t numFixed
, uint32_t slot
, const Value
& value
) {
1321 MOZ_ASSERT(numFixedSlots() == numFixed
);
1322 MOZ_ASSERT(slot
>= numFixed
);
1323 MOZ_ASSERT(slot
- numFixed
< getSlotsHeader()->capacity());
1324 checkStoredValue(value
);
1325 slots_
[slot
- numFixed
].set(this, HeapSlot::Slot
, slot
, value
);
1328 void initFixedSlot(uint32_t slot
, const Value
& value
) {
1329 MOZ_ASSERT(slotIsFixed(slot
));
1330 checkStoredValue(value
);
1331 fixedSlots()[slot
].init(this, HeapSlot::Slot
, slot
, value
);
1334 void initDynamicSlot(uint32_t numFixed
, uint32_t slot
, const Value
& value
) {
1335 MOZ_ASSERT(numFixedSlots() == numFixed
);
1336 MOZ_ASSERT(slot
>= numFixed
);
1337 MOZ_ASSERT(slot
- numFixed
< getSlotsHeader()->capacity());
1338 checkStoredValue(value
);
1339 slots_
[slot
- numFixed
].init(this, HeapSlot::Slot
, slot
, value
);
1342 template <typename T
>
1343 T
* maybePtrFromReservedSlot(uint32_t slot
) const {
1344 Value v
= getReservedSlot(slot
);
1345 return v
.isUndefined() ? nullptr : static_cast<T
*>(v
.toPrivate());
1349 * Calculate the number of dynamic slots to allocate to cover the properties
1350 * in an object with the given number of fixed slots and slot span.
1352 static MOZ_ALWAYS_INLINE
uint32_t calculateDynamicSlots(uint32_t nfixed
,
1354 const JSClass
* clasp
);
1355 static MOZ_ALWAYS_INLINE
uint32_t calculateDynamicSlots(SharedShape
* shape
);
1357 ObjectSlots
* getSlotsHeader() const { return ObjectSlots::fromSlots(slots_
); }
1359 /* Elements accessors. */
1361 // The maximum size, in sizeof(Value), of the allocation used for an
1362 // object's dense elements. (This includes space used to store an
1363 // ObjectElements instance.)
1364 // |MAX_DENSE_ELEMENTS_ALLOCATION * sizeof(JS::Value)| shouldn't overflow
1365 // int32_t (see elementsSizeMustNotOverflow).
1366 static const uint32_t MAX_DENSE_ELEMENTS_ALLOCATION
= (1 << 28) - 1;
1368 // The maximum number of usable dense elements in an object.
1369 static const uint32_t MAX_DENSE_ELEMENTS_COUNT
=
1370 MAX_DENSE_ELEMENTS_ALLOCATION
- ObjectElements::VALUES_PER_HEADER
;
1372 static void elementsSizeMustNotOverflow() {
1374 NativeObject::MAX_DENSE_ELEMENTS_COUNT
<= INT32_MAX
/ sizeof(JS::Value
),
1375 "every caller of this method require that an element "
1376 "count multiplied by sizeof(Value) can't overflow "
1377 "uint32_t (and sometimes int32_t ,too)");
1380 ObjectElements
* getElementsHeader() const {
1381 return ObjectElements::fromElements(elements_
);
1384 // Returns a pointer to the first element, including shifted elements.
1385 inline HeapSlot
* unshiftedElements() const {
1386 return elements_
- getElementsHeader()->numShiftedElements();
1389 // Like getElementsHeader, but returns a pointer to the unshifted header.
1390 // This is mainly useful for free()ing dynamic elements: the pointer
1391 // returned here is the one we got from malloc.
1392 void* getUnshiftedElementsHeader() const {
1393 return ObjectElements::fromElements(unshiftedElements());
1396 uint32_t unshiftedIndex(uint32_t index
) const {
1397 return index
+ getElementsHeader()->numShiftedElements();
1400 /* Accessors for elements. */
1401 bool ensureElements(JSContext
* cx
, uint32_t capacity
) {
1402 MOZ_ASSERT(isExtensible());
1403 if (capacity
> getDenseCapacity()) {
1404 return growElements(cx
, capacity
);
1409 // Try to shift |count| dense elements, see the "Shifted elements" comment.
1410 inline bool tryShiftDenseElements(uint32_t count
);
1412 // Try to make space for |count| dense elements at the start of the array.
1413 bool tryUnshiftDenseElements(uint32_t count
);
1415 // Move the elements header and all shifted elements to the start of the
1416 // allocated elements space, so that numShiftedElements is 0 afterwards.
1417 void moveShiftedElements();
1419 // If this object has many shifted elements call moveShiftedElements.
1420 void maybeMoveShiftedElements();
1422 static bool goodElementsAllocationAmount(JSContext
* cx
, uint32_t reqAllocated
,
1424 uint32_t* goodAmount
);
1425 bool growElements(JSContext
* cx
, uint32_t newcap
);
1426 void shrinkElements(JSContext
* cx
, uint32_t cap
);
1429 // Run a post write barrier that encompasses multiple contiguous elements in a
1431 inline void elementsRangePostWriteBarrier(uint32_t start
, uint32_t count
);
1434 void shrinkCapacityToInitializedLength(JSContext
* cx
);
1437 void setDenseInitializedLengthInternal(uint32_t length
) {
1438 MOZ_ASSERT(length
<= getDenseCapacity());
1439 MOZ_ASSERT(!denseElementsAreFrozen());
1440 prepareElementRangeForOverwrite(length
,
1441 getElementsHeader()->initializedLength
);
1442 getElementsHeader()->initializedLength
= length
;
1446 void setDenseInitializedLength(uint32_t length
) {
1447 MOZ_ASSERT(isExtensible());
1448 setDenseInitializedLengthInternal(length
);
1451 void setDenseInitializedLengthMaybeNonExtensible(JSContext
* cx
,
1453 setDenseInitializedLengthInternal(length
);
1454 if (!isExtensible()) {
1455 shrinkCapacityToInitializedLength(cx
);
1459 inline void ensureDenseInitializedLength(uint32_t index
, uint32_t extra
);
1461 void setDenseElement(uint32_t index
, const Value
& val
) {
1462 MOZ_ASSERT_IF(val
.isMagic(), val
.whyMagic() != JS_ELEMENTS_HOLE
);
1463 setDenseElementUnchecked(index
, val
);
1466 void initDenseElement(uint32_t index
, const Value
& val
) {
1467 MOZ_ASSERT(!val
.isMagic(JS_ELEMENTS_HOLE
));
1468 initDenseElementUnchecked(index
, val
);
1472 // Note: 'Unchecked' here means we don't assert |val| isn't the hole
1474 void initDenseElementUnchecked(uint32_t index
, const Value
& val
) {
1475 MOZ_ASSERT(index
< getDenseInitializedLength());
1476 MOZ_ASSERT(isExtensible());
1477 checkStoredValue(val
);
1478 elements_
[index
].init(this, HeapSlot::Element
, unshiftedIndex(index
), val
);
1480 void setDenseElementUnchecked(uint32_t index
, const Value
& val
) {
1481 MOZ_ASSERT(index
< getDenseInitializedLength());
1482 MOZ_ASSERT(!denseElementsAreFrozen());
1483 checkStoredValue(val
);
1484 elements_
[index
].set(this, HeapSlot::Element
, unshiftedIndex(index
), val
);
1487 // Mark the dense elements as possibly containing holes.
1488 inline void markDenseElementsNotPacked();
1491 inline void initDenseElementHole(uint32_t index
);
1492 inline void setDenseElementHole(uint32_t index
);
1493 inline void removeDenseElementForSparseIndex(uint32_t index
);
1495 inline void copyDenseElements(uint32_t dstStart
, const Value
* src
,
1498 inline void initDenseElements(const Value
* src
, uint32_t count
);
1499 inline void initDenseElements(NativeObject
* src
, uint32_t srcStart
,
1502 // Copy the first `count` dense elements from `src` to `this`, starting at
1503 // `destStart`. The initialized length must already include the new elements.
1504 inline void initDenseElementRange(uint32_t destStart
, NativeObject
* src
,
1507 // Store the Values in the range [begin, end) as elements of this array.
1509 // Preconditions: This must be a boring ArrayObject with dense initialized
1510 // length 0: no shifted elements, no frozen elements, no fixed "length", not
1511 // indexed, not inextensible, not copy-on-write. Existing capacity is
1514 // This runs write barriers but does not update types. `end - begin` must
1515 // return the size of the range, which must be >= 0 and fit in an int32_t.
1516 template <typename Iter
>
1517 [[nodiscard
]] inline bool initDenseElementsFromRange(JSContext
* cx
,
1518 Iter begin
, Iter end
);
1520 inline void moveDenseElements(uint32_t dstStart
, uint32_t srcStart
,
1522 inline void reverseDenseElementsNoPreBarrier(uint32_t length
);
1524 inline DenseElementResult
setOrExtendDenseElements(JSContext
* cx
,
1529 bool denseElementsAreSealed() const {
1530 return getElementsHeader()->isSealed();
1532 bool denseElementsAreFrozen() const {
1533 return hasFlag(ObjectFlag::FrozenElements
);
1536 bool denseElementsArePacked() const {
1537 return getElementsHeader()->isPacked();
1540 void markDenseElementsMaybeInIteration() {
1541 getElementsHeader()->markMaybeInIteration();
1544 // Return whether the object's dense elements might be in the midst of for-in
1545 // iteration. We rely on this to be able to safely delete or move dense array
1546 // elements without worrying about updating in-progress iterators.
1549 // Note that it's fine to return false if this object is on the prototype of
1550 // another object: SuppressDeletedProperty only suppresses properties deleted
1551 // from the iterated object itself.
1552 inline bool denseElementsHaveMaybeInIterationFlag();
1553 inline bool denseElementsMaybeInIteration();
1555 // Ensures that the object can hold at least index + extra elements. This
1556 // returns DenseElement_Success on success, DenseElement_Failed on failure
1557 // to grow the array, or DenseElement_Incomplete when the object is too
1558 // sparse to grow (this includes the case of index + extra overflow). In
1559 // the last two cases the object is kept intact.
1560 inline DenseElementResult
ensureDenseElements(JSContext
* cx
, uint32_t index
,
1563 inline DenseElementResult
extendDenseElements(JSContext
* cx
,
1564 uint32_t requiredCapacity
,
1567 /* Small objects are dense, no matter what. */
1568 static const uint32_t MIN_SPARSE_INDEX
= 1000;
1571 * Element storage for an object will be sparse if fewer than 1/8 indexes
1574 static const unsigned SPARSE_DENSITY_RATIO
= 8;
1577 * Check if after growing the object's elements will be too sparse.
1578 * newElementsHint is an estimated number of elements to be added.
1580 bool willBeSparseElements(uint32_t requiredCapacity
,
1581 uint32_t newElementsHint
);
1584 * After adding a sparse index to obj, see if it should be converted to use
1587 static DenseElementResult
maybeDensifySparseElements(
1588 JSContext
* cx
, Handle
<NativeObject
*> obj
);
1589 static bool densifySparseElements(JSContext
* cx
, Handle
<NativeObject
*> obj
);
1591 inline HeapSlot
* fixedElements() const {
1592 static_assert(2 * sizeof(Value
) == sizeof(ObjectElements
),
1593 "when elements are stored inline, the first two "
1594 "slots will hold the ObjectElements header");
1595 return &fixedSlots()[2];
1599 bool canHaveNonEmptyElements();
1602 void setEmptyElements() { elements_
= emptyObjectElements
; }
1604 void initFixedElements(gc::AllocKind kind
, uint32_t length
);
1606 // Update the elements pointer to use the fixed elements storage. The caller
1607 // is responsible for initializing the elements themselves and setting the
1609 void setFixedElements(uint32_t numShifted
= 0) {
1610 MOZ_ASSERT(canHaveNonEmptyElements());
1611 elements_
= fixedElements() + numShifted
;
1614 inline bool hasDynamicElements() const {
1616 * Note: for objects with zero fixed slots this could potentially give
1617 * a spurious 'true' result, if the end of this object is exactly
1618 * aligned with the end of its arena and dynamic slots are allocated
1619 * immediately afterwards. Such cases cannot occur for dense arrays
1620 * (which have at least two fixed slots) and can only result in a leak.
1622 return !hasEmptyElements() && !hasFixedElements();
1625 inline bool hasFixedElements() const {
1626 bool fixed
= getElementsHeader()->flags
& ObjectElements::FIXED
;
1627 MOZ_ASSERT_IF(fixed
, unshiftedElements() == fixedElements());
1631 inline bool hasEmptyElements() const {
1632 return elements_
== emptyObjectElements
||
1633 elements_
== emptyObjectElementsShared
;
1637 * Get a pointer to the unused data in the object's allocation immediately
1638 * following this object, for use with objects which allocate a larger size
1639 * class than they need and store non-elements data inline.
1641 inline uint8_t* fixedData(size_t nslots
) const;
1643 inline void privatePreWriteBarrier(HeapSlot
* pprivate
);
1645 // The methods below are used to store GC things in a reserved slot as
1646 // PrivateValues. This is done to bypass the normal tracing code (debugger
1647 // objects use this to store cross-compartment pointers).
1649 // WARNING: make sure you REALLY need this and you know what you're doing
1650 // before using these methods!
1651 void setReservedSlotGCThingAsPrivate(uint32_t slot
, gc::Cell
* cell
) {
1653 if (IsMarkedBlack(this)) {
1654 JS::AssertCellIsNotGray(cell
);
1657 HeapSlot
* pslot
= getSlotAddress(slot
);
1658 Cell
* prev
= nullptr;
1659 if (!pslot
->isUndefined()) {
1660 prev
= static_cast<gc::Cell
*>(pslot
->toPrivate());
1661 privatePreWriteBarrier(pslot
);
1663 setReservedSlotGCThingAsPrivateUnbarriered(slot
, cell
);
1664 gc::PostWriteBarrierCell(this, prev
, cell
);
1666 void setReservedSlotGCThingAsPrivateUnbarriered(uint32_t slot
,
1668 MOZ_ASSERT(slot
< JSCLASS_RESERVED_SLOTS(getClass()));
1670 getReservedSlotRef(slot
).unbarrieredSet(PrivateValue(cell
));
1672 void clearReservedSlotGCThingAsPrivate(uint32_t slot
) {
1673 MOZ_ASSERT(slot
< JSCLASS_RESERVED_SLOTS(getClass()));
1674 HeapSlot
* pslot
= &getReservedSlotRef(slot
);
1675 if (!pslot
->isUndefined()) {
1676 privatePreWriteBarrier(pslot
);
1677 pslot
->unbarrieredSet(UndefinedValue());
1681 /* Return the allocKind we would use if we were to tenure this object. */
1682 inline js::gc::AllocKind
allocKindForTenure() const;
1684 // Native objects are never wrappers, so a native object always has a realm
1686 JS::Realm
* realm() const { return nonCCWRealm(); }
1687 inline js::GlobalObject
& global() const;
1689 TaggedSlotOffset
getTaggedSlotOffset(size_t slot
) const {
1690 MOZ_ASSERT(slot
< slotSpan());
1691 uint32_t nfixed
= numFixedSlots();
1692 if (slot
< nfixed
) {
1693 return TaggedSlotOffset(getFixedSlotOffset(slot
),
1694 /* isFixedSlot = */ true);
1696 return TaggedSlotOffset((slot
- nfixed
) * sizeof(Value
),
1697 /* isFixedSlot = */ false);
1701 static size_t offsetOfElements() { return offsetof(NativeObject
, elements_
); }
1702 static size_t offsetOfFixedElements() {
1703 return sizeof(NativeObject
) + sizeof(ObjectElements
);
1706 static constexpr size_t getFixedSlotOffset(size_t slot
) {
1707 MOZ_ASSERT(slot
< MAX_FIXED_SLOTS
);
1708 return sizeof(NativeObject
) + slot
* sizeof(Value
);
1710 static constexpr size_t getFixedSlotIndexFromOffset(size_t offset
) {
1711 MOZ_ASSERT(offset
>= sizeof(NativeObject
));
1712 offset
-= sizeof(NativeObject
);
1713 MOZ_ASSERT(offset
% sizeof(Value
) == 0);
1714 MOZ_ASSERT(offset
/ sizeof(Value
) < MAX_FIXED_SLOTS
);
1715 return offset
/ sizeof(Value
);
1717 static constexpr size_t getDynamicSlotIndexFromOffset(size_t offset
) {
1718 MOZ_ASSERT(offset
% sizeof(Value
) == 0);
1719 return offset
/ sizeof(Value
);
1721 static size_t offsetOfSlots() { return offsetof(NativeObject
, slots_
); }
1724 inline void NativeObject::privatePreWriteBarrier(HeapSlot
* pprivate
) {
1725 JS::shadow::Zone
* shadowZone
= this->shadowZoneFromAnyThread();
1726 if (shadowZone
->needsIncrementalBarrier() && pprivate
->get().toPrivate() &&
1727 getClass()->hasTrace()) {
1728 getClass()->doTrace(shadowZone
->barrierTracer(), this);
1732 /*** Standard internal methods **********************************************/
1735 * These functions should follow the algorithms in ES6 draft rev 29 section 9.1
1736 * ("Ordinary Object Internal Methods"). It's an ongoing project.
1738 * Many native objects are not "ordinary" in ES6, so these functions also have
1739 * to serve some of the special needs of Functions (9.2, 9.3, 9.4.1), Arrays
1740 * (9.4.2), Strings (9.4.3), and so on.
1743 extern bool NativeDefineProperty(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1745 Handle
<JS::PropertyDescriptor
> desc
,
1746 ObjectOpResult
& result
);
1748 extern bool NativeDefineDataProperty(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1749 HandleId id
, HandleValue value
,
1750 unsigned attrs
, ObjectOpResult
& result
);
1752 /* If the result out-param is omitted, throw on failure. */
1754 extern bool NativeDefineAccessorProperty(JSContext
* cx
,
1755 Handle
<NativeObject
*> obj
, HandleId id
,
1756 HandleObject getter
,
1757 HandleObject setter
, unsigned attrs
);
1759 extern bool NativeDefineDataProperty(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1760 HandleId id
, HandleValue value
,
1763 extern bool NativeDefineDataProperty(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1764 PropertyName
* name
, HandleValue value
,
1767 extern bool NativeHasProperty(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1768 HandleId id
, bool* foundp
);
1770 extern bool NativeGetOwnPropertyDescriptor(
1771 JSContext
* cx
, Handle
<NativeObject
*> obj
, HandleId id
,
1772 MutableHandle
<mozilla::Maybe
<JS::PropertyDescriptor
>> desc
);
1774 extern bool NativeGetProperty(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1775 HandleValue receiver
, HandleId id
,
1776 MutableHandleValue vp
);
1778 extern bool NativeGetPropertyNoGC(JSContext
* cx
, NativeObject
* obj
,
1779 const Value
& receiver
, jsid id
, Value
* vp
);
1781 inline bool NativeGetProperty(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1782 HandleId id
, MutableHandleValue vp
) {
1783 RootedValue
receiver(cx
, ObjectValue(*obj
));
1784 return NativeGetProperty(cx
, obj
, receiver
, id
, vp
);
1787 extern bool NativeGetElement(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1788 HandleValue receiver
, int32_t index
,
1789 MutableHandleValue vp
);
1791 bool GetSparseElementHelper(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1792 int32_t int_id
, MutableHandleValue result
);
1794 bool SetPropertyByDefining(JSContext
* cx
, HandleId id
, HandleValue v
,
1795 HandleValue receiver
, ObjectOpResult
& result
);
1797 bool SetPropertyOnProto(JSContext
* cx
, HandleObject obj
, HandleId id
,
1798 HandleValue v
, HandleValue receiver
,
1799 ObjectOpResult
& result
);
1801 bool AddOrUpdateSparseElementHelper(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1802 int32_t int_id
, HandleValue v
, bool strict
);
1805 * Indicates whether an assignment operation is qualified (`x.y = 0`) or
1806 * unqualified (`y = 0`). In strict mode, the latter is an error if no such
1807 * variable already exists.
1809 * Used as an argument to NativeSetProperty.
1811 enum QualifiedBool
{ Unqualified
= 0, Qualified
= 1 };
1813 template <QualifiedBool Qualified
>
1814 extern bool NativeSetProperty(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1815 HandleId id
, HandleValue v
, HandleValue receiver
,
1816 ObjectOpResult
& result
);
1818 extern bool NativeSetElement(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1819 uint32_t index
, HandleValue v
,
1820 HandleValue receiver
, ObjectOpResult
& result
);
1822 extern bool NativeDeleteProperty(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1823 HandleId id
, ObjectOpResult
& result
);
1825 /*** SpiderMonkey nonstandard internal methods ******************************/
1827 template <AllowGC allowGC
>
1828 extern bool NativeLookupOwnProperty(
1829 JSContext
* cx
, typename MaybeRooted
<NativeObject
*, allowGC
>::HandleType obj
,
1830 typename MaybeRooted
<jsid
, allowGC
>::HandleType id
, PropertyResult
* propp
);
1833 * Get a property from `receiver`, after having already done a lookup and found
1834 * the property on a native object `obj`.
1836 * `prop` must be present in obj's shape.
1838 extern bool NativeGetExistingProperty(JSContext
* cx
, HandleObject receiver
,
1839 Handle
<NativeObject
*> obj
, HandleId id
,
1840 PropertyInfo prop
, MutableHandleValue vp
);
1844 extern bool GetNameBoundInEnvironment(JSContext
* cx
, HandleObject env
,
1845 HandleId id
, MutableHandleValue vp
);
1847 } /* namespace js */
1850 inline bool JSObject::is
<js::NativeObject
>() const {
1851 return shape()->isNative();
1856 // Alternate to JSObject::as<NativeObject>() that tolerates null pointers.
1857 inline NativeObject
* MaybeNativeObject(JSObject
* obj
) {
1858 return obj
? &obj
->as
<NativeObject
>() : nullptr;
1861 // Defined in NativeObject-inl.h.
1862 bool IsPackedArray(JSObject
* obj
);
1864 // Initialize an object's reserved slot with a private value pointing to
1865 // malloc-allocated memory and associate the memory with the object.
1867 // This call should be matched with a call to JS::GCContext::free_/delete_ in
1868 // the object's finalizer to free the memory and update the memory accounting.
1870 inline void InitReservedSlot(NativeObject
* obj
, uint32_t slot
, void* ptr
,
1871 size_t nbytes
, MemoryUse use
) {
1872 AddCellMemory(obj
, nbytes
, use
);
1873 obj
->initReservedSlot(slot
, PrivateValue(ptr
));
1875 template <typename T
>
1876 inline void InitReservedSlot(NativeObject
* obj
, uint32_t slot
, T
* ptr
,
1878 InitReservedSlot(obj
, slot
, ptr
, sizeof(T
), use
);
1881 bool AddSlotAndCallAddPropHook(JSContext
* cx
, Handle
<NativeObject
*> obj
,
1882 HandleValue v
, Handle
<Shape
*> newShape
);
1886 #endif /* vm_NativeObject_h */