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