no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / js / src / vm / NativeObject-inl.h
blob561506f995c0eb76b999269dd1eaadc11e668614
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);
39 #endif
41 namespace js {
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,
86 uint32_t count) {
87 if (!isTenured()) {
88 return;
90 for (size_t i = 0; i < count; i++) {
91 const Value& v = elements_[start + i];
92 if (v.isGCThing()) {
93 if (gc::StoreBuffer* sb = v.toGCThing()->storeBuffer()) {
94 sb->putSlot(this, HeapSlot::Element, unshiftedIndex(start + i),
95 count - i);
96 return;
102 inline void NativeObject::copyDenseElements(uint32_t dstStart, const Value* src,
103 uint32_t count) {
104 MOZ_ASSERT(dstStart + count <= getDenseCapacity());
105 MOZ_ASSERT(isExtensible());
106 MOZ_ASSERT_IF(count > 0, src != nullptr);
107 #ifdef DEBUG
108 for (uint32_t i = 0; i < count; ++i) {
109 checkStoredValue(src[i]);
111 #endif
112 if (count == 0) {
113 return;
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]);
121 } else {
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();
140 } else {
141 for (uint32_t i = 0; i < count; i++) {
142 if (vp[i].isMagic(JS_ELEMENTS_HOLE)) {
143 markDenseElementsNotPacked();
144 break;
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());
156 MOZ_ASSERT(src);
157 MOZ_ASSERT(isExtensible());
159 setDenseInitializedLength(count);
161 #ifdef DEBUG
162 for (uint32_t i = 0; i < count; ++i) {
163 checkStoredValue(src[i]);
165 #endif
167 memcpy(reinterpret_cast<Value*>(elements_), src, count * sizeof(Value));
168 elementsRangePostWriteBarrier(0, count);
171 inline void NativeObject::initDenseElements(JSLinearString** src,
172 uint32_t count) {
173 MOZ_ASSERT(getDenseInitializedLength() == 0);
174 MOZ_ASSERT(count <= getDenseCapacity());
175 MOZ_ASSERT(src);
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,
188 NativeObject* src,
189 uint32_t count) {
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();
200 #ifdef DEBUG
201 for (uint32_t i = 0; i < count; ++i) {
202 checkStoredValue(vp[i]);
204 #endif
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,
212 Iter end) {
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)) {
230 return false;
234 HeapSlot* sp = elements_;
235 size_t slot = 0;
236 for (; begin != end; sp++, begin++) {
237 Value v = *begin;
238 #ifdef DEBUG
239 checkStoredValue(v);
240 #endif
241 sp->init(this, HeapSlot::Element, slot++, v);
243 MOZ_ASSERT(slot == count);
245 getElementsHeader()->initializedLength = count;
246 as<ArrayObject>().setLength(count);
247 return true;
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()) {
257 return false;
260 shiftDenseElementsUnchecked(count);
261 return true;
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);
280 elements_ += 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);
311 } else {
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);
318 } else {
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);
337 do {
338 Value origLo = *valLo;
339 *valLo = *valHi;
340 *valHi = origLo;
341 ++valLo;
342 --valHi;
343 } while (valLo < valHi);
345 elementsRangePostWriteBarrier(0, length);
348 inline void NativeObject::ensureDenseInitializedLength(uint32_t index,
349 uint32_t extra) {
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
352 // for a write.
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) {
360 return;
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);
372 sp++, offset++) {
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,
382 uint32_t extra) {
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.
390 if (isIndexed()) {
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,
411 uint32_t index,
412 uint32_t extra) {
413 MOZ_ASSERT(is<NativeObject>());
414 MOZ_ASSERT(isExtensible() || (containsDenseElement(index) && extra == 1));
416 uint32_t requiredCapacity;
417 if (extra == 1) {
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) {
425 /* Overflow. */
426 return DenseElementResult::Incomplete;
428 } else {
429 requiredCapacity = index + extra;
430 if (requiredCapacity < index) {
431 /* Overflow. */
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) {
442 return result;
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) {
462 return result;
465 if (is<ArrayObject>() && start + count >= as<ArrayObject>().length()) {
466 as<ArrayObject>().setLength(start + count);
469 copyDenseElements(start, vp, count);
470 return DenseElementResult::Success;
473 /* static */
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);
489 if (!nobj) {
490 return nullptr;
493 nobj->initShape(shape);
494 nobj->setEmptyElements();
496 if (!nDynamicSlots) {
497 nobj->initEmptyDynamicSlots();
498 } else if (!nobj->allocateInitialSlots(cx, nDynamicSlots)) {
499 return nullptr;
502 if (slotSpan > 0) {
503 nobj->initSlots(nfixed, slotSpan);
506 if (MOZ_UNLIKELY(cx->realm()->hasAllocationMetadataBuilder())) {
507 if (clasp->shouldDelayMetadataBuilder()) {
508 cx->realm()->setObjectPendingMetadata(nobj);
509 } else {
510 nobj = SetNewObjectMetadata(cx, nobj);
514 js::gc::gcprobes::CreateObject(nobj);
516 return 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);
528 return;
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))) {
568 return false;
573 // Initialize slots [oldSpan, newSpan). Use the *Unchecked version because
574 // the shape's slot span does not reflect the allocated slots at this
575 // point.
576 auto initRange = [](HeapSlot* start, HeapSlot* end) {
577 for (HeapSlot* slot = start; slot < end; slot++) {
578 slot->initAsUndefined();
581 forEachSlotRangeUnchecked(oldSpan, newSpan, initRange);
583 setShape(newShape);
584 return true;
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());
601 } else {
602 uint32_t dynamicSlotIndex = slot - numFixed;
603 if (dynamicSlotIndex >= numDynamicSlots()) {
604 if (MOZ_UNLIKELY(!growSlotsForNewSlot(cx, numFixed, slot))) {
605 return false;
608 initDynamicSlot(numFixed, slot, UndefinedValue());
611 setShape(newShape);
612 return true;
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())) {
620 return kind;
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);
630 return false;
632 return true;
635 inline bool NativeObject::denseElementsMaybeInIteration() {
636 if (!denseElementsHaveMaybeInIterationFlag()) {
637 return false;
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,
662 HandleId id,
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();
669 return true;
672 bool resolved = false;
673 AutoRealm ar(cx, obj);
674 if (!obj->getClass()->getResolve()(cx, obj, id, &resolved)) {
675 return false;
678 if (!resolved) {
679 propp->setNotFound();
680 return true;
683 // Assert the mayResolve hook, if there is one, returns true for this
684 // property.
685 MOZ_ASSERT_IF(obj->getClass()->getMayResolve(),
686 obj->getClass()->getMayResolve()(cx->names(), id, obj));
688 if (id.isInt()) {
689 uint32_t index = id.toInt();
690 if (obj->containsDenseElement(index)) {
691 propp->setDenseElement(index);
692 return true;
696 MOZ_ASSERT(!obj->is<TypedArrayObject>());
698 mozilla::Maybe<PropertyInfo> prop = obj->lookup(cx, id);
699 if (prop.isSome()) {
700 propp->setNativeProperty(*prop);
701 } else {
702 propp->setNotFound();
705 return true;
708 enum class LookupResolveMode {
709 IgnoreResolve,
710 CheckResolve,
711 CheckMayResolve,
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));
726 #endif
728 // Check for a native dense element.
729 if (id.isInt()) {
730 uint32_t index = id.toInt();
731 if (obj->containsDenseElement(index)) {
732 propp->setDenseElement(index);
733 return true;
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);
745 } else {
746 propp->setTypedArrayOutOfRange();
748 return true;
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.
756 uint32_t index;
757 if (PropMap* map = obj->shape()->lookup(cx, id, &index)) {
758 propp->setNativeProperty(map->getPropertyInfo(index));
759 return true;
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();
766 return true;
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) {
784 return false;
785 } else {
786 return CallResolveOp(cx, obj, id, propp);
790 propp->setNotFound();
791 return true;
795 * Simplified version of NativeLookupOwnPropertyInline that doesn't call
796 * resolve hooks.
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);
816 while (true) {
817 if (!NativeLookupOwnPropertyInline<allowGC, resolveMode>(cx, current, id,
818 propp)) {
819 return false;
822 if (propp->isFound()) {
823 objp.set(current);
824 return true;
827 if (propp->shouldIgnoreProtoChain()) {
828 break;
831 JSObject* proto = current->staticPrototype();
832 if (!proto) {
833 break;
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);
842 } else {
843 return false;
847 current = &proto->as<NativeObject>();
850 MOZ_ASSERT(propp->isNotFound());
851 objp.set(nullptr);
852 return true;
855 inline bool ThrowIfNotConstructing(JSContext* cx, const CallArgs& args,
856 const char* builtinName) {
857 if (args.isConstructing()) {
858 return true;
860 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
861 JSMSG_BUILTIN_CTOR_NO_NEW, builtinName);
862 return false;
865 inline bool IsPackedArray(JSObject* obj) {
866 if (!obj->is<ArrayObject>()) {
867 return false;
870 ArrayObject* arr = &obj->as<ArrayObject>();
871 if (arr->getDenseInitializedLength() != arr->length()) {
872 return false;
875 if (!arr->denseElementsArePacked()) {
876 return false;
879 #ifdef DEBUG
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));
886 #endif
888 return true;
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());
898 uint32_t slot;
899 if (!resultSlot) {
900 resultSlot = &slot;
902 if (!NativeObject::addProperty(
903 cx, obj, id, PropertyFlags::defaultDataPropFlags, resultSlot)) {
904 return false;
907 obj->initSlot(*resultSlot, v);
909 MOZ_ASSERT(!obj->getClass()->getAddProperty());
910 return true;
913 } // namespace js
915 #endif /* vm_NativeObject_inl_h */