1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef vm_NativeObject_inl_h
8 #define vm_NativeObject_inl_h
10 #include "vm/NativeObject.h"
12 #include "mozilla/Maybe.h"
14 #include <type_traits>
16 #include "gc/GCEnum.h"
17 #include "gc/GCProbes.h"
18 #include "gc/MaybeRooted.h"
19 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
20 #include "vm/Compartment.h"
21 #include "vm/Iteration.h"
22 #include "vm/PlainObject.h"
23 #include "vm/PropertyResult.h"
24 #include "vm/StringType.h"
25 #include "vm/TypedArrayObject.h"
27 #include "gc/Marking-inl.h"
28 #include "gc/ObjectKind-inl.h"
29 #include "vm/Compartment-inl.h"
30 #include "vm/JSContext-inl.h"
31 #include "vm/JSObject-inl.h"
32 #include "vm/Realm-inl.h"
33 #include "vm/Shape-inl.h"
35 #ifdef ENABLE_RECORD_TUPLE
36 // Defined in vm/RecordTupleShared.{h,cpp}. We cannot include that file
37 // because it causes circular dependencies.
38 extern bool js::IsExtendedPrimitive(const JSObject
& obj
);
43 constexpr ObjectSlots::ObjectSlots(uint32_t capacity
,
44 uint32_t dictionarySlotSpan
,
45 uint64_t maybeUniqueId
)
46 : capacity_(capacity
),
47 dictionarySlotSpan_(dictionarySlotSpan
),
48 maybeUniqueId_(maybeUniqueId
) {
49 MOZ_ASSERT(this->capacity() == capacity
);
50 MOZ_ASSERT(this->dictionarySlotSpan() == dictionarySlotSpan
);
53 inline uint32_t NativeObject::numFixedSlotsMaybeForwarded() const {
54 return gc::MaybeForwarded(JSObject::shape())->asNative().numFixedSlots();
57 inline uint8_t* NativeObject::fixedData(size_t nslots
) const {
58 MOZ_ASSERT(ClassCanHaveFixedData(gc::MaybeForwardedObjectClass(this)));
59 MOZ_ASSERT(nslots
== numFixedSlotsMaybeForwarded());
60 return reinterpret_cast<uint8_t*>(&fixedSlots()[nslots
]);
63 inline void NativeObject::initDenseElementHole(uint32_t index
) {
64 markDenseElementsNotPacked();
65 initDenseElementUnchecked(index
, MagicValue(JS_ELEMENTS_HOLE
));
68 inline void NativeObject::setDenseElementHole(uint32_t index
) {
69 markDenseElementsNotPacked();
70 setDenseElementUnchecked(index
, MagicValue(JS_ELEMENTS_HOLE
));
73 inline void NativeObject::removeDenseElementForSparseIndex(uint32_t index
) {
74 MOZ_ASSERT(containsPure(PropertyKey::Int(index
)));
75 if (containsDenseElement(index
)) {
76 setDenseElementHole(index
);
80 inline void NativeObject::markDenseElementsNotPacked() {
81 MOZ_ASSERT(is
<NativeObject
>());
82 getElementsHeader()->markNonPacked();
85 inline void NativeObject::elementsRangePostWriteBarrier(uint32_t start
,
90 for (size_t i
= 0; i
< count
; i
++) {
91 const Value
& v
= elements_
[start
+ i
];
93 if (gc::StoreBuffer
* sb
= v
.toGCThing()->storeBuffer()) {
94 sb
->putSlot(this, HeapSlot::Element
, unshiftedIndex(start
+ i
),
102 inline void NativeObject::copyDenseElements(uint32_t dstStart
, const Value
* src
,
104 MOZ_ASSERT(dstStart
+ count
<= getDenseCapacity());
105 MOZ_ASSERT(isExtensible());
106 MOZ_ASSERT_IF(count
> 0, src
!= nullptr);
108 for (uint32_t i
= 0; i
< count
; ++i
) {
109 checkStoredValue(src
[i
]);
115 if (zone()->needsIncrementalBarrier()) {
116 uint32_t numShifted
= getElementsHeader()->numShiftedElements();
117 for (uint32_t i
= 0; i
< count
; ++i
) {
118 elements_
[dstStart
+ i
].set(this, HeapSlot::Element
,
119 dstStart
+ i
+ numShifted
, src
[i
]);
122 memcpy(reinterpret_cast<Value
*>(&elements_
[dstStart
]), src
,
123 count
* sizeof(Value
));
124 elementsRangePostWriteBarrier(dstStart
, count
);
128 inline void NativeObject::initDenseElements(NativeObject
* src
,
129 uint32_t srcStart
, uint32_t count
) {
130 MOZ_ASSERT(src
->getDenseInitializedLength() >= srcStart
+ count
);
132 const Value
* vp
= src
->getDenseElements() + srcStart
;
134 if (!src
->denseElementsArePacked()) {
135 // Mark non-packed if we're copying holes or if there are too many elements
136 // to check this efficiently.
137 static constexpr uint32_t MaxCountForPackedCheck
= 30;
138 if (count
> MaxCountForPackedCheck
) {
139 markDenseElementsNotPacked();
141 for (uint32_t i
= 0; i
< count
; i
++) {
142 if (vp
[i
].isMagic(JS_ELEMENTS_HOLE
)) {
143 markDenseElementsNotPacked();
150 initDenseElements(vp
, count
);
153 inline void NativeObject::initDenseElements(const Value
* src
, uint32_t count
) {
154 MOZ_ASSERT(getDenseInitializedLength() == 0);
155 MOZ_ASSERT(count
<= getDenseCapacity());
157 MOZ_ASSERT(isExtensible());
159 setDenseInitializedLength(count
);
162 for (uint32_t i
= 0; i
< count
; ++i
) {
163 checkStoredValue(src
[i
]);
167 memcpy(reinterpret_cast<Value
*>(elements_
), src
, count
* sizeof(Value
));
168 elementsRangePostWriteBarrier(0, count
);
171 inline void NativeObject::initDenseElements(JSLinearString
** src
,
173 MOZ_ASSERT(getDenseInitializedLength() == 0);
174 MOZ_ASSERT(count
<= getDenseCapacity());
176 MOZ_ASSERT(isExtensible());
178 setDenseInitializedLength(count
);
179 Value
* elementsBase
= reinterpret_cast<Value
*>(elements_
);
180 for (size_t i
= 0; i
< count
; i
++) {
181 elementsBase
[i
].setString(src
[i
]);
184 elementsRangePostWriteBarrier(0, count
);
187 inline void NativeObject::initDenseElementRange(uint32_t destStart
,
190 MOZ_ASSERT(count
<= src
->getDenseInitializedLength());
192 // The initialized length must already be set to the correct value.
193 MOZ_ASSERT(destStart
+ count
== getDenseInitializedLength());
195 if (!src
->denseElementsArePacked()) {
196 markDenseElementsNotPacked();
199 const Value
* vp
= src
->getDenseElements();
201 for (uint32_t i
= 0; i
< count
; ++i
) {
202 checkStoredValue(vp
[i
]);
205 memcpy(reinterpret_cast<Value
*>(elements_
) + destStart
, vp
,
206 count
* sizeof(Value
));
207 elementsRangePostWriteBarrier(destStart
, count
);
210 template <typename Iter
>
211 inline bool NativeObject::initDenseElementsFromRange(JSContext
* cx
, Iter begin
,
213 // This method populates the elements of a particular Array that's an
214 // internal implementation detail of GeneratorObject. Failing any of the
215 // following means the Array has escaped and/or been mistreated.
216 MOZ_ASSERT(isExtensible());
217 MOZ_ASSERT(!isIndexed());
218 MOZ_ASSERT(is
<ArrayObject
>());
219 MOZ_ASSERT(as
<ArrayObject
>().lengthIsWritable());
220 MOZ_ASSERT(!denseElementsAreFrozen());
221 MOZ_ASSERT(getElementsHeader()->numShiftedElements() == 0);
223 MOZ_ASSERT(getDenseInitializedLength() == 0);
225 auto size
= end
- begin
;
226 uint32_t count
= uint32_t(size
);
227 MOZ_ASSERT(count
<= uint32_t(INT32_MAX
));
228 if (count
> getDenseCapacity()) {
229 if (!growElements(cx
, count
)) {
234 HeapSlot
* sp
= elements_
;
236 for (; begin
!= end
; sp
++, begin
++) {
241 sp
->init(this, HeapSlot::Element
, slot
++, v
);
243 MOZ_ASSERT(slot
== count
);
245 getElementsHeader()->initializedLength
= count
;
246 as
<ArrayObject
>().setLength(count
);
250 inline bool NativeObject::tryShiftDenseElements(uint32_t count
) {
251 MOZ_ASSERT(isExtensible());
253 ObjectElements
* header
= getElementsHeader();
254 if (header
->initializedLength
== count
||
255 count
> ObjectElements::MaxShiftedElements
||
256 header
->hasNonwritableArrayLength()) {
260 shiftDenseElementsUnchecked(count
);
264 inline void NativeObject::shiftDenseElementsUnchecked(uint32_t count
) {
265 MOZ_ASSERT(isExtensible());
267 ObjectElements
* header
= getElementsHeader();
268 MOZ_ASSERT(count
> 0);
269 MOZ_ASSERT(count
< header
->initializedLength
);
271 if (MOZ_UNLIKELY(header
->numShiftedElements() + count
>
272 ObjectElements::MaxShiftedElements
)) {
273 moveShiftedElements();
274 header
= getElementsHeader();
277 prepareElementRangeForOverwrite(0, count
);
278 header
->addShiftedElements(count
);
281 ObjectElements
* newHeader
= getElementsHeader();
282 memmove(newHeader
, header
, sizeof(ObjectElements
));
285 inline void NativeObject::moveDenseElements(uint32_t dstStart
,
286 uint32_t srcStart
, uint32_t count
) {
287 MOZ_ASSERT(dstStart
+ count
<= getDenseCapacity());
288 MOZ_ASSERT(srcStart
+ count
<= getDenseInitializedLength());
289 MOZ_ASSERT(isExtensible());
292 * Using memmove here would skip write barriers. Also, we need to consider
293 * an array containing [A, B, C], in the following situation:
295 * 1. Incremental GC marks slot 0 of array (i.e., A), then returns to JS code.
296 * 2. JS code moves slots 1..2 into slots 0..1, so it contains [B, C, C].
297 * 3. Incremental GC finishes by marking slots 1 and 2 (i.e., C).
299 * Since normal marking never happens on B, it is very important that the
300 * write barrier is invoked here on B, despite the fact that it exists in
301 * the array before and after the move.
303 if (zone()->needsIncrementalBarrier()) {
304 uint32_t numShifted
= getElementsHeader()->numShiftedElements();
305 if (dstStart
< srcStart
) {
306 HeapSlot
* dst
= elements_
+ dstStart
;
307 HeapSlot
* src
= elements_
+ srcStart
;
308 for (uint32_t i
= 0; i
< count
; i
++, dst
++, src
++) {
309 dst
->set(this, HeapSlot::Element
, dst
- elements_
+ numShifted
, *src
);
312 HeapSlot
* dst
= elements_
+ dstStart
+ count
- 1;
313 HeapSlot
* src
= elements_
+ srcStart
+ count
- 1;
314 for (uint32_t i
= 0; i
< count
; i
++, dst
--, src
--) {
315 dst
->set(this, HeapSlot::Element
, dst
- elements_
+ numShifted
, *src
);
319 memmove(elements_
+ dstStart
, elements_
+ srcStart
,
320 count
* sizeof(HeapSlot
));
321 elementsRangePostWriteBarrier(dstStart
, count
);
325 inline void NativeObject::reverseDenseElementsNoPreBarrier(uint32_t length
) {
326 MOZ_ASSERT(!zone()->needsIncrementalBarrier());
328 MOZ_ASSERT(isExtensible());
330 MOZ_ASSERT(length
> 1);
331 MOZ_ASSERT(length
<= getDenseInitializedLength());
333 Value
* valLo
= reinterpret_cast<Value
*>(elements_
);
334 Value
* valHi
= valLo
+ (length
- 1);
335 MOZ_ASSERT(valLo
< valHi
);
338 Value origLo
= *valLo
;
343 } while (valLo
< valHi
);
345 elementsRangePostWriteBarrier(0, length
);
348 inline void NativeObject::ensureDenseInitializedLength(uint32_t index
,
350 // Ensure that the array's contents have been initialized up to index, and
351 // mark the elements through 'index + extra' as initialized in preparation
354 MOZ_ASSERT(!denseElementsAreFrozen());
355 MOZ_ASSERT(isExtensible() || (containsDenseElement(index
) && extra
== 1));
356 MOZ_ASSERT(index
+ extra
<= getDenseCapacity());
358 uint32_t initlen
= getDenseInitializedLength();
359 if (index
+ extra
<= initlen
) {
363 MOZ_ASSERT(isExtensible());
365 if (index
> initlen
) {
366 markDenseElementsNotPacked();
369 uint32_t numShifted
= getElementsHeader()->numShiftedElements();
370 size_t offset
= initlen
;
371 for (HeapSlot
* sp
= elements_
+ initlen
; sp
!= elements_
+ (index
+ extra
);
373 sp
->init(this, HeapSlot::Element
, offset
+ numShifted
,
374 MagicValue(JS_ELEMENTS_HOLE
));
377 getElementsHeader()->initializedLength
= index
+ extra
;
380 DenseElementResult
NativeObject::extendDenseElements(JSContext
* cx
,
381 uint32_t requiredCapacity
,
383 MOZ_ASSERT(isExtensible());
386 * Don't grow elements for objects which already have sparse indexes.
387 * This avoids needing to count non-hole elements in willBeSparseElements
388 * every time a new index is added.
391 return DenseElementResult::Incomplete
;
395 * We use the extra argument also as a hint about number of non-hole
396 * elements to be inserted.
398 if (requiredCapacity
> MIN_SPARSE_INDEX
&&
399 willBeSparseElements(requiredCapacity
, extra
)) {
400 return DenseElementResult::Incomplete
;
403 if (!growElements(cx
, requiredCapacity
)) {
404 return DenseElementResult::Failure
;
407 return DenseElementResult::Success
;
410 inline DenseElementResult
NativeObject::ensureDenseElements(JSContext
* cx
,
413 MOZ_ASSERT(is
<NativeObject
>());
414 MOZ_ASSERT(isExtensible() || (containsDenseElement(index
) && extra
== 1));
416 uint32_t requiredCapacity
;
418 /* Optimize for the common case. */
419 if (index
< getDenseCapacity()) {
420 ensureDenseInitializedLength(index
, 1);
421 return DenseElementResult::Success
;
423 requiredCapacity
= index
+ 1;
424 if (requiredCapacity
== 0) {
426 return DenseElementResult::Incomplete
;
429 requiredCapacity
= index
+ extra
;
430 if (requiredCapacity
< index
) {
432 return DenseElementResult::Incomplete
;
434 if (requiredCapacity
<= getDenseCapacity()) {
435 ensureDenseInitializedLength(index
, extra
);
436 return DenseElementResult::Success
;
440 DenseElementResult result
= extendDenseElements(cx
, requiredCapacity
, extra
);
441 if (result
!= DenseElementResult::Success
) {
445 ensureDenseInitializedLength(index
, extra
);
446 return DenseElementResult::Success
;
449 inline DenseElementResult
NativeObject::setOrExtendDenseElements(
450 JSContext
* cx
, uint32_t start
, const Value
* vp
, uint32_t count
) {
451 if (!isExtensible()) {
452 return DenseElementResult::Incomplete
;
455 if (is
<ArrayObject
>() && !as
<ArrayObject
>().lengthIsWritable() &&
456 start
+ count
>= as
<ArrayObject
>().length()) {
457 return DenseElementResult::Incomplete
;
460 DenseElementResult result
= ensureDenseElements(cx
, start
, count
);
461 if (result
!= DenseElementResult::Success
) {
465 if (is
<ArrayObject
>() && start
+ count
>= as
<ArrayObject
>().length()) {
466 as
<ArrayObject
>().setLength(start
+ count
);
469 copyDenseElements(start
, vp
, count
);
470 return DenseElementResult::Success
;
474 inline NativeObject
* NativeObject::create(
475 JSContext
* cx
, js::gc::AllocKind kind
, js::gc::Heap heap
,
476 js::Handle
<SharedShape
*> shape
, js::gc::AllocSite
* site
/* = nullptr */) {
477 debugCheckNewObject(shape
, kind
, heap
);
479 const JSClass
* clasp
= shape
->getObjectClass();
480 MOZ_ASSERT(clasp
->isNativeObject());
481 MOZ_ASSERT(!clasp
->isJSFunction(), "should use JSFunction::create");
482 MOZ_ASSERT(clasp
!= &ArrayObject::class_
, "should use ArrayObject::create");
484 const uint32_t nfixed
= shape
->numFixedSlots();
485 const uint32_t slotSpan
= shape
->slotSpan();
486 const size_t nDynamicSlots
= calculateDynamicSlots(nfixed
, slotSpan
, clasp
);
488 NativeObject
* nobj
= cx
->newCell
<NativeObject
>(kind
, heap
, clasp
, site
);
493 nobj
->initShape(shape
);
494 nobj
->setEmptyElements();
496 if (!nDynamicSlots
) {
497 nobj
->initEmptyDynamicSlots();
498 } else if (!nobj
->allocateInitialSlots(cx
, nDynamicSlots
)) {
503 nobj
->initSlots(nfixed
, slotSpan
);
506 if (MOZ_UNLIKELY(cx
->realm()->hasAllocationMetadataBuilder())) {
507 if (clasp
->shouldDelayMetadataBuilder()) {
508 cx
->realm()->setObjectPendingMetadata(nobj
);
510 nobj
= SetNewObjectMetadata(cx
, nobj
);
514 js::gc::gcprobes::CreateObject(nobj
);
519 MOZ_ALWAYS_INLINE
void NativeObject::initEmptyDynamicSlots() {
520 setEmptyDynamicSlots(0);
523 MOZ_ALWAYS_INLINE
void NativeObject::setDictionaryModeSlotSpan(uint32_t span
) {
524 MOZ_ASSERT(inDictionaryMode());
526 if (!hasDynamicSlots()) {
527 setEmptyDynamicSlots(span
);
531 getSlotsHeader()->setDictionarySlotSpan(span
);
534 MOZ_ALWAYS_INLINE
void NativeObject::setEmptyDynamicSlots(
535 uint32_t dictionarySlotSpan
) {
536 MOZ_ASSERT_IF(!inDictionaryMode(), dictionarySlotSpan
== 0);
537 MOZ_ASSERT(dictionarySlotSpan
<= MAX_FIXED_SLOTS
);
539 slots_
= emptyObjectSlotsForDictionaryObject
[dictionarySlotSpan
];
541 MOZ_ASSERT(getSlotsHeader()->capacity() == 0);
542 MOZ_ASSERT(getSlotsHeader()->dictionarySlotSpan() == dictionarySlotSpan
);
543 MOZ_ASSERT(!hasDynamicSlots());
544 MOZ_ASSERT(!hasUniqueId());
547 MOZ_ALWAYS_INLINE
bool NativeObject::setShapeAndAddNewSlots(
548 JSContext
* cx
, SharedShape
* newShape
, uint32_t oldSpan
, uint32_t newSpan
) {
549 MOZ_ASSERT(!inDictionaryMode());
550 MOZ_ASSERT(newShape
->isShared());
551 MOZ_ASSERT(newShape
->zone() == zone());
552 MOZ_ASSERT(newShape
->numFixedSlots() == numFixedSlots());
553 MOZ_ASSERT(newShape
->getObjectClass() == getClass());
555 MOZ_ASSERT(oldSpan
< newSpan
);
556 MOZ_ASSERT(sharedShape()->slotSpan() == oldSpan
);
557 MOZ_ASSERT(newShape
->slotSpan() == newSpan
);
559 uint32_t numFixed
= newShape
->numFixedSlots();
560 if (newSpan
> numFixed
) {
561 uint32_t oldCapacity
= numDynamicSlots();
562 uint32_t newCapacity
=
563 calculateDynamicSlots(numFixed
, newSpan
, newShape
->getObjectClass());
564 MOZ_ASSERT(oldCapacity
<= newCapacity
);
566 if (oldCapacity
< newCapacity
) {
567 if (MOZ_UNLIKELY(!growSlots(cx
, oldCapacity
, newCapacity
))) {
573 // Initialize slots [oldSpan, newSpan). Use the *Unchecked version because
574 // the shape's slot span does not reflect the allocated slots at this
576 auto initRange
= [](HeapSlot
* start
, HeapSlot
* end
) {
577 for (HeapSlot
* slot
= start
; slot
< end
; slot
++) {
578 slot
->initAsUndefined();
581 forEachSlotRangeUnchecked(oldSpan
, newSpan
, initRange
);
587 MOZ_ALWAYS_INLINE
bool NativeObject::setShapeAndAddNewSlot(
588 JSContext
* cx
, SharedShape
* newShape
, uint32_t slot
) {
589 MOZ_ASSERT(!inDictionaryMode());
590 MOZ_ASSERT(newShape
->isShared());
591 MOZ_ASSERT(newShape
->zone() == zone());
592 MOZ_ASSERT(newShape
->numFixedSlots() == numFixedSlots());
594 MOZ_ASSERT(newShape
->base() == shape()->base());
595 MOZ_ASSERT(newShape
->slotSpan() == sharedShape()->slotSpan() + 1);
596 MOZ_ASSERT(newShape
->slotSpan() == slot
+ 1);
598 uint32_t numFixed
= newShape
->numFixedSlots();
599 if (slot
< numFixed
) {
600 initFixedSlot(slot
, UndefinedValue());
602 uint32_t dynamicSlotIndex
= slot
- numFixed
;
603 if (dynamicSlotIndex
>= numDynamicSlots()) {
604 if (MOZ_UNLIKELY(!growSlotsForNewSlot(cx
, numFixed
, slot
))) {
608 initDynamicSlot(numFixed
, slot
, UndefinedValue());
615 inline js::gc::AllocKind
NativeObject::allocKindForTenure() const {
616 using namespace js::gc
;
617 AllocKind kind
= GetGCObjectFixedSlotsKind(numFixedSlots());
618 MOZ_ASSERT(!IsBackgroundFinalized(kind
));
619 if (!CanChangeToBackgroundAllocKind(kind
, getClass())) {
622 return ForegroundToBackgroundAllocKind(kind
);
625 inline js::GlobalObject
& NativeObject::global() const { return nonCCWGlobal(); }
627 inline bool NativeObject::denseElementsHaveMaybeInIterationFlag() {
628 if (!getElementsHeader()->maybeInIteration()) {
629 AssertDenseElementsNotIterated(this);
635 inline bool NativeObject::denseElementsMaybeInIteration() {
636 if (!denseElementsHaveMaybeInIterationFlag()) {
639 return compartment()->objectMaybeInIteration(this);
643 * Call obj's resolve hook.
645 * cx and id are the parameters initially passed to the ongoing lookup;
646 * propp and recursedp are its out parameters.
648 * There are four possible outcomes:
650 * - On failure, report an error or exception and return false.
652 * - If we are already resolving a property of obj, call setRecursiveResolve on
653 * propp and return true.
655 * - If the resolve hook finds or defines the sought property, set propp
656 * appropriately, and return true.
658 * - Otherwise no property was resolved. Set propp to NotFound and return true.
660 static MOZ_ALWAYS_INLINE
bool CallResolveOp(JSContext
* cx
,
661 Handle
<NativeObject
*> obj
,
663 PropertyResult
* propp
) {
664 // Avoid recursion on (obj, id) already being resolved on cx.
665 AutoResolving
resolving(cx
, obj
, id
);
666 if (resolving
.alreadyStarted()) {
667 // Already resolving id in obj, suppress recursion.
668 propp
->setRecursiveResolve();
672 bool resolved
= false;
673 AutoRealm
ar(cx
, obj
);
674 if (!obj
->getClass()->getResolve()(cx
, obj
, id
, &resolved
)) {
679 propp
->setNotFound();
683 // Assert the mayResolve hook, if there is one, returns true for this
685 MOZ_ASSERT_IF(obj
->getClass()->getMayResolve(),
686 obj
->getClass()->getMayResolve()(cx
->names(), id
, obj
));
689 uint32_t index
= id
.toInt();
690 if (obj
->containsDenseElement(index
)) {
691 propp
->setDenseElement(index
);
696 MOZ_ASSERT(!obj
->is
<TypedArrayObject
>());
698 mozilla::Maybe
<PropertyInfo
> prop
= obj
->lookup(cx
, id
);
700 propp
->setNativeProperty(*prop
);
702 propp
->setNotFound();
708 enum class LookupResolveMode
{
714 template <AllowGC allowGC
,
715 LookupResolveMode resolveMode
= LookupResolveMode::CheckResolve
>
716 static MOZ_ALWAYS_INLINE
bool NativeLookupOwnPropertyInline(
717 JSContext
* cx
, typename MaybeRooted
<NativeObject
*, allowGC
>::HandleType obj
,
718 typename MaybeRooted
<jsid
, allowGC
>::HandleType id
, PropertyResult
* propp
) {
719 // Native objects should should avoid `lookupProperty` hooks, and those that
720 // use them should avoid recursively triggering lookup, and those that still
721 // violate this guidance are the ModuleEnvironmentObject.
722 MOZ_ASSERT_IF(obj
->getOpsLookupProperty(),
723 obj
->template is
<ModuleEnvironmentObject
>());
724 #ifdef ENABLE_RECORD_TUPLE
725 MOZ_ASSERT(!js::IsExtendedPrimitive(*obj
));
728 // Check for a native dense element.
730 uint32_t index
= id
.toInt();
731 if (obj
->containsDenseElement(index
)) {
732 propp
->setDenseElement(index
);
737 // Check for a typed array element. Integer lookups always finish here
738 // so that integer properties on the prototype are ignored even for out
739 // of bounds accesses.
740 if (obj
->template is
<TypedArrayObject
>()) {
741 if (mozilla::Maybe
<uint64_t> index
= ToTypedArrayIndex(id
)) {
742 uint64_t idx
= index
.value();
743 if (idx
< obj
->template as
<TypedArrayObject
>().length().valueOr(0)) {
744 propp
->setTypedArrayElement(idx
);
746 propp
->setTypedArrayOutOfRange();
752 MOZ_ASSERT(cx
->compartment() == obj
->compartment());
754 // Check for a native property. Call Shape::lookup directly (instead of
755 // NativeObject::lookup) because it's inlined.
757 if (PropMap
* map
= obj
->shape()->lookup(cx
, id
, &index
)) {
758 propp
->setNativeProperty(map
->getPropertyInfo(index
));
762 // Some callers explicitily want us to ignore the resolve hook entirely. In
763 // that case, we report the property as NotFound.
764 if constexpr (resolveMode
== LookupResolveMode::IgnoreResolve
) {
765 propp
->setNotFound();
769 // JITs in particular use the `mayResolve` hook to determine a JSClass can
770 // never resolve this property name (for all instances of the class).
771 if constexpr (resolveMode
== LookupResolveMode::CheckMayResolve
) {
772 static_assert(allowGC
== false,
773 "CheckMayResolve can only be used with NoGC");
775 MOZ_ASSERT(propp
->isNotFound());
776 return !ClassMayResolveId(cx
->names(), obj
->getClass(), id
, obj
);
779 MOZ_ASSERT(resolveMode
== LookupResolveMode::CheckResolve
);
781 // If there is no resolve hook, the property definitely does not exist.
782 if (obj
->getClass()->getResolve()) {
783 if constexpr (!allowGC
) {
786 return CallResolveOp(cx
, obj
, id
, propp
);
790 propp
->setNotFound();
795 * Simplified version of NativeLookupOwnPropertyInline that doesn't call
798 [[nodiscard
]] static inline bool NativeLookupOwnPropertyNoResolve(
799 JSContext
* cx
, NativeObject
* obj
, jsid id
, PropertyResult
* result
) {
800 return NativeLookupOwnPropertyInline
<NoGC
, LookupResolveMode::IgnoreResolve
>(
801 cx
, obj
, id
, result
);
804 template <AllowGC allowGC
,
805 LookupResolveMode resolveMode
= LookupResolveMode::CheckResolve
>
806 static MOZ_ALWAYS_INLINE
bool NativeLookupPropertyInline(
807 JSContext
* cx
, typename MaybeRooted
<NativeObject
*, allowGC
>::HandleType obj
,
808 typename MaybeRooted
<jsid
, allowGC
>::HandleType id
,
809 typename MaybeRooted
<
810 std::conditional_t
<allowGC
== AllowGC::CanGC
, JSObject
*, NativeObject
*>,
811 allowGC
>::MutableHandleType objp
,
812 PropertyResult
* propp
) {
813 /* Search scopes starting with obj and following the prototype link. */
814 typename MaybeRooted
<NativeObject
*, allowGC
>::RootType
current(cx
, obj
);
817 if (!NativeLookupOwnPropertyInline
<allowGC
, resolveMode
>(cx
, current
, id
,
822 if (propp
->isFound()) {
827 if (propp
->shouldIgnoreProtoChain()) {
831 JSObject
* proto
= current
->staticPrototype();
836 // If a `lookupProperty` hook exists, recurse into LookupProperty, otherwise
837 // we can simply loop within this call frame.
838 if (proto
->getOpsLookupProperty()) {
839 if constexpr (allowGC
) {
840 RootedObject
protoRoot(cx
, proto
);
841 return LookupProperty(cx
, protoRoot
, id
, objp
, propp
);
847 current
= &proto
->as
<NativeObject
>();
850 MOZ_ASSERT(propp
->isNotFound());
855 inline bool ThrowIfNotConstructing(JSContext
* cx
, const CallArgs
& args
,
856 const char* builtinName
) {
857 if (args
.isConstructing()) {
860 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
861 JSMSG_BUILTIN_CTOR_NO_NEW
, builtinName
);
865 inline bool IsPackedArray(JSObject
* obj
) {
866 if (!obj
->is
<ArrayObject
>()) {
870 ArrayObject
* arr
= &obj
->as
<ArrayObject
>();
871 if (arr
->getDenseInitializedLength() != arr
->length()) {
875 if (!arr
->denseElementsArePacked()) {
880 // Assert correctness of the NON_PACKED flag by checking the first few
881 // elements don't contain holes.
882 uint32_t numToCheck
= std::min
<uint32_t>(5, arr
->getDenseInitializedLength());
883 for (uint32_t i
= 0; i
< numToCheck
; i
++) {
884 MOZ_ASSERT(!arr
->getDenseElement(i
).isMagic(JS_ELEMENTS_HOLE
));
891 // Like AddDataProperty but optimized for plain objects. Plain objects don't
892 // have an addProperty hook.
893 MOZ_ALWAYS_INLINE
bool AddDataPropertyToPlainObject(
894 JSContext
* cx
, Handle
<PlainObject
*> obj
, HandleId id
, HandleValue v
,
895 uint32_t* resultSlot
= nullptr) {
896 MOZ_ASSERT(!id
.isInt());
902 if (!NativeObject::addProperty(
903 cx
, obj
, id
, PropertyFlags::defaultDataPropFlags
, resultSlot
)) {
907 obj
->initSlot(*resultSlot
, v
);
909 MOZ_ASSERT(!obj
->getClass()->getAddProperty());
915 #endif /* vm_NativeObject_inl_h */