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