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