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/Heap-inl.h"
28 #include "gc/Marking-inl.h"
29 #include "gc/ObjectKind-inl.h"
30 #include "vm/Compartment-inl.h"
31 #include "vm/JSContext-inl.h"
32 #include "vm/JSObject-inl.h"
33 #include "vm/Realm-inl.h"
34 #include "vm/Shape-inl.h"
36 #ifdef ENABLE_RECORD_TUPLE
37 // Defined in vm/RecordTupleShared.{h,cpp}. We cannot include that file
38 // because it causes circular dependencies.
39 extern bool js::IsExtendedPrimitive(const JSObject
& obj
);
44 constexpr ObjectSlots::ObjectSlots(uint32_t capacity
,
45 uint32_t dictionarySlotSpan
,
46 uint64_t maybeUniqueId
)
47 : capacity_(capacity
),
48 dictionarySlotSpan_(dictionarySlotSpan
),
49 maybeUniqueId_(maybeUniqueId
) {
50 MOZ_ASSERT(this->capacity() == capacity
);
51 MOZ_ASSERT(this->dictionarySlotSpan() == dictionarySlotSpan
);
54 inline uint32_t NativeObject::numFixedSlotsMaybeForwarded() const {
55 return gc::MaybeForwarded(JSObject::shape())->asNative().numFixedSlots();
58 inline uint8_t* NativeObject::fixedData(size_t nslots
) const {
59 MOZ_ASSERT(ClassCanHaveFixedData(gc::MaybeForwardedObjectClass(this)));
60 MOZ_ASSERT(nslots
== numFixedSlotsMaybeForwarded());
61 return reinterpret_cast<uint8_t*>(&fixedSlots()[nslots
]);
64 inline void NativeObject::initDenseElementHole(uint32_t index
) {
65 markDenseElementsNotPacked();
66 initDenseElementUnchecked(index
, MagicValue(JS_ELEMENTS_HOLE
));
69 inline void NativeObject::setDenseElementHole(uint32_t index
) {
70 markDenseElementsNotPacked();
71 setDenseElementUnchecked(index
, MagicValue(JS_ELEMENTS_HOLE
));
74 inline void NativeObject::removeDenseElementForSparseIndex(uint32_t index
) {
75 MOZ_ASSERT(containsPure(PropertyKey::Int(index
)));
76 if (containsDenseElement(index
)) {
77 setDenseElementHole(index
);
81 inline void NativeObject::markDenseElementsNotPacked() {
82 MOZ_ASSERT(is
<NativeObject
>());
83 getElementsHeader()->markNonPacked();
86 inline void NativeObject::elementsRangePostWriteBarrier(uint32_t start
,
91 for (size_t i
= 0; i
< count
; i
++) {
92 const Value
& v
= elements_
[start
+ i
];
94 if (gc::StoreBuffer
* sb
= v
.toGCThing()->storeBuffer()) {
95 sb
->putSlot(this, HeapSlot::Element
, unshiftedIndex(start
+ i
),
103 inline void NativeObject::copyDenseElements(uint32_t dstStart
, const Value
* src
,
105 MOZ_ASSERT(dstStart
+ count
<= getDenseCapacity());
106 MOZ_ASSERT(isExtensible());
107 MOZ_ASSERT_IF(count
> 0, src
!= nullptr);
109 for (uint32_t i
= 0; i
< count
; ++i
) {
110 checkStoredValue(src
[i
]);
116 if (zone()->needsIncrementalBarrier()) {
117 uint32_t numShifted
= getElementsHeader()->numShiftedElements();
118 for (uint32_t i
= 0; i
< count
; ++i
) {
119 elements_
[dstStart
+ i
].set(this, HeapSlot::Element
,
120 dstStart
+ i
+ numShifted
, src
[i
]);
123 memcpy(reinterpret_cast<Value
*>(&elements_
[dstStart
]), src
,
124 count
* sizeof(Value
));
125 elementsRangePostWriteBarrier(dstStart
, count
);
129 inline void NativeObject::initDenseElements(NativeObject
* src
,
130 uint32_t srcStart
, uint32_t count
) {
131 MOZ_ASSERT(src
->getDenseInitializedLength() >= srcStart
+ count
);
133 const Value
* vp
= src
->getDenseElements() + srcStart
;
135 if (!src
->denseElementsArePacked()) {
136 // Mark non-packed if we're copying holes or if there are too many elements
137 // to check this efficiently.
138 static constexpr uint32_t MaxCountForPackedCheck
= 30;
139 if (count
> MaxCountForPackedCheck
) {
140 markDenseElementsNotPacked();
142 for (uint32_t i
= 0; i
< count
; i
++) {
143 if (vp
[i
].isMagic(JS_ELEMENTS_HOLE
)) {
144 markDenseElementsNotPacked();
151 initDenseElements(vp
, count
);
154 inline void NativeObject::initDenseElements(const Value
* src
, uint32_t count
) {
155 MOZ_ASSERT(getDenseInitializedLength() == 0);
156 MOZ_ASSERT(count
<= getDenseCapacity());
158 MOZ_ASSERT(isExtensible());
160 setDenseInitializedLength(count
);
163 for (uint32_t i
= 0; i
< count
; ++i
) {
164 checkStoredValue(src
[i
]);
168 memcpy(reinterpret_cast<Value
*>(elements_
), src
, count
* sizeof(Value
));
169 elementsRangePostWriteBarrier(0, count
);
172 inline void NativeObject::initDenseElements(JSLinearString
** src
,
174 MOZ_ASSERT(getDenseInitializedLength() == 0);
175 MOZ_ASSERT(count
<= getDenseCapacity());
177 MOZ_ASSERT(isExtensible());
179 setDenseInitializedLength(count
);
180 Value
* elementsBase
= reinterpret_cast<Value
*>(elements_
);
181 for (size_t i
= 0; i
< count
; i
++) {
182 elementsBase
[i
].setString(src
[i
]);
185 elementsRangePostWriteBarrier(0, count
);
188 inline void NativeObject::initDenseElementRange(uint32_t destStart
,
191 MOZ_ASSERT(count
<= src
->getDenseInitializedLength());
193 // The initialized length must already be set to the correct value.
194 MOZ_ASSERT(destStart
+ count
== getDenseInitializedLength());
196 if (!src
->denseElementsArePacked()) {
197 markDenseElementsNotPacked();
200 const Value
* vp
= src
->getDenseElements();
202 for (uint32_t i
= 0; i
< count
; ++i
) {
203 checkStoredValue(vp
[i
]);
206 memcpy(reinterpret_cast<Value
*>(elements_
) + destStart
, vp
,
207 count
* sizeof(Value
));
208 elementsRangePostWriteBarrier(destStart
, count
);
211 template <typename Iter
>
212 inline bool NativeObject::initDenseElementsFromRange(JSContext
* cx
, Iter begin
,
214 // This method populates the elements of a particular Array that's an
215 // internal implementation detail of GeneratorObject. Failing any of the
216 // following means the Array has escaped and/or been mistreated.
217 MOZ_ASSERT(isExtensible());
218 MOZ_ASSERT(!isIndexed());
219 MOZ_ASSERT(is
<ArrayObject
>());
220 MOZ_ASSERT(as
<ArrayObject
>().lengthIsWritable());
221 MOZ_ASSERT(!denseElementsAreFrozen());
222 MOZ_ASSERT(getElementsHeader()->numShiftedElements() == 0);
224 MOZ_ASSERT(getDenseInitializedLength() == 0);
226 auto size
= end
- begin
;
227 uint32_t count
= uint32_t(size
);
228 MOZ_ASSERT(count
<= uint32_t(INT32_MAX
));
229 if (count
> getDenseCapacity()) {
230 if (!growElements(cx
, count
)) {
235 HeapSlot
* sp
= elements_
;
237 for (; begin
!= end
; sp
++, begin
++) {
242 sp
->init(this, HeapSlot::Element
, slot
++, v
);
244 MOZ_ASSERT(slot
== count
);
246 getElementsHeader()->initializedLength
= count
;
247 as
<ArrayObject
>().setLength(count
);
251 inline bool NativeObject::tryShiftDenseElements(uint32_t count
) {
252 MOZ_ASSERT(isExtensible());
254 ObjectElements
* header
= getElementsHeader();
255 if (header
->initializedLength
== count
||
256 count
> ObjectElements::MaxShiftedElements
||
257 header
->hasNonwritableArrayLength()) {
261 shiftDenseElementsUnchecked(count
);
265 inline void NativeObject::shiftDenseElementsUnchecked(uint32_t count
) {
266 MOZ_ASSERT(isExtensible());
268 ObjectElements
* header
= getElementsHeader();
269 MOZ_ASSERT(count
> 0);
270 MOZ_ASSERT(count
< header
->initializedLength
);
272 if (MOZ_UNLIKELY(header
->numShiftedElements() + count
>
273 ObjectElements::MaxShiftedElements
)) {
274 moveShiftedElements();
275 header
= getElementsHeader();
278 prepareElementRangeForOverwrite(0, count
);
279 header
->addShiftedElements(count
);
282 ObjectElements
* newHeader
= getElementsHeader();
283 memmove(newHeader
, header
, sizeof(ObjectElements
));
286 inline void NativeObject::moveDenseElements(uint32_t dstStart
,
287 uint32_t srcStart
, uint32_t count
) {
288 MOZ_ASSERT(dstStart
+ count
<= getDenseCapacity());
289 MOZ_ASSERT(srcStart
+ count
<= getDenseInitializedLength());
290 MOZ_ASSERT(isExtensible());
293 * Using memmove here would skip write barriers. Also, we need to consider
294 * an array containing [A, B, C], in the following situation:
296 * 1. Incremental GC marks slot 0 of array (i.e., A), then returns to JS code.
297 * 2. JS code moves slots 1..2 into slots 0..1, so it contains [B, C, C].
298 * 3. Incremental GC finishes by marking slots 1 and 2 (i.e., C).
300 * Since normal marking never happens on B, it is very important that the
301 * write barrier is invoked here on B, despite the fact that it exists in
302 * the array before and after the move.
304 if (zone()->needsIncrementalBarrier()) {
305 uint32_t numShifted
= getElementsHeader()->numShiftedElements();
306 if (dstStart
< srcStart
) {
307 HeapSlot
* dst
= elements_
+ dstStart
;
308 HeapSlot
* src
= elements_
+ srcStart
;
309 for (uint32_t i
= 0; i
< count
; i
++, dst
++, src
++) {
310 dst
->set(this, HeapSlot::Element
, dst
- elements_
+ numShifted
, *src
);
313 HeapSlot
* dst
= elements_
+ dstStart
+ count
- 1;
314 HeapSlot
* src
= elements_
+ srcStart
+ count
- 1;
315 for (uint32_t i
= 0; i
< count
; i
++, dst
--, src
--) {
316 dst
->set(this, HeapSlot::Element
, dst
- elements_
+ numShifted
, *src
);
320 memmove(elements_
+ dstStart
, elements_
+ srcStart
,
321 count
* sizeof(HeapSlot
));
322 elementsRangePostWriteBarrier(dstStart
, count
);
326 inline void NativeObject::reverseDenseElementsNoPreBarrier(uint32_t length
) {
327 MOZ_ASSERT(!zone()->needsIncrementalBarrier());
329 MOZ_ASSERT(isExtensible());
331 MOZ_ASSERT(length
> 1);
332 MOZ_ASSERT(length
<= getDenseInitializedLength());
334 Value
* valLo
= reinterpret_cast<Value
*>(elements_
);
335 Value
* valHi
= valLo
+ (length
- 1);
336 MOZ_ASSERT(valLo
< valHi
);
339 Value origLo
= *valLo
;
344 } while (valLo
< valHi
);
346 elementsRangePostWriteBarrier(0, length
);
349 inline void NativeObject::ensureDenseInitializedLength(uint32_t index
,
351 // Ensure that the array's contents have been initialized up to index, and
352 // mark the elements through 'index + extra' as initialized in preparation
355 MOZ_ASSERT(!denseElementsAreFrozen());
356 MOZ_ASSERT(isExtensible() || (containsDenseElement(index
) && extra
== 1));
357 MOZ_ASSERT(index
+ extra
<= getDenseCapacity());
359 uint32_t initlen
= getDenseInitializedLength();
360 if (index
+ extra
<= initlen
) {
364 MOZ_ASSERT(isExtensible());
366 if (index
> initlen
) {
367 markDenseElementsNotPacked();
370 uint32_t numShifted
= getElementsHeader()->numShiftedElements();
371 size_t offset
= initlen
;
372 for (HeapSlot
* sp
= elements_
+ initlen
; sp
!= elements_
+ (index
+ extra
);
374 sp
->init(this, HeapSlot::Element
, offset
+ numShifted
,
375 MagicValue(JS_ELEMENTS_HOLE
));
378 getElementsHeader()->initializedLength
= index
+ extra
;
381 DenseElementResult
NativeObject::extendDenseElements(JSContext
* cx
,
382 uint32_t requiredCapacity
,
384 MOZ_ASSERT(isExtensible());
387 * Don't grow elements for objects which already have sparse indexes.
388 * This avoids needing to count non-hole elements in willBeSparseElements
389 * every time a new index is added.
392 return DenseElementResult::Incomplete
;
396 * We use the extra argument also as a hint about number of non-hole
397 * elements to be inserted.
399 if (requiredCapacity
> MIN_SPARSE_INDEX
&&
400 willBeSparseElements(requiredCapacity
, extra
)) {
401 return DenseElementResult::Incomplete
;
404 if (!growElements(cx
, requiredCapacity
)) {
405 return DenseElementResult::Failure
;
408 return DenseElementResult::Success
;
411 inline DenseElementResult
NativeObject::ensureDenseElements(JSContext
* cx
,
414 MOZ_ASSERT(is
<NativeObject
>());
415 MOZ_ASSERT(isExtensible() || (containsDenseElement(index
) && extra
== 1));
417 uint32_t requiredCapacity
;
419 /* Optimize for the common case. */
420 if (index
< getDenseCapacity()) {
421 ensureDenseInitializedLength(index
, 1);
422 return DenseElementResult::Success
;
424 requiredCapacity
= index
+ 1;
425 if (requiredCapacity
== 0) {
427 return DenseElementResult::Incomplete
;
430 requiredCapacity
= index
+ extra
;
431 if (requiredCapacity
< index
) {
433 return DenseElementResult::Incomplete
;
435 if (requiredCapacity
<= getDenseCapacity()) {
436 ensureDenseInitializedLength(index
, extra
);
437 return DenseElementResult::Success
;
441 DenseElementResult result
= extendDenseElements(cx
, requiredCapacity
, extra
);
442 if (result
!= DenseElementResult::Success
) {
446 ensureDenseInitializedLength(index
, extra
);
447 return DenseElementResult::Success
;
450 inline DenseElementResult
NativeObject::setOrExtendDenseElements(
451 JSContext
* cx
, uint32_t start
, const Value
* vp
, uint32_t count
) {
452 if (!isExtensible()) {
453 return DenseElementResult::Incomplete
;
456 if (is
<ArrayObject
>() && !as
<ArrayObject
>().lengthIsWritable() &&
457 start
+ count
>= as
<ArrayObject
>().length()) {
458 return DenseElementResult::Incomplete
;
461 DenseElementResult result
= ensureDenseElements(cx
, start
, count
);
462 if (result
!= DenseElementResult::Success
) {
466 if (is
<ArrayObject
>() && start
+ count
>= as
<ArrayObject
>().length()) {
467 as
<ArrayObject
>().setLength(start
+ count
);
470 copyDenseElements(start
, vp
, count
);
471 return DenseElementResult::Success
;
474 inline bool NativeObject::isInWholeCellBuffer() const {
475 const gc::TenuredCell
* cell
= &asTenured();
476 gc::ArenaCellSet
* cells
= cell
->arena()->bufferedCells();
477 return cells
&& cells
->hasCell(cell
);
481 inline NativeObject
* NativeObject::create(
482 JSContext
* cx
, js::gc::AllocKind kind
, js::gc::Heap heap
,
483 js::Handle
<SharedShape
*> shape
, js::gc::AllocSite
* site
/* = nullptr */) {
484 debugCheckNewObject(shape
, kind
, heap
);
486 const JSClass
* clasp
= shape
->getObjectClass();
487 MOZ_ASSERT(clasp
->isNativeObject());
488 MOZ_ASSERT(!clasp
->isJSFunction(), "should use JSFunction::create");
489 MOZ_ASSERT(clasp
!= &ArrayObject::class_
, "should use ArrayObject::create");
491 const uint32_t nfixed
= shape
->numFixedSlots();
492 const uint32_t slotSpan
= shape
->slotSpan();
493 const size_t nDynamicSlots
= calculateDynamicSlots(nfixed
, slotSpan
, clasp
);
495 NativeObject
* nobj
= cx
->newCell
<NativeObject
>(kind
, heap
, clasp
, site
);
500 nobj
->initShape(shape
);
501 nobj
->setEmptyElements();
503 if (!nDynamicSlots
) {
504 nobj
->initEmptyDynamicSlots();
505 } else if (!nobj
->allocateInitialSlots(cx
, nDynamicSlots
)) {
510 nobj
->initSlots(nfixed
, slotSpan
);
513 if (MOZ_UNLIKELY(cx
->realm()->hasAllocationMetadataBuilder())) {
514 if (clasp
->shouldDelayMetadataBuilder()) {
515 cx
->realm()->setObjectPendingMetadata(nobj
);
517 nobj
= SetNewObjectMetadata(cx
, nobj
);
521 js::gc::gcprobes::CreateObject(nobj
);
526 MOZ_ALWAYS_INLINE
void NativeObject::initEmptyDynamicSlots() {
527 setEmptyDynamicSlots(0);
530 MOZ_ALWAYS_INLINE
void NativeObject::setDictionaryModeSlotSpan(uint32_t span
) {
531 MOZ_ASSERT(inDictionaryMode());
533 if (!hasDynamicSlots()) {
534 setEmptyDynamicSlots(span
);
538 getSlotsHeader()->setDictionarySlotSpan(span
);
541 MOZ_ALWAYS_INLINE
void NativeObject::setEmptyDynamicSlots(
542 uint32_t dictionarySlotSpan
) {
543 MOZ_ASSERT_IF(!inDictionaryMode(), dictionarySlotSpan
== 0);
544 MOZ_ASSERT(dictionarySlotSpan
<= MAX_FIXED_SLOTS
);
546 slots_
= emptyObjectSlotsForDictionaryObject
[dictionarySlotSpan
];
548 MOZ_ASSERT(getSlotsHeader()->capacity() == 0);
549 MOZ_ASSERT(getSlotsHeader()->dictionarySlotSpan() == dictionarySlotSpan
);
550 MOZ_ASSERT(!hasDynamicSlots());
551 MOZ_ASSERT(!hasUniqueId());
554 MOZ_ALWAYS_INLINE
bool NativeObject::setShapeAndAddNewSlots(
555 JSContext
* cx
, SharedShape
* newShape
, uint32_t oldSpan
, uint32_t newSpan
) {
556 MOZ_ASSERT(!inDictionaryMode());
557 MOZ_ASSERT(newShape
->isShared());
558 MOZ_ASSERT(newShape
->zone() == zone());
559 MOZ_ASSERT(newShape
->numFixedSlots() == numFixedSlots());
560 MOZ_ASSERT(newShape
->getObjectClass() == getClass());
562 MOZ_ASSERT(oldSpan
< newSpan
);
563 MOZ_ASSERT(sharedShape()->slotSpan() == oldSpan
);
564 MOZ_ASSERT(newShape
->slotSpan() == newSpan
);
566 uint32_t numFixed
= newShape
->numFixedSlots();
567 if (newSpan
> numFixed
) {
568 uint32_t oldCapacity
= numDynamicSlots();
569 uint32_t newCapacity
=
570 calculateDynamicSlots(numFixed
, newSpan
, newShape
->getObjectClass());
571 MOZ_ASSERT(oldCapacity
<= newCapacity
);
573 if (oldCapacity
< newCapacity
) {
574 if (MOZ_UNLIKELY(!growSlots(cx
, oldCapacity
, newCapacity
))) {
580 // Initialize slots [oldSpan, newSpan). Use the *Unchecked version because
581 // the shape's slot span does not reflect the allocated slots at this
583 auto initRange
= [](HeapSlot
* start
, HeapSlot
* end
) {
584 for (HeapSlot
* slot
= start
; slot
< end
; slot
++) {
585 slot
->initAsUndefined();
588 forEachSlotRangeUnchecked(oldSpan
, newSpan
, initRange
);
594 MOZ_ALWAYS_INLINE
bool NativeObject::setShapeAndAddNewSlot(
595 JSContext
* cx
, SharedShape
* newShape
, uint32_t slot
) {
596 MOZ_ASSERT(!inDictionaryMode());
597 MOZ_ASSERT(newShape
->isShared());
598 MOZ_ASSERT(newShape
->zone() == zone());
599 MOZ_ASSERT(newShape
->numFixedSlots() == numFixedSlots());
601 MOZ_ASSERT(newShape
->base() == shape()->base());
602 MOZ_ASSERT(newShape
->slotSpan() == sharedShape()->slotSpan() + 1);
603 MOZ_ASSERT(newShape
->slotSpan() == slot
+ 1);
605 uint32_t numFixed
= newShape
->numFixedSlots();
606 if (slot
< numFixed
) {
607 initFixedSlot(slot
, UndefinedValue());
609 uint32_t dynamicSlotIndex
= slot
- numFixed
;
610 if (dynamicSlotIndex
>= numDynamicSlots()) {
611 if (MOZ_UNLIKELY(!growSlotsForNewSlot(cx
, numFixed
, slot
))) {
615 initDynamicSlot(numFixed
, slot
, UndefinedValue());
622 inline js::gc::AllocKind
NativeObject::allocKindForTenure() const {
623 using namespace js::gc
;
624 AllocKind kind
= GetGCObjectFixedSlotsKind(numFixedSlots());
625 MOZ_ASSERT(!IsBackgroundFinalized(kind
));
626 if (!CanChangeToBackgroundAllocKind(kind
, getClass())) {
629 return ForegroundToBackgroundAllocKind(kind
);
632 inline js::GlobalObject
& NativeObject::global() const { return nonCCWGlobal(); }
634 inline bool NativeObject::denseElementsHaveMaybeInIterationFlag() {
635 if (!getElementsHeader()->maybeInIteration()) {
636 AssertDenseElementsNotIterated(this);
642 inline bool NativeObject::denseElementsMaybeInIteration() {
643 if (!denseElementsHaveMaybeInIterationFlag()) {
646 return compartment()->objectMaybeInIteration(this);
650 * Call obj's resolve hook.
652 * cx and id are the parameters initially passed to the ongoing lookup;
653 * propp and recursedp are its out parameters.
655 * There are four possible outcomes:
657 * - On failure, report an error or exception and return false.
659 * - If we are already resolving a property of obj, call setRecursiveResolve on
660 * propp and return true.
662 * - If the resolve hook finds or defines the sought property, set propp
663 * appropriately, and return true.
665 * - Otherwise no property was resolved. Set propp to NotFound and return true.
667 static MOZ_ALWAYS_INLINE
bool CallResolveOp(JSContext
* cx
,
668 Handle
<NativeObject
*> obj
,
670 PropertyResult
* propp
) {
671 // Avoid recursion on (obj, id) already being resolved on cx.
672 AutoResolving
resolving(cx
, obj
, id
);
673 if (resolving
.alreadyStarted()) {
674 // Already resolving id in obj, suppress recursion.
675 propp
->setRecursiveResolve();
679 bool resolved
= false;
680 AutoRealm
ar(cx
, obj
);
681 if (!obj
->getClass()->getResolve()(cx
, obj
, id
, &resolved
)) {
686 propp
->setNotFound();
690 // Assert the mayResolve hook, if there is one, returns true for this
692 MOZ_ASSERT_IF(obj
->getClass()->getMayResolve(),
693 obj
->getClass()->getMayResolve()(cx
->names(), id
, obj
));
696 uint32_t index
= id
.toInt();
697 if (obj
->containsDenseElement(index
)) {
698 propp
->setDenseElement(index
);
703 MOZ_ASSERT(!obj
->is
<TypedArrayObject
>());
705 mozilla::Maybe
<PropertyInfo
> prop
= obj
->lookup(cx
, id
);
707 propp
->setNativeProperty(*prop
);
709 propp
->setNotFound();
715 enum class LookupResolveMode
{
721 template <AllowGC allowGC
,
722 LookupResolveMode resolveMode
= LookupResolveMode::CheckResolve
>
723 static MOZ_ALWAYS_INLINE
bool NativeLookupOwnPropertyInline(
724 JSContext
* cx
, typename MaybeRooted
<NativeObject
*, allowGC
>::HandleType obj
,
725 typename MaybeRooted
<jsid
, allowGC
>::HandleType id
, PropertyResult
* propp
) {
726 // Native objects should should avoid `lookupProperty` hooks, and those that
727 // use them should avoid recursively triggering lookup, and those that still
728 // violate this guidance are the ModuleEnvironmentObject.
729 MOZ_ASSERT_IF(obj
->getOpsLookupProperty(),
730 obj
->template is
<ModuleEnvironmentObject
>());
731 #ifdef ENABLE_RECORD_TUPLE
732 MOZ_ASSERT(!js::IsExtendedPrimitive(*obj
));
735 // Check for a native dense element.
737 uint32_t index
= id
.toInt();
738 if (obj
->containsDenseElement(index
)) {
739 propp
->setDenseElement(index
);
744 // Check for a typed array element. Integer lookups always finish here
745 // so that integer properties on the prototype are ignored even for out
746 // of bounds accesses.
747 if (obj
->template is
<TypedArrayObject
>()) {
748 if (mozilla::Maybe
<uint64_t> index
= ToTypedArrayIndex(id
)) {
749 uint64_t idx
= index
.value();
750 if (idx
< obj
->template as
<TypedArrayObject
>().length().valueOr(0)) {
751 propp
->setTypedArrayElement(idx
);
753 propp
->setTypedArrayOutOfRange();
759 MOZ_ASSERT(cx
->compartment() == obj
->compartment());
761 // Check for a native property. Call Shape::lookup directly (instead of
762 // NativeObject::lookup) because it's inlined.
764 if (PropMap
* map
= obj
->shape()->lookup(cx
, id
, &index
)) {
765 propp
->setNativeProperty(map
->getPropertyInfo(index
));
769 // Some callers explicitily want us to ignore the resolve hook entirely. In
770 // that case, we report the property as NotFound.
771 if constexpr (resolveMode
== LookupResolveMode::IgnoreResolve
) {
772 propp
->setNotFound();
776 // JITs in particular use the `mayResolve` hook to determine a JSClass can
777 // never resolve this property name (for all instances of the class).
778 if constexpr (resolveMode
== LookupResolveMode::CheckMayResolve
) {
779 static_assert(allowGC
== false,
780 "CheckMayResolve can only be used with NoGC");
782 MOZ_ASSERT(propp
->isNotFound());
783 return !ClassMayResolveId(cx
->names(), obj
->getClass(), id
, obj
);
786 MOZ_ASSERT(resolveMode
== LookupResolveMode::CheckResolve
);
788 // If there is no resolve hook, the property definitely does not exist.
789 if (obj
->getClass()->getResolve()) {
790 if constexpr (!allowGC
) {
793 return CallResolveOp(cx
, obj
, id
, propp
);
797 propp
->setNotFound();
802 * Simplified version of NativeLookupOwnPropertyInline that doesn't call
805 [[nodiscard
]] static inline bool NativeLookupOwnPropertyNoResolve(
806 JSContext
* cx
, NativeObject
* obj
, jsid id
, PropertyResult
* result
) {
807 return NativeLookupOwnPropertyInline
<NoGC
, LookupResolveMode::IgnoreResolve
>(
808 cx
, obj
, id
, result
);
811 template <AllowGC allowGC
,
812 LookupResolveMode resolveMode
= LookupResolveMode::CheckResolve
>
813 static MOZ_ALWAYS_INLINE
bool NativeLookupPropertyInline(
814 JSContext
* cx
, typename MaybeRooted
<NativeObject
*, allowGC
>::HandleType obj
,
815 typename MaybeRooted
<jsid
, allowGC
>::HandleType id
,
816 typename MaybeRooted
<
817 std::conditional_t
<allowGC
== AllowGC::CanGC
, JSObject
*, NativeObject
*>,
818 allowGC
>::MutableHandleType objp
,
819 PropertyResult
* propp
) {
820 /* Search scopes starting with obj and following the prototype link. */
821 typename MaybeRooted
<NativeObject
*, allowGC
>::RootType
current(cx
, obj
);
824 if (!NativeLookupOwnPropertyInline
<allowGC
, resolveMode
>(cx
, current
, id
,
829 if (propp
->isFound()) {
834 if (propp
->shouldIgnoreProtoChain()) {
838 JSObject
* proto
= current
->staticPrototype();
843 // If a `lookupProperty` hook exists, recurse into LookupProperty, otherwise
844 // we can simply loop within this call frame.
845 if (proto
->getOpsLookupProperty()) {
846 if constexpr (allowGC
) {
847 RootedObject
protoRoot(cx
, proto
);
848 return LookupProperty(cx
, protoRoot
, id
, objp
, propp
);
854 current
= &proto
->as
<NativeObject
>();
857 MOZ_ASSERT(propp
->isNotFound());
862 inline bool ThrowIfNotConstructing(JSContext
* cx
, const CallArgs
& args
,
863 const char* builtinName
) {
864 if (args
.isConstructing()) {
867 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
868 JSMSG_BUILTIN_CTOR_NO_NEW
, builtinName
);
872 inline bool IsPackedArray(JSObject
* obj
) {
873 if (!obj
->is
<ArrayObject
>()) {
877 ArrayObject
* arr
= &obj
->as
<ArrayObject
>();
878 if (arr
->getDenseInitializedLength() != arr
->length()) {
882 if (!arr
->denseElementsArePacked()) {
887 // Assert correctness of the NON_PACKED flag by checking the first few
888 // elements don't contain holes.
889 uint32_t numToCheck
= std::min
<uint32_t>(5, arr
->getDenseInitializedLength());
890 for (uint32_t i
= 0; i
< numToCheck
; i
++) {
891 MOZ_ASSERT(!arr
->getDenseElement(i
).isMagic(JS_ELEMENTS_HOLE
));
898 // Like AddDataProperty but optimized for plain objects. Plain objects don't
899 // have an addProperty hook.
900 MOZ_ALWAYS_INLINE
bool AddDataPropertyToPlainObject(
901 JSContext
* cx
, Handle
<PlainObject
*> obj
, HandleId id
, HandleValue v
,
902 uint32_t* resultSlot
= nullptr) {
903 MOZ_ASSERT(!id
.isInt());
909 if (!NativeObject::addProperty(
910 cx
, obj
, id
, PropertyFlags::defaultDataPropFlags
, resultSlot
)) {
914 obj
->initSlot(*resultSlot
, v
);
916 MOZ_ASSERT(!obj
->getClass()->getAddProperty());
922 #endif /* vm_NativeObject_inl_h */