Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
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 :
13 : #include <stdint.h>
14 :
15 : #include "jsfriendapi.h"
16 : #include "jsobj.h"
17 : #include "NamespaceImports.h"
18 :
19 : #include "gc/Barrier.h"
20 : #include "gc/Heap.h"
21 : #include "gc/Marking.h"
22 : #include "js/Value.h"
23 : #include "vm/Shape.h"
24 : #include "vm/ShapedObject.h"
25 : #include "vm/String.h"
26 : #include "vm/TypeInference.h"
27 :
28 : namespace js {
29 :
30 : class Shape;
31 : class TenuringTracer;
32 :
33 : /*
34 : * To really poison a set of values, using 'magic' or 'undefined' isn't good
35 : * enough since often these will just be ignored by buggy code (see bug 629974)
36 : * in debug builds and crash in release builds. Instead, we use a safe-for-crash
37 : * pointer.
38 : */
39 : static MOZ_ALWAYS_INLINE void
40 17455 : Debug_SetValueRangeToCrashOnTouch(Value* beg, Value* end)
41 : {
42 : #ifdef DEBUG
43 280449 : for (Value* v = beg; v != end; ++v)
44 262994 : v->setObject(*reinterpret_cast<JSObject*>(0x48));
45 : #endif
46 17455 : }
47 :
48 : static MOZ_ALWAYS_INLINE void
49 17455 : Debug_SetValueRangeToCrashOnTouch(Value* vec, size_t len)
50 : {
51 : #ifdef DEBUG
52 17455 : Debug_SetValueRangeToCrashOnTouch(vec, vec + len);
53 : #endif
54 17455 : }
55 :
56 : static MOZ_ALWAYS_INLINE void
57 : Debug_SetValueRangeToCrashOnTouch(GCPtrValue* vec, size_t len)
58 : {
59 : #ifdef DEBUG
60 : Debug_SetValueRangeToCrashOnTouch((Value*) vec, len);
61 : #endif
62 : }
63 :
64 : static MOZ_ALWAYS_INLINE void
65 16926 : Debug_SetSlotRangeToCrashOnTouch(HeapSlot* vec, uint32_t len)
66 : {
67 : #ifdef DEBUG
68 16926 : Debug_SetValueRangeToCrashOnTouch((Value*) vec, len);
69 : #endif
70 16926 : }
71 :
72 : static MOZ_ALWAYS_INLINE void
73 32 : Debug_SetSlotRangeToCrashOnTouch(HeapSlot* begin, HeapSlot* end)
74 : {
75 : #ifdef DEBUG
76 32 : Debug_SetValueRangeToCrashOnTouch((Value*) begin, end - begin);
77 : #endif
78 32 : }
79 :
80 : class ArrayObject;
81 :
82 : /*
83 : * ES6 20130308 draft 8.4.2.4 ArraySetLength.
84 : *
85 : * |id| must be "length", |attrs| are the attributes to be used for the newly-
86 : * changed length property, |value| is the value for the new length, and
87 : * |result| receives an error code if the change is invalid.
88 : */
89 : extern bool
90 : ArraySetLength(JSContext* cx, Handle<ArrayObject*> obj, HandleId id,
91 : unsigned attrs, HandleValue value, ObjectOpResult& result);
92 :
93 : /*
94 : * Elements header used for native objects. The elements component of such objects
95 : * offers an efficient representation for all or some of the indexed properties
96 : * of the object, using a flat array of Values rather than a shape hierarchy
97 : * stored in the object's slots. This structure is immediately followed by an
98 : * array of elements, with the elements member in an object pointing to the
99 : * beginning of that array (the end of this structure).
100 : * See below for usage of this structure.
101 : *
102 : * The sets of properties represented by an object's elements and slots
103 : * are disjoint. The elements contain only indexed properties, while the slots
104 : * can contain both named and indexed properties; any indexes in the slots are
105 : * distinct from those in the elements. If isIndexed() is false for an object,
106 : * all indexed properties (if any) are stored in the dense elements.
107 : *
108 : * Indexes will be stored in the object's slots instead of its elements in
109 : * the following case:
110 : * - there are more than MIN_SPARSE_INDEX slots total and the load factor
111 : * (COUNT / capacity) is less than 0.25
112 : * - a property is defined that has non-default property attributes.
113 : *
114 : * We track these pieces of metadata for dense elements:
115 : * - The length property as a uint32_t, accessible for array objects with
116 : * ArrayObject::{length,setLength}(). This is unused for non-arrays.
117 : * - The number of element slots (capacity), gettable with
118 : * getDenseCapacity().
119 : * - The array's initialized length, accessible with
120 : * getDenseInitializedLength().
121 : *
122 : * Holes in the array are represented by MagicValue(JS_ELEMENTS_HOLE) values.
123 : * These indicate indexes which are not dense properties of the array. The
124 : * property may, however, be held by the object's properties.
125 : *
126 : * The capacity and length of an object's elements are almost entirely
127 : * unrelated! In general the length may be greater than, less than, or equal
128 : * to the capacity. The first case occurs with |new Array(100)|. The length
129 : * is 100, but the capacity remains 0 (indices below length and above capacity
130 : * must be treated as holes) until elements between capacity and length are
131 : * set. The other two cases are common, depending upon the number of elements
132 : * in an array and the underlying allocator used for element storage.
133 : *
134 : * The only case in which the capacity and length of an object's elements are
135 : * related is when the object is an array with non-writable length. In this
136 : * case the capacity is always less than or equal to the length. This permits
137 : * JIT code to optimize away the check for non-writable length when assigning
138 : * to possibly out-of-range elements: such code already has to check for
139 : * |index < capacity|, and fallback code checks for non-writable length.
140 : *
141 : * The initialized length of an object specifies the number of elements that
142 : * have been initialized. All elements above the initialized length are
143 : * holes in the object, and the memory for all elements between the initialized
144 : * length and capacity is left uninitialized. The initialized length is some
145 : * value less than or equal to both the object's length and the object's
146 : * capacity.
147 : *
148 : * There is flexibility in exactly the value the initialized length must hold,
149 : * e.g. if an array has length 5, capacity 10, completely empty, it is valid
150 : * for the initialized length to be any value between zero and 5, as long as
151 : * the in memory values below the initialized length have been initialized with
152 : * a hole value. However, in such cases we want to keep the initialized length
153 : * as small as possible: if the object is known to have no hole values below
154 : * its initialized length, then it is "packed" and can be accessed much faster
155 : * by JIT code.
156 : *
157 : * Elements do not track property creation order, so enumerating the elements
158 : * of an object does not necessarily visit indexes in the order they were
159 : * created.
160 : *
161 : * Shifted elements
162 : * ----------------
163 : * It's pretty common to use an array as a queue, like this:
164 : *
165 : * while (arr.length > 0)
166 : * foo(arr.shift());
167 : *
168 : * To ensure we don't get quadratic behavior on this, elements can be 'shifted'
169 : * in memory. tryShiftDenseElements does this by incrementing elements_ to point
170 : * to the next element and moving the ObjectElements header in memory (so it's
171 : * stored where the shifted Value used to be).
172 : *
173 : * Shifted elements can be moved when we grow the array, when the array is
174 : * frozen (for simplicity, shifted elements are not supported on objects that
175 : * are frozen, have copy-on-write elements, or on arrays with non-writable
176 : * length).
177 : */
178 : class ObjectElements
179 : {
180 : public:
181 : enum Flags: uint16_t {
182 : // Integers written to these elements must be converted to doubles.
183 : CONVERT_DOUBLE_ELEMENTS = 0x1,
184 :
185 : // Present only if these elements correspond to an array with
186 : // non-writable length; never present for non-arrays.
187 : NONWRITABLE_ARRAY_LENGTH = 0x2,
188 :
189 : // These elements are shared with another object and must be copied
190 : // before they can be changed. A pointer to the original owner of the
191 : // elements, which is immutable, is stored immediately after the
192 : // elements data. There is one case where elements can be written to
193 : // before being copied: when setting the CONVERT_DOUBLE_ELEMENTS flag
194 : // the shared elements may change (from ints to doubles) without
195 : // making a copy first.
196 : COPY_ON_WRITE = 0x4,
197 :
198 : // For TypedArrays only: this TypedArray's storage is mapping shared
199 : // memory. This is a static property of the TypedArray, set when it
200 : // is created and never changed.
201 : SHARED_MEMORY = 0x8,
202 :
203 : // These elements are set to integrity level "frozen".
204 : FROZEN = 0x10,
205 : };
206 :
207 : // The flags word stores both the flags and the number of shifted elements.
208 : // Allow shifting 2047 elements before actually moving the elements.
209 : static const size_t NumShiftedElementsBits = 11;
210 : static const size_t MaxShiftedElements = (1 << NumShiftedElementsBits) - 1;
211 : static const size_t NumShiftedElementsShift = 32 - NumShiftedElementsBits;
212 : static const size_t FlagsMask = (1 << NumShiftedElementsShift) - 1;
213 : static_assert(MaxShiftedElements == 2047,
214 : "MaxShiftedElements should match the comment");
215 :
216 : private:
217 : friend class ::JSObject;
218 : friend class ArrayObject;
219 : friend class NativeObject;
220 : friend class TenuringTracer;
221 :
222 : friend bool js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level);
223 :
224 : friend bool
225 : ArraySetLength(JSContext* cx, Handle<ArrayObject*> obj, HandleId id,
226 : unsigned attrs, HandleValue value, ObjectOpResult& result);
227 :
228 : // The NumShiftedElementsBits high bits of this are used to store the
229 : // number of shifted elements, the other bits are available for the flags.
230 : // See Flags enum above.
231 : uint32_t flags;
232 :
233 : /*
234 : * Number of initialized elements. This is <= the capacity, and for arrays
235 : * is <= the length. Memory for elements above the initialized length is
236 : * uninitialized, but values between the initialized length and the proper
237 : * length are conceptually holes.
238 : */
239 : uint32_t initializedLength;
240 :
241 : /* Number of allocated slots. */
242 : uint32_t capacity;
243 :
244 : /* 'length' property of array objects, unused for other objects. */
245 : uint32_t length;
246 :
247 3734 : bool shouldConvertDoubleElements() const {
248 3734 : return flags & CONVERT_DOUBLE_ELEMENTS;
249 : }
250 0 : void setShouldConvertDoubleElements() {
251 : // Note: allow isCopyOnWrite() here, see comment above.
252 0 : flags |= CONVERT_DOUBLE_ELEMENTS;
253 0 : }
254 : void clearShouldConvertDoubleElements() {
255 : MOZ_ASSERT(!isCopyOnWrite());
256 : flags &= ~CONVERT_DOUBLE_ELEMENTS;
257 : }
258 15708 : bool hasNonwritableArrayLength() const {
259 15708 : return flags & NONWRITABLE_ARRAY_LENGTH;
260 : }
261 8 : void setNonwritableArrayLength() {
262 : // See ArrayObject::setNonWritableLength.
263 8 : MOZ_ASSERT(capacity == initializedLength);
264 8 : MOZ_ASSERT(numShiftedElements() == 0);
265 8 : MOZ_ASSERT(!isCopyOnWrite());
266 8 : flags |= NONWRITABLE_ARRAY_LENGTH;
267 8 : }
268 76466 : bool isCopyOnWrite() const {
269 76466 : return flags & COPY_ON_WRITE;
270 : }
271 12 : void clearCopyOnWrite() {
272 12 : MOZ_ASSERT(isCopyOnWrite());
273 12 : flags &= ~COPY_ON_WRITE;
274 12 : }
275 :
276 7 : void addShiftedElements(uint32_t count) {
277 7 : MOZ_ASSERT(count < capacity);
278 7 : MOZ_ASSERT(count < initializedLength);
279 7 : MOZ_ASSERT(!(flags & (NONWRITABLE_ARRAY_LENGTH | FROZEN | COPY_ON_WRITE)));
280 7 : uint32_t numShifted = numShiftedElements() + count;
281 7 : MOZ_ASSERT(numShifted <= MaxShiftedElements);
282 7 : flags = (numShifted << NumShiftedElementsShift) | (flags & FlagsMask);
283 7 : capacity -= count;
284 7 : initializedLength -= count;
285 7 : }
286 0 : void unshiftShiftedElements(uint32_t count) {
287 0 : MOZ_ASSERT(count > 0);
288 0 : MOZ_ASSERT(!(flags & (NONWRITABLE_ARRAY_LENGTH | FROZEN | COPY_ON_WRITE)));
289 0 : uint32_t numShifted = numShiftedElements();
290 0 : MOZ_ASSERT(count <= numShifted);
291 0 : numShifted -= count;
292 0 : flags = (numShifted << NumShiftedElementsShift) | (flags & FlagsMask);
293 0 : capacity += count;
294 0 : initializedLength += count;
295 0 : }
296 0 : void clearShiftedElements() {
297 0 : flags &= FlagsMask;
298 0 : MOZ_ASSERT(numShiftedElements() == 0);
299 0 : }
300 :
301 : public:
302 2829 : constexpr ObjectElements(uint32_t capacity, uint32_t length)
303 2829 : : flags(0), initializedLength(0), capacity(capacity), length(length)
304 2829 : {}
305 :
306 : enum class SharedMemory {
307 : IsShared
308 : };
309 :
310 : constexpr ObjectElements(uint32_t capacity, uint32_t length, SharedMemory shmem)
311 : : flags(SHARED_MEMORY), initializedLength(0), capacity(capacity), length(length)
312 : {}
313 :
314 2870 : HeapSlot* elements() {
315 2870 : return reinterpret_cast<HeapSlot*>(uintptr_t(this) + sizeof(ObjectElements));
316 : }
317 2322 : const HeapSlot* elements() const {
318 2322 : return reinterpret_cast<const HeapSlot*>(uintptr_t(this) + sizeof(ObjectElements));
319 : }
320 354583 : static ObjectElements* fromElements(HeapSlot* elems) {
321 354583 : return reinterpret_cast<ObjectElements*>(uintptr_t(elems) - sizeof(ObjectElements));
322 : }
323 :
324 71 : bool isSharedMemory() const {
325 71 : return flags & SHARED_MEMORY;
326 : }
327 :
328 2322 : GCPtrNativeObject& ownerObject() const {
329 2322 : MOZ_ASSERT(isCopyOnWrite());
330 2322 : return *(GCPtrNativeObject*)(&elements()[initializedLength]);
331 : }
332 :
333 11 : static int offsetOfFlags() {
334 11 : return int(offsetof(ObjectElements, flags)) - int(sizeof(ObjectElements));
335 : }
336 44 : static int offsetOfInitializedLength() {
337 44 : return int(offsetof(ObjectElements, initializedLength)) - int(sizeof(ObjectElements));
338 : }
339 10 : static int offsetOfCapacity() {
340 10 : return int(offsetof(ObjectElements, capacity)) - int(sizeof(ObjectElements));
341 : }
342 25 : static int offsetOfLength() {
343 25 : return int(offsetof(ObjectElements, length)) - int(sizeof(ObjectElements));
344 : }
345 :
346 : static bool ConvertElementsToDoubles(JSContext* cx, uintptr_t elements);
347 : static bool MakeElementsCopyOnWrite(JSContext* cx, NativeObject* obj);
348 : static bool FreezeElements(JSContext* cx, HandleNativeObject obj);
349 :
350 37292 : bool isFrozen() const {
351 37292 : return flags & FROZEN;
352 : }
353 8 : void freeze() {
354 8 : MOZ_ASSERT(!isFrozen());
355 8 : MOZ_ASSERT(!isCopyOnWrite());
356 8 : flags |= FROZEN;
357 8 : }
358 :
359 0 : uint8_t elementAttributes() const {
360 0 : if (isFrozen())
361 0 : return JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY;
362 0 : return JSPROP_ENUMERATE;
363 : }
364 :
365 58680 : uint32_t numShiftedElements() const {
366 58680 : uint32_t numShifted = flags >> NumShiftedElementsShift;
367 58680 : MOZ_ASSERT_IF(numShifted > 0,
368 : !(flags & (NONWRITABLE_ARRAY_LENGTH | FROZEN | COPY_ON_WRITE)));
369 58680 : return numShifted;
370 : }
371 :
372 1105 : uint32_t numAllocatedElements() const {
373 1105 : return VALUES_PER_HEADER + capacity + numShiftedElements();
374 : }
375 :
376 : // This is enough slots to store an object of this class. See the static
377 : // assertion below.
378 : static const size_t VALUES_PER_HEADER = 2;
379 : };
380 :
381 : static_assert(ObjectElements::VALUES_PER_HEADER * sizeof(HeapSlot) == sizeof(ObjectElements),
382 : "ObjectElements doesn't fit in the given number of slots");
383 :
384 : /*
385 : * Shared singletons for objects with no elements.
386 : * emptyObjectElementsShared is used only for TypedArrays, when the TA
387 : * maps shared memory.
388 : */
389 : extern HeapSlot* const emptyObjectElements;
390 : extern HeapSlot* const emptyObjectElementsShared;
391 :
392 : struct Class;
393 : class GCMarker;
394 : class Shape;
395 :
396 : class NewObjectCache;
397 :
398 : // Operations which change an object's dense elements can either succeed, fail,
399 : // or be unable to complete. For native objects, the latter is used when the
400 : // object's elements must become sparse instead. The enum below is used for
401 : // such operations, and for similar operations on unboxed arrays and methods
402 : // that work on both kinds of objects.
403 : enum class DenseElementResult {
404 : Failure,
405 : Success,
406 : Incomplete
407 : };
408 :
409 : /*
410 : * NativeObject specifies the internal implementation of a native object.
411 : *
412 : * Native objects use ShapedObject::shape_ to record property information. Two
413 : * native objects with the same shape are guaranteed to have the same number of
414 : * fixed slots.
415 : *
416 : * Native objects extend the base implementation of an object with storage for
417 : * the object's named properties and indexed elements.
418 : *
419 : * These are stored separately from one another. Objects are followed by a
420 : * variable-sized array of values for inline storage, which may be used by
421 : * either properties of native objects (fixed slots), by elements (fixed
422 : * elements), or by other data for certain kinds of objects, such as
423 : * ArrayBufferObjects and TypedArrayObjects.
424 : *
425 : * Named property storage can be split between fixed slots and a dynamically
426 : * allocated array (the slots member). For an object with N fixed slots, shapes
427 : * with slots [0..N-1] are stored in the fixed slots, and the remainder are
428 : * stored in the dynamic array. If all properties fit in the fixed slots, the
429 : * 'slots_' member is nullptr.
430 : *
431 : * Elements are indexed via the 'elements_' member. This member can point to
432 : * either the shared emptyObjectElements and emptyObjectElementsShared singletons,
433 : * into the inline value array (the address of the third value, to leave room
434 : * for a ObjectElements header;in this case numFixedSlots() is zero) or to
435 : * a dynamically allocated array.
436 : *
437 : * Slots and elements may both be non-empty. The slots may be either names or
438 : * indexes; no indexed property will be in both the slots and elements.
439 : */
440 : class NativeObject : public ShapedObject
441 : {
442 : protected:
443 : /* Slots for object properties. */
444 : js::HeapSlot* slots_;
445 :
446 : /* Slots for object dense elements. */
447 : js::HeapSlot* elements_;
448 :
449 : friend class ::JSObject;
450 :
451 : private:
452 : static void staticAsserts() {
453 : static_assert(sizeof(NativeObject) == sizeof(JSObject_Slots0),
454 : "native object size must match GC thing size");
455 : static_assert(sizeof(NativeObject) == sizeof(shadow::Object),
456 : "shadow interface must match actual implementation");
457 : static_assert(sizeof(NativeObject) % sizeof(Value) == 0,
458 : "fixed slots after an object must be aligned");
459 :
460 : static_assert(offsetof(NativeObject, group_) == offsetof(shadow::Object, group),
461 : "shadow type must match actual type");
462 : static_assert(offsetof(NativeObject, slots_) == offsetof(shadow::Object, slots),
463 : "shadow slots must match actual slots");
464 : static_assert(offsetof(NativeObject, elements_) == offsetof(shadow::Object, _1),
465 : "shadow placeholder must match actual elements");
466 :
467 : static_assert(MAX_FIXED_SLOTS <= Shape::FIXED_SLOTS_MAX,
468 : "verify numFixedSlots() bitfield is big enough");
469 : static_assert(sizeof(NativeObject) + MAX_FIXED_SLOTS * sizeof(Value) == JSObject::MAX_BYTE_SIZE,
470 : "inconsistent maximum object size");
471 : }
472 :
473 : public:
474 6962473 : Shape* lastProperty() const {
475 6962473 : MOZ_ASSERT(shape_);
476 6962485 : return shape_;
477 : }
478 :
479 : uint32_t propertyCount() const {
480 : return lastProperty()->entryCount();
481 : }
482 :
483 0 : bool hasShapeTable() const {
484 0 : return lastProperty()->hasTable();
485 : }
486 :
487 21082 : HeapSlotArray getDenseElements() {
488 21082 : return HeapSlotArray(elements_, !getElementsHeader()->isCopyOnWrite());
489 : }
490 909 : HeapSlotArray getDenseElementsAllowCopyOnWrite() {
491 : // Backdoor allowing direct access to copy on write elements.
492 909 : return HeapSlotArray(elements_, true);
493 : }
494 20632 : const Value& getDenseElement(uint32_t idx) const {
495 20632 : MOZ_ASSERT(idx < getDenseInitializedLength());
496 20632 : return elements_[idx];
497 : }
498 10430 : bool containsDenseElement(uint32_t idx) {
499 10430 : return idx < getDenseInitializedLength() && !elements_[idx].isMagic(JS_ELEMENTS_HOLE);
500 : }
501 66520 : uint32_t getDenseInitializedLength() const {
502 66520 : return getElementsHeader()->initializedLength;
503 : }
504 41411 : uint32_t getDenseCapacity() const {
505 41411 : return getElementsHeader()->capacity;
506 : }
507 :
508 71 : bool isSharedMemory() const {
509 71 : return getElementsHeader()->isSharedMemory();
510 : }
511 :
512 : // Update the last property, keeping the number of allocated slots in sync
513 : // with the object's new slot span.
514 : MOZ_ALWAYS_INLINE bool setLastProperty(JSContext* cx, Shape* shape);
515 :
516 : // As for setLastProperty(), but allows the number of fixed slots to
517 : // change. This can only be used when fixed slots are being erased from the
518 : // object, and only when the object will not require dynamic slots to cover
519 : // the new properties.
520 : void setLastPropertyShrinkFixedSlots(Shape* shape);
521 :
522 : // As for setLastProperty(), but changes the class associated with the
523 : // object to a non-native one. This leaves the object with a type and shape
524 : // that are (temporarily) inconsistent.
525 : void setLastPropertyMakeNonNative(Shape* shape);
526 :
527 : // As for setLastProperty(), but changes the class associated with the
528 : // object to a native one. The object's type has already been changed, and
529 : // this brings the shape into sync with it.
530 : void setLastPropertyMakeNative(JSContext* cx, Shape* shape);
531 :
532 : // Newly-created TypedArrays that map a SharedArrayBuffer are
533 : // marked as shared by giving them an ObjectElements that has the
534 : // ObjectElements::SHARED_MEMORY flag set.
535 0 : void setIsSharedMemory() {
536 0 : MOZ_ASSERT(elements_ == emptyObjectElements);
537 0 : elements_ = emptyObjectElementsShared;
538 0 : }
539 :
540 : inline bool isInWholeCellBuffer() const;
541 :
542 : static inline JS::Result<NativeObject*, JS::OOM&>
543 : create(JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
544 : js::HandleShape shape, js::HandleObjectGroup group);
545 :
546 : protected:
547 : #ifdef DEBUG
548 : void checkShapeConsistency();
549 : #else
550 : void checkShapeConsistency() { }
551 : #endif
552 :
553 : static Shape*
554 : replaceWithNewEquivalentShape(JSContext* cx, HandleNativeObject obj,
555 : Shape* existingShape, Shape* newShape = nullptr,
556 : bool accessorShape = false);
557 :
558 : /*
559 : * Remove the last property of an object, provided that it is safe to do so
560 : * (the shape and previous shape do not carry conflicting information about
561 : * the object itself).
562 : */
563 : inline void removeLastProperty(JSContext* cx);
564 : inline bool canRemoveLastProperty();
565 :
566 : /*
567 : * Update the slot span directly for a dictionary object, and allocate
568 : * slots to cover the new span if necessary.
569 : */
570 : bool setSlotSpan(JSContext* cx, uint32_t span);
571 :
572 : static MOZ_MUST_USE bool toDictionaryMode(JSContext* cx, HandleNativeObject obj);
573 :
574 : private:
575 : friend class TenuringTracer;
576 :
577 : /*
578 : * Get internal pointers to the range of values starting at start and
579 : * running for length.
580 : */
581 44376 : void getSlotRangeUnchecked(uint32_t start, uint32_t length,
582 : HeapSlot** fixedStart, HeapSlot** fixedEnd,
583 : HeapSlot** slotsStart, HeapSlot** slotsEnd)
584 : {
585 44376 : MOZ_ASSERT(start + length >= start);
586 :
587 44376 : uint32_t fixed = numFixedSlots();
588 44376 : if (start < fixed) {
589 27074 : if (start + length < fixed) {
590 12979 : *fixedStart = &fixedSlots()[start];
591 12979 : *fixedEnd = &fixedSlots()[start + length];
592 12979 : *slotsStart = *slotsEnd = nullptr;
593 : } else {
594 14095 : uint32_t localCopy = fixed - start;
595 14095 : *fixedStart = &fixedSlots()[start];
596 14095 : *fixedEnd = &fixedSlots()[start + localCopy];
597 14095 : *slotsStart = &slots_[0];
598 14095 : *slotsEnd = &slots_[length - localCopy];
599 : }
600 : } else {
601 17302 : *fixedStart = *fixedEnd = nullptr;
602 17302 : *slotsStart = &slots_[start - fixed];
603 17302 : *slotsEnd = &slots_[start - fixed + length];
604 : }
605 44376 : }
606 :
607 24619 : void getSlotRange(uint32_t start, uint32_t length,
608 : HeapSlot** fixedStart, HeapSlot** fixedEnd,
609 : HeapSlot** slotsStart, HeapSlot** slotsEnd)
610 : {
611 24619 : MOZ_ASSERT(slotInRange(start + length, SENTINEL_ALLOWED));
612 24619 : getSlotRangeUnchecked(start, length, fixedStart, fixedEnd, slotsStart, slotsEnd);
613 24619 : }
614 :
615 : protected:
616 : friend class GCMarker;
617 : friend class Shape;
618 : friend class NewObjectCache;
619 :
620 16 : void invalidateSlotRange(uint32_t start, uint32_t length) {
621 : #ifdef DEBUG
622 : HeapSlot* fixedStart;
623 : HeapSlot* fixedEnd;
624 : HeapSlot* slotsStart;
625 : HeapSlot* slotsEnd;
626 16 : getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
627 16 : Debug_SetSlotRangeToCrashOnTouch(fixedStart, fixedEnd);
628 16 : Debug_SetSlotRangeToCrashOnTouch(slotsStart, slotsEnd);
629 : #endif /* DEBUG */
630 16 : }
631 :
632 : void initializeSlotRange(uint32_t start, uint32_t count);
633 :
634 : /*
635 : * Initialize a flat array of slots to this object at a start slot. The
636 : * caller must ensure that are enough slots.
637 : */
638 : void initSlotRange(uint32_t start, const Value* vector, uint32_t length);
639 :
640 : /*
641 : * Copy a flat array of slots to this object at a start slot. Caller must
642 : * ensure there are enough slots in this object.
643 : */
644 : void copySlotRange(uint32_t start, const Value* vector, uint32_t length);
645 :
646 : #ifdef DEBUG
647 : enum SentinelAllowed {
648 : SENTINEL_NOT_ALLOWED,
649 : SENTINEL_ALLOWED
650 : };
651 :
652 : /*
653 : * Check that slot is in range for the object's allocated slots.
654 : * If sentinelAllowed then slot may equal the slot capacity.
655 : */
656 : bool slotInRange(uint32_t slot, SentinelAllowed sentinel = SENTINEL_NOT_ALLOWED) const;
657 : #endif
658 :
659 : /*
660 : * Minimum size for dynamically allocated slots in normal Objects.
661 : * ArrayObjects don't use this limit and can have a lower slot capacity,
662 : * since they normally don't have a lot of slots.
663 : */
664 : static const uint32_t SLOT_CAPACITY_MIN = 8;
665 :
666 1534009 : HeapSlot* fixedSlots() const {
667 1534009 : return reinterpret_cast<HeapSlot*>(uintptr_t(this) + sizeof(NativeObject));
668 : }
669 :
670 : public:
671 4006 : static MOZ_MUST_USE bool generateOwnShape(JSContext* cx, HandleNativeObject obj,
672 : Shape* newShape = nullptr)
673 : {
674 4006 : return replaceWithNewEquivalentShape(cx, obj, obj->lastProperty(), newShape);
675 : }
676 :
677 : static MOZ_MUST_USE bool shadowingShapeChange(JSContext* cx, HandleNativeObject obj,
678 : const Shape& shape);
679 : static bool clearFlag(JSContext* cx, HandleNativeObject obj, BaseShape::Flag flag);
680 :
681 : // The maximum number of slots in an object.
682 : // |MAX_SLOTS_COUNT * sizeof(JS::Value)| shouldn't overflow
683 : // int32_t (see slotsSizeMustNotOverflow).
684 : static const uint32_t MAX_SLOTS_COUNT = (1 << 28) - 1;
685 :
686 15965 : static void slotsSizeMustNotOverflow() {
687 : static_assert(NativeObject::MAX_SLOTS_COUNT <= INT32_MAX / sizeof(JS::Value),
688 : "every caller of this method requires that a slot "
689 : "number (or slot count) count multiplied by "
690 : "sizeof(Value) can't overflow uint32_t (and sometimes "
691 : "int32_t, too)");
692 15965 : }
693 :
694 7720381 : uint32_t numFixedSlots() const {
695 7720381 : return reinterpret_cast<const shadow::Object*>(this)->numFixedSlots();
696 : }
697 35 : uint32_t numUsedFixedSlots() const {
698 35 : uint32_t nslots = lastProperty()->slotSpan(getClass());
699 35 : return Min(nslots, numFixedSlots());
700 : }
701 : uint32_t numFixedSlotsForCompilation() const;
702 :
703 2539816 : uint32_t slotSpan() const {
704 2539816 : if (inDictionaryMode())
705 189581 : return lastProperty()->base()->slotSpan();
706 2350188 : return lastProperty()->slotSpan();
707 : }
708 :
709 : /* Whether a slot is at a fixed offset from this object. */
710 4548 : bool isFixedSlot(size_t slot) {
711 4548 : return slot < numFixedSlots();
712 : }
713 :
714 : /* Index into the dynamic slots array to use for a dynamic slot. */
715 3142 : size_t dynamicSlotIndex(size_t slot) {
716 3142 : MOZ_ASSERT(slot >= numFixedSlots());
717 3142 : return slot - numFixedSlots();
718 : }
719 :
720 : /*
721 : * Grow or shrink slots immediately before changing the slot span.
722 : * The number of allocated slots is not stored explicitly, and changes to
723 : * the slots must track changes in the slot span.
724 : */
725 : bool growSlots(JSContext* cx, uint32_t oldCount, uint32_t newCount);
726 : void shrinkSlots(JSContext* cx, uint32_t oldCount, uint32_t newCount);
727 :
728 : /*
729 : * This method is static because it's called from JIT code. On OOM, returns
730 : * false without leaving a pending exception on the context.
731 : */
732 : static bool growSlotsDontReportOOM(JSContext* cx, NativeObject* obj, uint32_t newCount);
733 :
734 : /*
735 : * Like growSlotsDontReportOOM but for dense elements. This will return
736 : * false if we failed to allocate a dense element for some reason (OOM, too
737 : * many dense elements, non-writable array length, etc).
738 : */
739 : static bool addDenseElementDontReportOOM(JSContext* cx, NativeObject* obj);
740 :
741 27276 : bool hasDynamicSlots() const { return !!slots_; }
742 :
743 : /* Compute dynamicSlotsCount() for this object. */
744 : MOZ_ALWAYS_INLINE uint32_t numDynamicSlots() const;
745 :
746 1287 : bool empty() const {
747 1287 : return lastProperty()->isEmptyShape();
748 : }
749 :
750 : Shape* lookup(JSContext* cx, jsid id);
751 14885 : Shape* lookup(JSContext* cx, PropertyName* name) {
752 14885 : return lookup(cx, NameToId(name));
753 : }
754 :
755 602 : bool contains(JSContext* cx, jsid id) {
756 602 : return lookup(cx, id) != nullptr;
757 : }
758 : bool contains(JSContext* cx, PropertyName* name) {
759 : return lookup(cx, name) != nullptr;
760 : }
761 10261 : bool contains(JSContext* cx, Shape* shape) {
762 10261 : return lookup(cx, shape->propid()) == shape;
763 : }
764 :
765 0 : bool containsShapeOrElement(JSContext* cx, jsid id) {
766 0 : if (JSID_IS_INT(id) && containsDenseElement(JSID_TO_INT(id)))
767 0 : return true;
768 0 : return contains(cx, id);
769 : }
770 :
771 : /* Contextless; can be called from other pure code. */
772 : Shape* lookupPure(jsid id);
773 41838 : Shape* lookupPure(PropertyName* name) {
774 41838 : return lookupPure(NameToId(name));
775 : }
776 :
777 117704 : bool containsPure(jsid id) {
778 117704 : return lookupPure(id) != nullptr;
779 : }
780 251 : bool containsPure(PropertyName* name) {
781 251 : return containsPure(NameToId(name));
782 : }
783 44 : bool containsPure(Shape* shape) {
784 44 : return lookupPure(shape->propid()) == shape;
785 : }
786 :
787 : /*
788 : * Allocate and free an object slot.
789 : *
790 : * FIXME: bug 593129 -- slot allocation should be done by object methods
791 : * after calling object-parameter-free shape methods, avoiding coupling
792 : * logic across the object vs. shape module wall.
793 : */
794 : static bool allocDictionarySlot(JSContext* cx, HandleNativeObject obj, uint32_t* slotp);
795 : void freeSlot(JSContext* cx, uint32_t slot);
796 :
797 : private:
798 : static MOZ_ALWAYS_INLINE Shape* getChildProperty(JSContext* cx, HandleNativeObject obj,
799 : HandleShape parent,
800 : MutableHandle<StackShape> child);
801 :
802 : public:
803 : /* Add a property whose id is not yet in this scope. */
804 : static MOZ_ALWAYS_INLINE Shape* addProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
805 : JSGetterOp getter, JSSetterOp setter,
806 : uint32_t slot, unsigned attrs, unsigned flags,
807 : bool allowDictionary = true);
808 :
809 : /* Add a data property whose id is not yet in this scope. */
810 : static Shape* addDataProperty(JSContext* cx, HandleNativeObject obj,
811 : jsid id_, uint32_t slot, unsigned attrs);
812 : static Shape* addDataProperty(JSContext* cx, HandleNativeObject obj,
813 : HandlePropertyName name, uint32_t slot, unsigned attrs);
814 :
815 : /* Add or overwrite a property for id in this scope. */
816 : static Shape*
817 : putProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
818 : JSGetterOp getter, JSSetterOp setter,
819 : uint32_t slot, unsigned attrs,
820 : unsigned flags);
821 : static inline Shape*
822 : putProperty(JSContext* cx, HandleObject obj, PropertyName* name,
823 : JSGetterOp getter, JSSetterOp setter,
824 : uint32_t slot, unsigned attrs,
825 : unsigned flags);
826 :
827 : /* Change the given property into a sibling with the same id in this scope. */
828 : static Shape*
829 : changeProperty(JSContext* cx, HandleNativeObject obj, HandleShape shape,
830 : unsigned attrs, JSGetterOp getter, JSSetterOp setter);
831 :
832 : /* Remove the property named by id from this object. */
833 : static bool removeProperty(JSContext* cx, HandleNativeObject obj, jsid id);
834 :
835 : /* Clear the scope, making it empty. */
836 : static void clear(JSContext* cx, HandleNativeObject obj);
837 :
838 : protected:
839 : /*
840 : * Internal helper that adds a shape not yet mapped by this object.
841 : *
842 : * Notes:
843 : * 1. getter and setter must be normalized based on flags (see jsscope.cpp).
844 : * 2. Checks for non-extensibility must be done by callers.
845 : */
846 : static Shape*
847 : addPropertyInternal(JSContext* cx, HandleNativeObject obj, HandleId id,
848 : JSGetterOp getter, JSSetterOp setter, uint32_t slot, unsigned attrs,
849 : unsigned flags, ShapeTable::Entry* entry, bool allowDictionary,
850 : const AutoKeepShapeTables& keep);
851 :
852 : static MOZ_MUST_USE bool fillInAfterSwap(JSContext* cx, HandleNativeObject obj,
853 : const Vector<Value>& values, void* priv);
854 :
855 : public:
856 : // Return true if this object has been converted from shared-immutable
857 : // prototype-rooted shape storage to dictionary-shapes in a doubly-linked
858 : // list.
859 3190663 : bool inDictionaryMode() const {
860 3190663 : return lastProperty()->inDictionary();
861 : }
862 :
863 1347809 : const Value& getSlot(uint32_t slot) const {
864 1347809 : MOZ_ASSERT(slotInRange(slot));
865 1347761 : uint32_t fixed = numFixedSlots();
866 1347866 : if (slot < fixed)
867 870524 : return fixedSlots()[slot];
868 477342 : return slots_[slot - fixed];
869 : }
870 :
871 0 : const HeapSlot* getSlotAddressUnchecked(uint32_t slot) const {
872 0 : uint32_t fixed = numFixedSlots();
873 0 : if (slot < fixed)
874 0 : return fixedSlots() + slot;
875 0 : return slots_ + (slot - fixed);
876 : }
877 :
878 1049559 : HeapSlot* getSlotAddressUnchecked(uint32_t slot) {
879 1049559 : uint32_t fixed = numFixedSlots();
880 1049600 : if (slot < fixed)
881 437585 : return fixedSlots() + slot;
882 612015 : return slots_ + (slot - fixed);
883 : }
884 :
885 387473 : HeapSlot* getSlotAddress(uint32_t slot) {
886 : /*
887 : * This can be used to get the address of the end of the slots for the
888 : * object, which may be necessary when fetching zero-length arrays of
889 : * slots (e.g. for callObjVarArray).
890 : */
891 387473 : MOZ_ASSERT(slotInRange(slot, SENTINEL_ALLOWED));
892 387475 : return getSlotAddressUnchecked(slot);
893 : }
894 :
895 0 : const HeapSlot* getSlotAddress(uint32_t slot) const {
896 : /*
897 : * This can be used to get the address of the end of the slots for the
898 : * object, which may be necessary when fetching zero-length arrays of
899 : * slots (e.g. for callObjVarArray).
900 : */
901 0 : MOZ_ASSERT(slotInRange(slot, SENTINEL_ALLOWED));
902 0 : return getSlotAddressUnchecked(slot);
903 : }
904 :
905 387475 : MOZ_ALWAYS_INLINE HeapSlot& getSlotRef(uint32_t slot) {
906 387475 : MOZ_ASSERT(slotInRange(slot));
907 387472 : return *getSlotAddress(slot);
908 : }
909 :
910 0 : MOZ_ALWAYS_INLINE const HeapSlot& getSlotRef(uint32_t slot) const {
911 0 : MOZ_ASSERT(slotInRange(slot));
912 0 : return *getSlotAddress(slot);
913 : }
914 :
915 : // Check requirements on values stored to this object.
916 261312 : MOZ_ALWAYS_INLINE void checkStoredValue(const Value& v) {
917 261312 : MOZ_ASSERT(IsObjectValueInCompartment(v, compartment()));
918 261313 : MOZ_ASSERT(AtomIsMarked(zoneFromAnyThread(), v));
919 261313 : }
920 :
921 184374 : MOZ_ALWAYS_INLINE void setSlot(uint32_t slot, const Value& value) {
922 184374 : MOZ_ASSERT(slotInRange(slot));
923 184374 : checkStoredValue(value);
924 184374 : getSlotRef(slot).set(this, HeapSlot::Slot, slot, value);
925 184373 : }
926 :
927 41844 : MOZ_ALWAYS_INLINE void initSlot(uint32_t slot, const Value& value) {
928 41844 : MOZ_ASSERT(getSlot(slot).isUndefined());
929 41844 : MOZ_ASSERT(slotInRange(slot));
930 41844 : checkStoredValue(value);
931 41844 : initSlotUnchecked(slot, value);
932 41844 : }
933 :
934 167154 : MOZ_ALWAYS_INLINE void initSlotUnchecked(uint32_t slot, const Value& value) {
935 167154 : getSlotAddressUnchecked(slot)->init(this, HeapSlot::Slot, slot, value);
936 167154 : }
937 :
938 : // MAX_FIXED_SLOTS is the biggest number of fixed slots our GC
939 : // size classes will give an object.
940 : static const uint32_t MAX_FIXED_SLOTS = shadow::Object::MAX_FIXED_SLOTS;
941 :
942 : protected:
943 : MOZ_ALWAYS_INLINE bool updateSlotsForSpan(JSContext* cx, size_t oldSpan, size_t newSpan);
944 :
945 : private:
946 2464 : void prepareElementRangeForOverwrite(size_t start, size_t end) {
947 2464 : MOZ_ASSERT(end <= getDenseInitializedLength());
948 2464 : MOZ_ASSERT(!denseElementsAreCopyOnWrite());
949 2474 : for (size_t i = start; i < end; i++)
950 10 : elements_[i].HeapSlot::~HeapSlot();
951 2464 : }
952 :
953 : /*
954 : * Trigger the write barrier on a range of slots that will no longer be
955 : * reachable.
956 : */
957 16 : void prepareSlotRangeForOverwrite(size_t start, size_t end) {
958 32 : for (size_t i = start; i < end; i++)
959 16 : getSlotAddressUnchecked(i)->HeapSlot::~HeapSlot();
960 16 : }
961 :
962 : inline void shiftDenseElementsUnchecked(uint32_t count);
963 :
964 : public:
965 : static bool rollbackProperties(JSContext* cx, HandleNativeObject obj,
966 : uint32_t slotSpan);
967 :
968 : MOZ_ALWAYS_INLINE void setSlotWithType(JSContext* cx, Shape* shape,
969 : const Value& value, bool overwriting = true);
970 :
971 509536 : MOZ_ALWAYS_INLINE const Value& getReservedSlot(uint32_t index) const {
972 509536 : MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
973 509536 : return getSlot(index);
974 : }
975 :
976 0 : MOZ_ALWAYS_INLINE const HeapSlot& getReservedSlotRef(uint32_t index) const {
977 0 : MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
978 0 : return getSlotRef(index);
979 : }
980 :
981 0 : MOZ_ALWAYS_INLINE HeapSlot& getReservedSlotRef(uint32_t index) {
982 0 : MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
983 0 : return getSlotRef(index);
984 : }
985 :
986 38852 : MOZ_ALWAYS_INLINE void initReservedSlot(uint32_t index, const Value& v) {
987 38852 : MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
988 38852 : initSlot(index, v);
989 38852 : }
990 :
991 20823 : MOZ_ALWAYS_INLINE void setReservedSlot(uint32_t index, const Value& v) {
992 20823 : MOZ_ASSERT(index < JSSLOT_FREE(getClass()));
993 20823 : setSlot(index, v);
994 20823 : }
995 :
996 : /* For slots which are known to always be fixed, due to the way they are allocated. */
997 :
998 0 : HeapSlot& getFixedSlotRef(uint32_t slot) {
999 0 : MOZ_ASSERT(slot < numFixedSlots());
1000 0 : return fixedSlots()[slot];
1001 : }
1002 :
1003 17761 : const Value& getFixedSlot(uint32_t slot) const {
1004 17761 : MOZ_ASSERT(slot < numFixedSlots());
1005 17761 : return fixedSlots()[slot];
1006 : }
1007 :
1008 10340 : void setFixedSlot(uint32_t slot, const Value& value) {
1009 10340 : MOZ_ASSERT(slot < numFixedSlots());
1010 10340 : checkStoredValue(value);
1011 10340 : fixedSlots()[slot].set(this, HeapSlot::Slot, slot, value);
1012 10340 : }
1013 :
1014 4657 : void initFixedSlot(uint32_t slot, const Value& value) {
1015 4657 : MOZ_ASSERT(slot < numFixedSlots());
1016 4657 : checkStoredValue(value);
1017 4657 : fixedSlots()[slot].init(this, HeapSlot::Slot, slot, value);
1018 4657 : }
1019 :
1020 : /*
1021 : * Get the number of dynamic slots to allocate to cover the properties in
1022 : * an object with the given number of fixed slots and slot span. The slot
1023 : * capacity is not stored explicitly, and the allocated size of the slot
1024 : * array is kept in sync with this count.
1025 : */
1026 : static MOZ_ALWAYS_INLINE uint32_t dynamicSlotsCount(uint32_t nfixed, uint32_t span,
1027 : const Class* clasp);
1028 : static MOZ_ALWAYS_INLINE uint32_t dynamicSlotsCount(Shape* shape);
1029 :
1030 : /* Elements accessors. */
1031 :
1032 : // The maximum size, in sizeof(Value), of the allocation used for an
1033 : // object's dense elements. (This includes space used to store an
1034 : // ObjectElements instance.)
1035 : // |MAX_DENSE_ELEMENTS_ALLOCATION * sizeof(JS::Value)| shouldn't overflow
1036 : // int32_t (see elementsSizeMustNotOverflow).
1037 : static const uint32_t MAX_DENSE_ELEMENTS_ALLOCATION = (1 << 28) - 1;
1038 :
1039 : // The maximum number of usable dense elements in an object.
1040 : static const uint32_t MAX_DENSE_ELEMENTS_COUNT =
1041 : MAX_DENSE_ELEMENTS_ALLOCATION - ObjectElements::VALUES_PER_HEADER;
1042 :
1043 25 : static void elementsSizeMustNotOverflow() {
1044 : static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT <= INT32_MAX / sizeof(JS::Value),
1045 : "every caller of this method require that an element "
1046 : "count multiplied by sizeof(Value) can't overflow "
1047 : "uint32_t (and sometimes int32_t ,too)");
1048 25 : }
1049 :
1050 352841 : ObjectElements* getElementsHeader() const {
1051 352841 : return ObjectElements::fromElements(elements_);
1052 : }
1053 :
1054 : // Returns a pointer to the first element, including shifted elements.
1055 9442 : inline HeapSlot* unshiftedElements() const {
1056 9442 : return elements_ - getElementsHeader()->numShiftedElements();
1057 : }
1058 :
1059 : // Like getElementsHeader, but returns a pointer to the unshifted header.
1060 : // This is mainly useful for free()ing dynamic elements: the pointer
1061 : // returned here is the one we got from malloc.
1062 1742 : void* getUnshiftedElementsHeader() const {
1063 1742 : return ObjectElements::fromElements(unshiftedElements());
1064 : }
1065 :
1066 9645 : uint32_t unshiftedIndex(uint32_t index) const {
1067 9645 : return index + getElementsHeader()->numShiftedElements();
1068 : }
1069 :
1070 : /* Accessors for elements. */
1071 7350 : bool ensureElements(JSContext* cx, uint32_t capacity) {
1072 7350 : MOZ_ASSERT(!denseElementsAreCopyOnWrite());
1073 7350 : MOZ_ASSERT(!denseElementsAreFrozen());
1074 7350 : if (capacity > getDenseCapacity())
1075 514 : return growElements(cx, capacity);
1076 6836 : return true;
1077 : }
1078 :
1079 : // Try to shift |count| dense elements, see the "Shifted elements" comment.
1080 : inline bool tryShiftDenseElements(uint32_t count);
1081 :
1082 : // Try to make space for |count| dense elements at the start of the array.
1083 : bool tryUnshiftDenseElements(uint32_t count);
1084 :
1085 : // Move the elements header and all shifted elements to the start of the
1086 : // allocated elements space, so that numShiftedElements is 0 afterwards.
1087 : void moveShiftedElements();
1088 :
1089 : // If this object has many shifted elements call moveShiftedElements.
1090 : void maybeMoveShiftedElements();
1091 :
1092 : static bool goodElementsAllocationAmount(JSContext* cx, uint32_t reqAllocated,
1093 : uint32_t length, uint32_t* goodAmount);
1094 : bool growElements(JSContext* cx, uint32_t newcap);
1095 : void shrinkElements(JSContext* cx, uint32_t cap);
1096 : void setDynamicElements(ObjectElements* header) {
1097 : MOZ_ASSERT(!hasDynamicElements());
1098 : elements_ = header->elements();
1099 : MOZ_ASSERT(hasDynamicElements());
1100 : }
1101 :
1102 : static bool CopyElementsForWrite(JSContext* cx, NativeObject* obj);
1103 :
1104 11424 : bool maybeCopyElementsForWrite(JSContext* cx) {
1105 11424 : if (denseElementsAreCopyOnWrite())
1106 12 : return CopyElementsForWrite(cx, this);
1107 11412 : return true;
1108 : }
1109 :
1110 : private:
1111 : inline void ensureDenseInitializedLengthNoPackedCheck(JSContext* cx,
1112 : uint32_t index, uint32_t extra);
1113 :
1114 : // Run a post write barrier that encompasses multiple contiguous elements in a
1115 : // single step.
1116 : inline void elementsRangeWriteBarrierPost(uint32_t start, uint32_t count);
1117 :
1118 : // See the comment over setDenseElementUnchecked, this applies in the same way.
1119 2457 : void setDenseInitializedLengthUnchecked(uint32_t length) {
1120 2457 : MOZ_ASSERT(length <= getDenseCapacity());
1121 2457 : MOZ_ASSERT(!denseElementsAreCopyOnWrite());
1122 2457 : prepareElementRangeForOverwrite(length, getElementsHeader()->initializedLength);
1123 2457 : getElementsHeader()->initializedLength = length;
1124 2457 : }
1125 :
1126 : // Use this function with care. This is done to allow sparsifying frozen
1127 : // objects, but should only be called in a few places, and should be
1128 : // audited carefully!
1129 9253 : void setDenseElementUnchecked(uint32_t index, const Value& val) {
1130 9253 : MOZ_ASSERT(index < getDenseInitializedLength());
1131 9253 : MOZ_ASSERT(!denseElementsAreCopyOnWrite());
1132 9253 : checkStoredValue(val);
1133 9253 : elements_[index].set(this, HeapSlot::Element, unshiftedIndex(index), val);
1134 9253 : }
1135 :
1136 : public:
1137 2457 : void setDenseInitializedLength(uint32_t length) {
1138 2457 : MOZ_ASSERT(!denseElementsAreFrozen());
1139 2457 : setDenseInitializedLengthUnchecked(length);
1140 2457 : }
1141 :
1142 : inline void ensureDenseInitializedLength(JSContext* cx,
1143 : uint32_t index, uint32_t extra);
1144 :
1145 9253 : void setDenseElement(uint32_t index, const Value& val) {
1146 9253 : MOZ_ASSERT(!denseElementsAreFrozen());
1147 9253 : setDenseElementUnchecked(index, val);
1148 9253 : }
1149 :
1150 171 : void initDenseElement(uint32_t index, const Value& val) {
1151 171 : MOZ_ASSERT(index < getDenseInitializedLength());
1152 171 : MOZ_ASSERT(!denseElementsAreCopyOnWrite());
1153 171 : MOZ_ASSERT(!denseElementsAreFrozen());
1154 171 : checkStoredValue(val);
1155 171 : elements_[index].init(this, HeapSlot::Element, unshiftedIndex(index), val);
1156 171 : }
1157 :
1158 9006 : void setDenseElementMaybeConvertDouble(uint32_t index, const Value& val) {
1159 9006 : if (val.isInt32() && shouldConvertDoubleElements())
1160 0 : setDenseElement(index, DoubleValue(val.toInt32()));
1161 : else
1162 9006 : setDenseElement(index, val);
1163 9006 : }
1164 :
1165 : inline void setDenseElementWithType(JSContext* cx, uint32_t index,
1166 : const Value& val);
1167 : inline void initDenseElementWithType(JSContext* cx, uint32_t index,
1168 : const Value& val);
1169 : inline void setDenseElementHole(JSContext* cx, uint32_t index);
1170 : static inline void removeDenseElementForSparseIndex(JSContext* cx,
1171 : HandleNativeObject obj, uint32_t index);
1172 :
1173 : inline Value getDenseOrTypedArrayElement(uint32_t idx);
1174 :
1175 : inline void copyDenseElements(uint32_t dstStart, const Value* src, uint32_t count);
1176 : inline void initDenseElements(uint32_t dstStart, const Value* src, uint32_t count);
1177 : inline void moveDenseElements(uint32_t dstStart, uint32_t srcStart, uint32_t count);
1178 : inline void moveDenseElementsNoPreBarrier(uint32_t dstStart, uint32_t srcStart, uint32_t count);
1179 :
1180 3734 : bool shouldConvertDoubleElements() {
1181 3734 : return getElementsHeader()->shouldConvertDoubleElements();
1182 : }
1183 :
1184 : inline void setShouldConvertDoubleElements();
1185 : inline void clearShouldConvertDoubleElements();
1186 :
1187 50748 : bool denseElementsAreCopyOnWrite() {
1188 50748 : return getElementsHeader()->isCopyOnWrite();
1189 : }
1190 :
1191 35452 : bool denseElementsAreFrozen() {
1192 35452 : return getElementsHeader()->isFrozen();
1193 : }
1194 :
1195 : /* Packed information for this object's elements. */
1196 : inline bool writeToIndexWouldMarkNotPacked(uint32_t index);
1197 : inline void markDenseElementsNotPacked(JSContext* cx);
1198 :
1199 : // Ensures that the object can hold at least index + extra elements. This
1200 : // returns DenseElement_Success on success, DenseElement_Failed on failure
1201 : // to grow the array, or DenseElement_Incomplete when the object is too
1202 : // sparse to grow (this includes the case of index + extra overflow). In
1203 : // the last two cases the object is kept intact.
1204 : inline DenseElementResult ensureDenseElements(JSContext* cx,
1205 : uint32_t index, uint32_t extra);
1206 :
1207 : inline DenseElementResult extendDenseElements(JSContext* cx,
1208 : uint32_t requiredCapacity, uint32_t extra);
1209 :
1210 : /* Convert a single dense element to a sparse property. */
1211 : static bool sparsifyDenseElement(JSContext* cx,
1212 : HandleNativeObject obj, uint32_t index);
1213 :
1214 : /* Convert all dense elements to sparse properties. */
1215 : static bool sparsifyDenseElements(JSContext* cx, HandleNativeObject obj);
1216 :
1217 : /* Small objects are dense, no matter what. */
1218 : static const uint32_t MIN_SPARSE_INDEX = 1000;
1219 :
1220 : /*
1221 : * Element storage for an object will be sparse if fewer than 1/8 indexes
1222 : * are filled in.
1223 : */
1224 : static const unsigned SPARSE_DENSITY_RATIO = 8;
1225 :
1226 : /*
1227 : * Check if after growing the object's elements will be too sparse.
1228 : * newElementsHint is an estimated number of elements to be added.
1229 : */
1230 : bool willBeSparseElements(uint32_t requiredCapacity, uint32_t newElementsHint);
1231 :
1232 : /*
1233 : * After adding a sparse index to obj, see if it should be converted to use
1234 : * dense elements.
1235 : */
1236 : static DenseElementResult maybeDensifySparseElements(JSContext* cx,
1237 : HandleNativeObject obj);
1238 :
1239 14890 : inline HeapSlot* fixedElements() const {
1240 : static_assert(2 * sizeof(Value) == sizeof(ObjectElements),
1241 : "when elements are stored inline, the first two "
1242 : "slots will hold the ObjectElements header");
1243 14890 : return &fixedSlots()[2];
1244 : }
1245 :
1246 : #ifdef DEBUG
1247 : bool canHaveNonEmptyElements();
1248 : #endif
1249 :
1250 7190 : void setFixedElements(uint32_t numShifted = 0) {
1251 7190 : MOZ_ASSERT(canHaveNonEmptyElements());
1252 7190 : elements_ = fixedElements() + numShifted;
1253 7190 : }
1254 :
1255 7597 : inline bool hasDynamicElements() const {
1256 : /*
1257 : * Note: for objects with zero fixed slots this could potentially give
1258 : * a spurious 'true' result, if the end of this object is exactly
1259 : * aligned with the end of its arena and dynamic slots are allocated
1260 : * immediately afterwards. Such cases cannot occur for dense arrays
1261 : * (which have at least two fixed slots) and can only result in a leak.
1262 : */
1263 7597 : return !hasEmptyElements() && !hasFixedElements();
1264 : }
1265 :
1266 7700 : inline bool hasFixedElements() const {
1267 7700 : return unshiftedElements() == fixedElements();
1268 : }
1269 :
1270 53207 : inline bool hasEmptyElements() const {
1271 53207 : return elements_ == emptyObjectElements || elements_ == emptyObjectElementsShared;
1272 : }
1273 :
1274 : /*
1275 : * Get a pointer to the unused data in the object's allocation immediately
1276 : * following this object, for use with objects which allocate a larger size
1277 : * class than they need and store non-elements data inline.
1278 : */
1279 : inline uint8_t* fixedData(size_t nslots) const;
1280 :
1281 : inline void privateWriteBarrierPre(void** oldval);
1282 :
1283 0 : void privateWriteBarrierPost(void** pprivate) {
1284 0 : gc::Cell** cellp = reinterpret_cast<gc::Cell**>(pprivate);
1285 0 : MOZ_ASSERT(cellp);
1286 0 : MOZ_ASSERT(*cellp);
1287 0 : gc::StoreBuffer* storeBuffer = (*cellp)->storeBuffer();
1288 0 : if (storeBuffer)
1289 0 : storeBuffer->putCell(cellp);
1290 0 : }
1291 :
1292 : /* Private data accessors. */
1293 :
1294 27297 : inline void*& privateRef(uint32_t nfixed) const { /* XXX should be private, not protected! */
1295 : /*
1296 : * The private pointer of an object can hold any word sized value.
1297 : * Private pointers are stored immediately after the last fixed slot of
1298 : * the object.
1299 : */
1300 27297 : MOZ_ASSERT(nfixed == numFixedSlots());
1301 27297 : MOZ_ASSERT(hasPrivate());
1302 27297 : HeapSlot* end = &fixedSlots()[nfixed];
1303 27297 : return *reinterpret_cast<void**>(end);
1304 : }
1305 :
1306 27439 : bool hasPrivate() const {
1307 27439 : return getClass()->hasPrivate();
1308 : }
1309 7171 : void* getPrivate() const {
1310 7171 : return privateRef(numFixedSlots());
1311 : }
1312 8390 : void setPrivate(void* data) {
1313 8390 : void** pprivate = &privateRef(numFixedSlots());
1314 8390 : privateWriteBarrierPre(pprivate);
1315 8390 : *pprivate = data;
1316 8390 : }
1317 :
1318 0 : void setPrivateGCThing(gc::Cell* cell) {
1319 0 : MOZ_ASSERT_IF(IsMarkedBlack(this),
1320 : !JS::GCThingIsMarkedGray(JS::GCCellPtr(cell, cell->getTraceKind())));
1321 0 : void** pprivate = &privateRef(numFixedSlots());
1322 0 : privateWriteBarrierPre(pprivate);
1323 0 : *pprivate = reinterpret_cast<void*>(cell);
1324 0 : privateWriteBarrierPost(pprivate);
1325 0 : }
1326 :
1327 0 : void setPrivateUnbarriered(void* data) {
1328 0 : void** pprivate = &privateRef(numFixedSlots());
1329 0 : *pprivate = data;
1330 0 : }
1331 909 : void initPrivate(void* data) {
1332 909 : privateRef(numFixedSlots()) = data;
1333 909 : }
1334 :
1335 : /* Access private data for an object with a known number of fixed slots. */
1336 264 : inline void* getPrivate(uint32_t nfixed) const {
1337 264 : return privateRef(nfixed);
1338 : }
1339 :
1340 : static inline NativeObject*
1341 : copy(JSContext* cx, gc::AllocKind kind, gc::InitialHeap heap,
1342 : HandleNativeObject templateObject);
1343 :
1344 : void updateShapeAfterMovingGC();
1345 : void sweepDictionaryListPointer();
1346 :
1347 : /* JIT Accessors */
1348 138 : static size_t offsetOfElements() { return offsetof(NativeObject, elements_); }
1349 4 : static size_t offsetOfFixedElements() {
1350 4 : return sizeof(NativeObject) + sizeof(ObjectElements);
1351 : }
1352 :
1353 2945 : static size_t getFixedSlotOffset(size_t slot) {
1354 2945 : return sizeof(NativeObject) + slot * sizeof(Value);
1355 : }
1356 12 : static size_t getPrivateDataOffset(size_t nfixed) { return getFixedSlotOffset(nfixed); }
1357 184 : static size_t offsetOfSlots() { return offsetof(NativeObject, slots_); }
1358 : };
1359 :
1360 : // Object class for plain native objects created using '{}' object literals,
1361 : // 'new Object()', 'Object.create', etc.
1362 : class PlainObject : public NativeObject
1363 : {
1364 : public:
1365 : static const js::Class class_;
1366 : };
1367 :
1368 : inline void
1369 8390 : NativeObject::privateWriteBarrierPre(void** oldval)
1370 : {
1371 8390 : JS::shadow::Zone* shadowZone = this->shadowZoneFromAnyThread();
1372 8390 : if (shadowZone->needsIncrementalBarrier() && *oldval && getClass()->hasTrace())
1373 0 : getClass()->doTrace(shadowZone->barrierTracer(), this);
1374 8390 : }
1375 :
1376 :
1377 : /*** Standard internal methods *******************************************************************/
1378 :
1379 : /*
1380 : * These functions should follow the algorithms in ES6 draft rev 29 section 9.1
1381 : * ("Ordinary Object Internal Methods"). It's an ongoing project.
1382 : *
1383 : * Many native objects are not "ordinary" in ES6, so these functions also have
1384 : * to serve some of the special needs of Functions (9.2, 9.3, 9.4.1), Arrays
1385 : * (9.4.2), Strings (9.4.3), and so on.
1386 : */
1387 :
1388 : extern bool
1389 : NativeDefineProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
1390 : Handle<JS::PropertyDescriptor> desc,
1391 : ObjectOpResult& result);
1392 :
1393 : extern bool
1394 : NativeDefineProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value,
1395 : JSGetterOp getter, JSSetterOp setter, unsigned attrs,
1396 : ObjectOpResult& result);
1397 :
1398 : extern bool
1399 : NativeDefineProperty(JSContext* cx, HandleNativeObject obj, PropertyName* name,
1400 : HandleValue value, GetterOp getter, SetterOp setter,
1401 : unsigned attrs, ObjectOpResult& result);
1402 :
1403 : extern bool
1404 : NativeDefineElement(JSContext* cx, HandleNativeObject obj, uint32_t index, HandleValue value,
1405 : JSGetterOp getter, JSSetterOp setter, unsigned attrs,
1406 : ObjectOpResult& result);
1407 :
1408 : /* If the result out-param is omitted, throw on failure. */
1409 : extern bool
1410 : NativeDefineProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value,
1411 : JSGetterOp getter, JSSetterOp setter, unsigned attrs);
1412 :
1413 : extern bool
1414 : NativeDefineProperty(JSContext* cx, HandleNativeObject obj, PropertyName* name,
1415 : HandleValue value, JSGetterOp getter, JSSetterOp setter,
1416 : unsigned attrs);
1417 :
1418 : extern bool
1419 : NativeHasProperty(JSContext* cx, HandleNativeObject obj, HandleId id, bool* foundp);
1420 :
1421 : extern bool
1422 : NativeGetOwnPropertyDescriptor(JSContext* cx, HandleNativeObject obj, HandleId id,
1423 : MutableHandle<JS::PropertyDescriptor> desc);
1424 :
1425 : extern bool
1426 : NativeGetProperty(JSContext* cx, HandleNativeObject obj, HandleValue receiver, HandleId id,
1427 : MutableHandleValue vp);
1428 :
1429 : extern bool
1430 : NativeGetPropertyNoGC(JSContext* cx, NativeObject* obj, const Value& receiver, jsid id, Value* vp);
1431 :
1432 : inline bool
1433 141 : NativeGetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, MutableHandleValue vp)
1434 : {
1435 282 : RootedValue receiver(cx, ObjectValue(*obj));
1436 282 : return NativeGetProperty(cx, obj, receiver, id, vp);
1437 : }
1438 :
1439 : bool
1440 : SetPropertyByDefining(JSContext* cx, HandleId id, HandleValue v, HandleValue receiver,
1441 : ObjectOpResult& result);
1442 :
1443 : bool
1444 : SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
1445 : HandleValue receiver, ObjectOpResult& result);
1446 :
1447 : /*
1448 : * Indicates whether an assignment operation is qualified (`x.y = 0`) or
1449 : * unqualified (`y = 0`). In strict mode, the latter is an error if no such
1450 : * variable already exists.
1451 : *
1452 : * Used as an argument to NativeSetProperty.
1453 : */
1454 : enum QualifiedBool {
1455 : Unqualified = 0,
1456 : Qualified = 1
1457 : };
1458 :
1459 : extern bool
1460 : NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v,
1461 : HandleValue receiver, QualifiedBool qualified, ObjectOpResult& result);
1462 :
1463 : extern bool
1464 : NativeSetElement(JSContext* cx, HandleNativeObject obj, uint32_t index, HandleValue v,
1465 : HandleValue receiver, ObjectOpResult& result);
1466 :
1467 : extern bool
1468 : NativeDeleteProperty(JSContext* cx, HandleNativeObject obj, HandleId id, ObjectOpResult& result);
1469 :
1470 :
1471 : /*** SpiderMonkey nonstandard internal methods ***************************************************/
1472 :
1473 : template <AllowGC allowGC>
1474 : extern bool
1475 : NativeLookupOwnProperty(JSContext* cx,
1476 : typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
1477 : typename MaybeRooted<jsid, allowGC>::HandleType id,
1478 : typename MaybeRooted<PropertyResult, allowGC>::MutableHandleType propp);
1479 :
1480 : /*
1481 : * Get a property from `receiver`, after having already done a lookup and found
1482 : * the property on a native object `obj`.
1483 : *
1484 : * `shape` must not be null and must not be an implicit dense property. It must
1485 : * be present in obj's shape chain.
1486 : */
1487 : extern bool
1488 : NativeGetExistingProperty(JSContext* cx, HandleObject receiver, HandleNativeObject obj,
1489 : HandleShape shape, MutableHandleValue vp);
1490 :
1491 : /* * */
1492 :
1493 : extern bool
1494 : GetNameBoundInEnvironment(JSContext* cx, HandleObject env, HandleId id, MutableHandleValue vp);
1495 :
1496 : } /* namespace js */
1497 :
1498 : template <>
1499 : inline bool
1500 687798 : JSObject::is<js::NativeObject>() const { return isNative(); }
1501 :
1502 : namespace js {
1503 :
1504 : // Alternate to JSObject::as<NativeObject>() that tolerates null pointers.
1505 : inline NativeObject*
1506 4738 : MaybeNativeObject(JSObject* obj)
1507 : {
1508 4738 : return obj ? &obj->as<NativeObject>() : nullptr;
1509 : }
1510 :
1511 : // Defined in NativeObject-inl.h.
1512 : bool IsPackedArray(JSObject* obj);
1513 :
1514 : extern void
1515 : AddPropertyTypesAfterProtoChange(JSContext* cx, NativeObject* obj, ObjectGroup* oldGroup);
1516 :
1517 : } // namespace js
1518 :
1519 :
1520 : /*** Inline functions declared in jsobj.h that use the native declarations above *****************/
1521 :
1522 : inline bool
1523 20665 : js::HasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
1524 : {
1525 20665 : if (HasPropertyOp op = obj->getOpsHasProperty())
1526 8023 : return op(cx, obj, id, foundp);
1527 12642 : return NativeHasProperty(cx, obj.as<NativeObject>(), id, foundp);
1528 : }
1529 :
1530 : inline bool
1531 68183 : js::GetProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
1532 : MutableHandleValue vp)
1533 : {
1534 68183 : if (GetPropertyOp op = obj->getOpsGetProperty())
1535 12194 : return op(cx, obj, receiver, id, vp);
1536 55989 : return NativeGetProperty(cx, obj.as<NativeObject>(), receiver, id, vp);
1537 : }
1538 :
1539 : inline bool
1540 3075 : js::GetPropertyNoGC(JSContext* cx, JSObject* obj, const Value& receiver, jsid id, Value* vp)
1541 : {
1542 3075 : if (obj->getOpsGetProperty())
1543 153 : return false;
1544 2922 : return NativeGetPropertyNoGC(cx, &obj->as<NativeObject>(), receiver, id, vp);
1545 : }
1546 :
1547 : inline bool
1548 9301 : js::SetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
1549 : HandleValue receiver, ObjectOpResult& result)
1550 : {
1551 9301 : if (obj->getOpsSetProperty())
1552 386 : return JSObject::nonNativeSetProperty(cx, obj, id, v, receiver, result);
1553 8915 : return NativeSetProperty(cx, obj.as<NativeObject>(), id, v, receiver, Qualified, result);
1554 : }
1555 :
1556 : inline bool
1557 0 : js::SetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue v,
1558 : HandleValue receiver, ObjectOpResult& result)
1559 : {
1560 0 : if (obj->getOpsSetProperty())
1561 0 : return JSObject::nonNativeSetElement(cx, obj, index, v, receiver, result);
1562 0 : return NativeSetElement(cx, obj.as<NativeObject>(), index, v, receiver, result);
1563 : }
1564 :
1565 : #endif /* vm_NativeObject_h */
|