LCOV - code coverage report
Current view: top level - js/src/vm - NativeObject.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 756 1286 58.8 %
Date: 2017-07-14 16:53:18 Functions: 71 88 80.7 %
Legend: Lines: hit not hit

          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             : #include "vm/NativeObject-inl.h"
       8             : 
       9             : #include "mozilla/ArrayUtils.h"
      10             : #include "mozilla/Casting.h"
      11             : #include "mozilla/CheckedInt.h"
      12             : 
      13             : #include "jswatchpoint.h"
      14             : 
      15             : #include "gc/Marking.h"
      16             : #include "js/Value.h"
      17             : #include "vm/Debugger.h"
      18             : #include "vm/TypedArrayObject.h"
      19             : 
      20             : #include "jsobjinlines.h"
      21             : 
      22             : #include "gc/Nursery-inl.h"
      23             : #include "vm/ArrayObject-inl.h"
      24             : #include "vm/EnvironmentObject-inl.h"
      25             : #include "vm/Shape-inl.h"
      26             : 
      27             : using namespace js;
      28             : 
      29             : using JS::AutoCheckCannotGC;
      30             : using JS::GenericNaN;
      31             : using mozilla::ArrayLength;
      32             : using mozilla::CheckedInt;
      33             : using mozilla::DebugOnly;
      34             : using mozilla::PodCopy;
      35             : using mozilla::RoundUpPow2;
      36             : 
      37             : static const ObjectElements emptyElementsHeader(0, 0);
      38             : 
      39             : /* Objects with no elements share one empty set of elements. */
      40             : HeapSlot* const js::emptyObjectElements =
      41           4 :     reinterpret_cast<HeapSlot*>(uintptr_t(&emptyElementsHeader) + sizeof(ObjectElements));
      42             : 
      43             : static const ObjectElements emptyElementsHeaderShared(0, 0, ObjectElements::SharedMemory::IsShared);
      44             : 
      45             : /* Objects with no elements share one empty set of elements. */
      46             : HeapSlot* const js::emptyObjectElementsShared =
      47           0 :     reinterpret_cast<HeapSlot*>(uintptr_t(&emptyElementsHeaderShared) + sizeof(ObjectElements));
      48             : 
      49             : 
      50             : #ifdef DEBUG
      51             : 
      52             : bool
      53        7840 : NativeObject::canHaveNonEmptyElements()
      54             : {
      55        7840 :     return !this->is<TypedArrayObject>();
      56             : }
      57             : 
      58             : #endif // DEBUG
      59             : 
      60             : /* static */ bool
      61           0 : ObjectElements::ConvertElementsToDoubles(JSContext* cx, uintptr_t elementsPtr)
      62             : {
      63             :     /*
      64             :      * This function is infallible, but has a fallible interface so that it can
      65             :      * be called directly from Ion code. Only arrays can have their dense
      66             :      * elements converted to doubles, and arrays never have empty elements.
      67             :      */
      68           0 :     HeapSlot* elementsHeapPtr = (HeapSlot*) elementsPtr;
      69           0 :     MOZ_ASSERT(elementsHeapPtr != emptyObjectElements &&
      70             :                elementsHeapPtr != emptyObjectElementsShared);
      71             : 
      72           0 :     ObjectElements* header = ObjectElements::fromElements(elementsHeapPtr);
      73           0 :     MOZ_ASSERT(!header->shouldConvertDoubleElements());
      74             : 
      75             :     // Note: the elements can be mutated in place even for copy on write
      76             :     // arrays. See comment on ObjectElements.
      77           0 :     Value* vp = (Value*) elementsPtr;
      78           0 :     for (size_t i = 0; i < header->initializedLength; i++) {
      79           0 :         if (vp[i].isInt32())
      80           0 :             vp[i].setDouble(vp[i].toInt32());
      81             :     }
      82             : 
      83           0 :     header->setShouldConvertDoubleElements();
      84           0 :     return true;
      85             : }
      86             : 
      87             : /* static */ bool
      88        1436 : ObjectElements::MakeElementsCopyOnWrite(JSContext* cx, NativeObject* obj)
      89             : {
      90             :     static_assert(sizeof(HeapSlot) >= sizeof(GCPtrObject),
      91             :                   "there must be enough room for the owner object pointer at "
      92             :                   "the end of the elements");
      93        1436 :     if (!obj->ensureElements(cx, obj->getDenseInitializedLength() + 1))
      94           0 :         return false;
      95             : 
      96        1436 :     ObjectElements* header = obj->getElementsHeader();
      97             : 
      98             :     // Note: this method doesn't update type information to indicate that the
      99             :     // elements might be copy on write. Handling this is left to the caller.
     100        1436 :     MOZ_ASSERT(!header->isCopyOnWrite());
     101        1436 :     MOZ_ASSERT(!header->isFrozen());
     102        1436 :     header->flags |= COPY_ON_WRITE;
     103             : 
     104        1436 :     header->ownerObject().init(obj);
     105        1436 :     return true;
     106             : }
     107             : 
     108             : /* static */ bool
     109        2390 : ObjectElements::FreezeElements(JSContext* cx, HandleNativeObject obj)
     110             : {
     111        2390 :     MOZ_ASSERT_IF(obj->is<ArrayObject>(),
     112             :                   !obj->as<ArrayObject>().lengthIsWritable());
     113             : 
     114        2390 :     if (!obj->maybeCopyElementsForWrite(cx))
     115           0 :         return false;
     116             : 
     117        2390 :     if (obj->hasEmptyElements() || obj->denseElementsAreFrozen())
     118        2382 :         return true;
     119             : 
     120           8 :     if (obj->getElementsHeader()->numShiftedElements() > 0)
     121           0 :         obj->moveShiftedElements();
     122             : 
     123           8 :     MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_FROZEN_ELEMENTS);
     124           8 :     obj->getElementsHeader()->freeze();
     125             : 
     126           8 :     return true;
     127             : }
     128             : 
     129             : #ifdef DEBUG
     130             : void
     131      118808 : js::NativeObject::checkShapeConsistency()
     132             : {
     133             :     static int throttle = -1;
     134      118808 :     if (throttle < 0) {
     135           3 :         if (const char* var = getenv("JS_CHECK_SHAPE_THROTTLE"))
     136           0 :             throttle = atoi(var);
     137           3 :         if (throttle < 0)
     138           3 :             throttle = 0;
     139             :     }
     140      118808 :     if (throttle == 0)
     141      118808 :         return;
     142             : 
     143           0 :     MOZ_ASSERT(isNative());
     144             : 
     145           0 :     Shape* shape = lastProperty();
     146           0 :     Shape* prev = nullptr;
     147             : 
     148           0 :     AutoCheckCannotGC nogc;
     149           0 :     if (inDictionaryMode()) {
     150           0 :         if (ShapeTable* table = shape->maybeTable(nogc)) {
     151           0 :             for (uint32_t fslot = table->freeList();
     152           0 :                  fslot != SHAPE_INVALID_SLOT;
     153           0 :                  fslot = getSlot(fslot).toPrivateUint32())
     154             :             {
     155           0 :                 MOZ_ASSERT(fslot < slotSpan());
     156             :             }
     157             : 
     158           0 :             for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
     159           0 :                 MOZ_ASSERT_IF(lastProperty() != shape, !shape->hasTable());
     160             : 
     161           0 :                 ShapeTable::Entry& entry = table->search<MaybeAdding::NotAdding>(shape->propid(),
     162           0 :                                                                                  nogc);
     163           0 :                 MOZ_ASSERT(entry.shape() == shape);
     164             :             }
     165             :         }
     166             : 
     167           0 :         shape = lastProperty();
     168           0 :         for (int n = throttle; --n >= 0 && shape; shape = shape->parent) {
     169           0 :             MOZ_ASSERT_IF(shape->slot() != SHAPE_INVALID_SLOT, shape->slot() < slotSpan());
     170           0 :             if (!prev) {
     171           0 :                 MOZ_ASSERT(lastProperty() == shape);
     172           0 :                 MOZ_ASSERT(shape->listp == &shape_);
     173             :             } else {
     174           0 :                 MOZ_ASSERT(shape->listp == &prev->parent);
     175             :             }
     176           0 :             prev = shape;
     177             :         }
     178             :     } else {
     179           0 :         for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
     180           0 :             if (ShapeTable* table = shape->maybeTable(nogc)) {
     181           0 :                 MOZ_ASSERT(shape->parent);
     182           0 :                 for (Shape::Range<NoGC> r(shape); !r.empty(); r.popFront()) {
     183             :                     ShapeTable::Entry& entry =
     184           0 :                         table->search<MaybeAdding::NotAdding>(r.front().propid(), nogc);
     185           0 :                     MOZ_ASSERT(entry.shape() == &r.front());
     186             :                 }
     187             :             }
     188           0 :             if (prev) {
     189           0 :                 MOZ_ASSERT(prev->maybeSlot() >= shape->maybeSlot());
     190           0 :                 shape->kids.checkConsistency(prev);
     191             :             }
     192           0 :             prev = shape;
     193             :         }
     194             :     }
     195             : }
     196             : #endif
     197             : 
     198             : void
     199       19741 : js::NativeObject::initializeSlotRange(uint32_t start, uint32_t length)
     200             : {
     201             :     /*
     202             :      * No bounds check, as this is used when the object's shape does not
     203             :      * reflect its allocated slots (updateSlotsForSpan).
     204             :      */
     205             :     HeapSlot* fixedStart;
     206             :     HeapSlot* fixedEnd;
     207             :     HeapSlot* slotsStart;
     208             :     HeapSlot* slotsEnd;
     209       19741 :     getSlotRangeUnchecked(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
     210             : 
     211       19741 :     uint32_t offset = start;
     212      103626 :     for (HeapSlot* sp = fixedStart; sp < fixedEnd; sp++)
     213       83885 :         sp->init(this, HeapSlot::Slot, offset++, UndefinedValue());
     214       64107 :     for (HeapSlot* sp = slotsStart; sp < slotsEnd; sp++)
     215       44366 :         sp->init(this, HeapSlot::Slot, offset++, UndefinedValue());
     216       19741 : }
     217             : 
     218             : void
     219           0 : js::NativeObject::initSlotRange(uint32_t start, const Value* vector, uint32_t length)
     220             : {
     221             :     HeapSlot* fixedStart;
     222             :     HeapSlot* fixedEnd;
     223             :     HeapSlot* slotsStart;
     224             :     HeapSlot* slotsEnd;
     225           0 :     getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
     226           0 :     for (HeapSlot* sp = fixedStart; sp < fixedEnd; sp++)
     227           0 :         sp->init(this, HeapSlot::Slot, start++, *vector++);
     228           0 :     for (HeapSlot* sp = slotsStart; sp < slotsEnd; sp++)
     229           0 :         sp->init(this, HeapSlot::Slot, start++, *vector++);
     230           0 : }
     231             : 
     232             : void
     233           0 : js::NativeObject::copySlotRange(uint32_t start, const Value* vector, uint32_t length)
     234             : {
     235             :     HeapSlot* fixedStart;
     236             :     HeapSlot* fixedEnd;
     237             :     HeapSlot* slotsStart;
     238             :     HeapSlot* slotsEnd;
     239           0 :     getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
     240           0 :     for (HeapSlot* sp = fixedStart; sp < fixedEnd; sp++)
     241           0 :         sp->set(this, HeapSlot::Slot, start++, *vector++);
     242           0 :     for (HeapSlot* sp = slotsStart; sp < slotsEnd; sp++)
     243           0 :         sp->set(this, HeapSlot::Slot, start++, *vector++);
     244           0 : }
     245             : 
     246             : #ifdef DEBUG
     247             : bool
     248     2371893 : js::NativeObject::slotInRange(uint32_t slot, SentinelAllowed sentinel) const
     249             : {
     250     2371893 :     uint32_t capacity = numFixedSlots() + numDynamicSlots();
     251     2371891 :     if (sentinel == SENTINEL_ALLOWED)
     252      411828 :         return slot <= capacity;
     253     1960063 :     return slot < capacity;
     254             : }
     255             : #endif /* DEBUG */
     256             : 
     257             : Shape*
     258      103596 : js::NativeObject::lookup(JSContext* cx, jsid id)
     259             : {
     260      103596 :     MOZ_ASSERT(isNative());
     261      103596 :     return Shape::search(cx, lastProperty(), id);
     262             : }
     263             : 
     264             : Shape*
     265      200285 : js::NativeObject::lookupPure(jsid id)
     266             : {
     267      200285 :     MOZ_ASSERT(isNative());
     268      200285 :     return Shape::searchNoHashify(lastProperty(), id);
     269             : }
     270             : 
     271             : uint32_t
     272          32 : js::NativeObject::numFixedSlotsForCompilation() const
     273             : {
     274             :     // This is an alternative method for getting the number of fixed slots in an
     275             :     // object. It requires more logic and memory accesses than numFixedSlots()
     276             :     // but is safe to be called from the compilation thread, even if the active
     277             :     // thread is mutating the VM.
     278             : 
     279             :     // The compiler does not have access to nursery things.
     280          32 :     MOZ_ASSERT(!IsInsideNursery(this));
     281             : 
     282          32 :     if (this->is<ArrayObject>())
     283           0 :         return 0;
     284             : 
     285          32 :     gc::AllocKind kind = asTenured().getAllocKind();
     286          32 :     return gc::GetGCKindSlots(kind, getClass());
     287             : }
     288             : 
     289             : void
     290          20 : NativeObject::setLastPropertyShrinkFixedSlots(Shape* shape)
     291             : {
     292          20 :     MOZ_ASSERT(!inDictionaryMode());
     293          20 :     MOZ_ASSERT(!shape->inDictionary());
     294          20 :     MOZ_ASSERT(shape->zone() == zone());
     295          20 :     MOZ_ASSERT(lastProperty()->slotSpan() == shape->slotSpan());
     296          20 :     MOZ_ASSERT(shape->getObjectClass() == getClass());
     297             : 
     298          40 :     DebugOnly<size_t> oldFixed = numFixedSlots();
     299          40 :     DebugOnly<size_t> newFixed = shape->numFixedSlots();
     300          20 :     MOZ_ASSERT(newFixed < oldFixed);
     301          20 :     MOZ_ASSERT(shape->slotSpan() <= oldFixed);
     302          20 :     MOZ_ASSERT(shape->slotSpan() <= newFixed);
     303          20 :     MOZ_ASSERT(dynamicSlotsCount(oldFixed, shape->slotSpan(), getClass()) == 0);
     304          20 :     MOZ_ASSERT(dynamicSlotsCount(newFixed, shape->slotSpan(), getClass()) == 0);
     305             : 
     306          20 :     shape_ = shape;
     307          20 : }
     308             : 
     309             : void
     310         380 : NativeObject::setLastPropertyMakeNonNative(Shape* shape)
     311             : {
     312         380 :     MOZ_ASSERT(!inDictionaryMode());
     313         380 :     MOZ_ASSERT(!shape->getObjectClass()->isNative());
     314         380 :     MOZ_ASSERT(shape->zone() == zone());
     315         380 :     MOZ_ASSERT(shape->slotSpan() == 0);
     316         380 :     MOZ_ASSERT(shape->numFixedSlots() == 0);
     317             : 
     318         380 :     if (hasDynamicElements())
     319           0 :         js_free(getUnshiftedElementsHeader());
     320         380 :     if (hasDynamicSlots()) {
     321           0 :         js_free(slots_);
     322           0 :         slots_ = nullptr;
     323             :     }
     324             : 
     325         380 :     shape_ = shape;
     326         380 : }
     327             : 
     328             : void
     329           4 : NativeObject::setLastPropertyMakeNative(JSContext* cx, Shape* shape)
     330             : {
     331           4 :     MOZ_ASSERT(getClass()->isNative());
     332           4 :     MOZ_ASSERT(shape->getObjectClass()->isNative());
     333           4 :     MOZ_ASSERT(!shape->inDictionary());
     334             : 
     335             :     // This method is used to convert unboxed objects into native objects. In
     336             :     // this case, the shape_ field was previously used to store other data and
     337             :     // this should be treated as an initialization.
     338           4 :     shape_.init(shape);
     339             : 
     340           4 :     slots_ = nullptr;
     341           4 :     elements_ = emptyObjectElements;
     342             : 
     343           4 :     size_t oldSpan = shape->numFixedSlots();
     344           4 :     size_t newSpan = shape->slotSpan();
     345             : 
     346           4 :     initializeSlotRange(0, oldSpan);
     347             : 
     348             :     // A failure at this point will leave the object as a mutant, and we
     349             :     // can't recover.
     350           8 :     AutoEnterOOMUnsafeRegion oomUnsafe;
     351           4 :     if (oldSpan != newSpan && !updateSlotsForSpan(cx, oldSpan, newSpan))
     352           0 :         oomUnsafe.crash("NativeObject::setLastPropertyMakeNative");
     353           4 : }
     354             : 
     355             : bool
     356        1935 : NativeObject::setSlotSpan(JSContext* cx, uint32_t span)
     357             : {
     358        1935 :     MOZ_ASSERT(inDictionaryMode());
     359             : 
     360        1935 :     size_t oldSpan = lastProperty()->base()->slotSpan();
     361        1935 :     if (oldSpan == span)
     362           0 :         return true;
     363             : 
     364        1935 :     if (!updateSlotsForSpan(cx, oldSpan, span))
     365           0 :         return false;
     366             : 
     367        1935 :     lastProperty()->base()->setSlotSpan(span);
     368        1935 :     return true;
     369             : }
     370             : 
     371             : bool
     372       15965 : NativeObject::growSlots(JSContext* cx, uint32_t oldCount, uint32_t newCount)
     373             : {
     374       15965 :     MOZ_ASSERT(newCount > oldCount);
     375       15965 :     MOZ_ASSERT_IF(!is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
     376             : 
     377             :     /*
     378             :      * Slot capacities are determined by the span of allocated objects. Due to
     379             :      * the limited number of bits to store shape slots, object growth is
     380             :      * throttled well before the slot capacity can overflow.
     381             :      */
     382       15965 :     NativeObject::slotsSizeMustNotOverflow();
     383       15965 :     MOZ_ASSERT(newCount <= MAX_SLOTS_COUNT);
     384             : 
     385       15965 :     if (!oldCount) {
     386        9987 :         MOZ_ASSERT(!slots_);
     387        9987 :         slots_ = AllocateObjectBuffer<HeapSlot>(cx, this, newCount);
     388        9987 :         if (!slots_)
     389           0 :             return false;
     390        9987 :         Debug_SetSlotRangeToCrashOnTouch(slots_, newCount);
     391        9987 :         return true;
     392             :     }
     393             : 
     394        5978 :     HeapSlot* newslots = ReallocateObjectBuffer<HeapSlot>(cx, this, slots_, oldCount, newCount);
     395        5978 :     if (!newslots)
     396           0 :         return false;  /* Leave slots at its old size. */
     397             : 
     398        5978 :     slots_ = newslots;
     399             : 
     400        5978 :     Debug_SetSlotRangeToCrashOnTouch(slots_ + oldCount, newCount - oldCount);
     401             : 
     402        5978 :     return true;
     403             : }
     404             : 
     405             : /* static */ bool
     406         116 : NativeObject::growSlotsDontReportOOM(JSContext* cx, NativeObject* obj, uint32_t newCount)
     407             : {
     408             :     // IC code calls this directly.
     409         232 :     AutoCheckCannotGC nogc;
     410             : 
     411         116 :     if (!obj->growSlots(cx, obj->numDynamicSlots(), newCount)) {
     412           0 :         cx->recoverFromOutOfMemory();
     413           0 :         return false;
     414             :     }
     415         116 :     return true;
     416             : }
     417             : 
     418             : /* static */ bool
     419          62 : NativeObject::addDenseElementDontReportOOM(JSContext* cx, NativeObject* obj)
     420             : {
     421             :     // IC code calls this directly.
     422         124 :     AutoCheckCannotGC nogc;
     423             : 
     424          62 :     MOZ_ASSERT(obj->getDenseInitializedLength() == obj->getDenseCapacity());
     425          62 :     MOZ_ASSERT(!obj->denseElementsAreCopyOnWrite());
     426          62 :     MOZ_ASSERT(!obj->denseElementsAreFrozen());
     427          62 :     MOZ_ASSERT(!obj->isIndexed());
     428          62 :     MOZ_ASSERT(!obj->is<TypedArrayObject>());
     429          62 :     MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().lengthIsWritable());
     430             : 
     431             :     // growElements will report OOM also if the number of dense elements will
     432             :     // exceed MAX_DENSE_ELEMENTS_COUNT. See goodElementsAllocationAmount.
     433          62 :     uint32_t oldCapacity = obj->getDenseCapacity();
     434          62 :     if (MOZ_UNLIKELY(!obj->growElements(cx, oldCapacity + 1))) {
     435           0 :         cx->recoverFromOutOfMemory();
     436           0 :         return false;
     437             :     }
     438             : 
     439          62 :     MOZ_ASSERT(obj->getDenseCapacity() > oldCapacity);
     440          62 :     MOZ_ASSERT(obj->getDenseCapacity() <= MAX_DENSE_ELEMENTS_COUNT);
     441          62 :     return true;
     442             : }
     443             : 
     444             : static void
     445           0 : FreeSlots(JSContext* cx, HeapSlot* slots)
     446             : {
     447           0 :     if (cx->helperThread())
     448           0 :         js_free(slots);
     449             :     else
     450           0 :         cx->nursery().freeBuffer(slots);
     451           0 : }
     452             : 
     453             : void
     454           0 : NativeObject::shrinkSlots(JSContext* cx, uint32_t oldCount, uint32_t newCount)
     455             : {
     456           0 :     MOZ_ASSERT(newCount < oldCount);
     457             : 
     458           0 :     if (newCount == 0) {
     459           0 :         FreeSlots(cx, slots_);
     460           0 :         slots_ = nullptr;
     461           0 :         return;
     462             :     }
     463             : 
     464           0 :     MOZ_ASSERT_IF(!is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
     465             : 
     466           0 :     HeapSlot* newslots = ReallocateObjectBuffer<HeapSlot>(cx, this, slots_, oldCount, newCount);
     467           0 :     if (!newslots) {
     468           0 :         cx->recoverFromOutOfMemory();
     469           0 :         return;  /* Leave slots at its old size. */
     470             :     }
     471             : 
     472           0 :     slots_ = newslots;
     473             : }
     474             : 
     475             : /* static */ bool
     476           0 : NativeObject::sparsifyDenseElement(JSContext* cx, HandleNativeObject obj, uint32_t index)
     477             : {
     478           0 :     if (!obj->maybeCopyElementsForWrite(cx))
     479           0 :         return false;
     480             : 
     481           0 :     RootedValue value(cx, obj->getDenseElement(index));
     482           0 :     MOZ_ASSERT(!value.isMagic(JS_ELEMENTS_HOLE));
     483             : 
     484           0 :     removeDenseElementForSparseIndex(cx, obj, index);
     485             : 
     486           0 :     uint32_t slot = obj->slotSpan();
     487             : 
     488           0 :     RootedId id(cx, INT_TO_JSID(index));
     489             : 
     490           0 :     AutoKeepShapeTables keep(cx);
     491           0 :     ShapeTable::Entry* entry = nullptr;
     492           0 :     if (obj->inDictionaryMode()) {
     493           0 :         ShapeTable* table = obj->lastProperty()->ensureTableForDictionary(cx, keep);
     494           0 :         if (!table)
     495           0 :             return false;
     496           0 :         entry = &table->search<MaybeAdding::Adding>(id, keep);
     497             :     }
     498             : 
     499             :     // NOTE: We don't use addDataProperty because we don't want the
     500             :     // extensibility check if we're, for example, sparsifying frozen objects..
     501           0 :     if (!addPropertyInternal(cx, obj, id, nullptr, nullptr, slot,
     502           0 :                              obj->getElementsHeader()->elementAttributes(),
     503             :                              0, entry, true, keep)) {
     504           0 :         obj->setDenseElementUnchecked(index, value);
     505           0 :         return false;
     506             :     }
     507             : 
     508           0 :     MOZ_ASSERT(slot == obj->slotSpan() - 1);
     509           0 :     obj->initSlot(slot, value);
     510             : 
     511           0 :     return true;
     512             : }
     513             : 
     514             : /* static */ bool
     515         316 : NativeObject::sparsifyDenseElements(JSContext* cx, HandleNativeObject obj)
     516             : {
     517         316 :     if (!obj->maybeCopyElementsForWrite(cx))
     518           0 :         return false;
     519             : 
     520         316 :     uint32_t initialized = obj->getDenseInitializedLength();
     521             : 
     522             :     /* Create new properties with the value of non-hole dense elements. */
     523         316 :     for (uint32_t i = 0; i < initialized; i++) {
     524           0 :         if (obj->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE))
     525           0 :             continue;
     526             : 
     527           0 :         if (!sparsifyDenseElement(cx, obj, i))
     528           0 :             return false;
     529             :     }
     530             : 
     531         316 :     if (initialized)
     532           0 :         obj->setDenseInitializedLengthUnchecked(0);
     533             : 
     534             :     // Reduce storage for dense elements which are now holes. Explicitly mark
     535             :     // the elements capacity as zero, so that any attempts to add dense
     536             :     // elements will be caught in ensureDenseElements.
     537             : 
     538         316 :     if (obj->getElementsHeader()->numShiftedElements() > 0)
     539           0 :         obj->moveShiftedElements();
     540             : 
     541         316 :     if (obj->getDenseCapacity()) {
     542           0 :         obj->shrinkElements(cx, 0);
     543           0 :         obj->getElementsHeader()->capacity = 0;
     544             :     }
     545             : 
     546         316 :     return true;
     547             : }
     548             : 
     549             : bool
     550           1 : NativeObject::willBeSparseElements(uint32_t requiredCapacity, uint32_t newElementsHint)
     551             : {
     552           1 :     MOZ_ASSERT(isNative());
     553           1 :     MOZ_ASSERT(requiredCapacity > MIN_SPARSE_INDEX);
     554             : 
     555           1 :     uint32_t cap = getDenseCapacity();
     556           1 :     MOZ_ASSERT(requiredCapacity >= cap);
     557             : 
     558           1 :     if (requiredCapacity > MAX_DENSE_ELEMENTS_COUNT)
     559           0 :         return true;
     560             : 
     561           1 :     uint32_t minimalDenseCount = requiredCapacity / SPARSE_DENSITY_RATIO;
     562           1 :     if (newElementsHint >= minimalDenseCount)
     563           0 :         return false;
     564           1 :     minimalDenseCount -= newElementsHint;
     565             : 
     566           1 :     if (minimalDenseCount > cap)
     567           0 :         return true;
     568             : 
     569           1 :     uint32_t len = getDenseInitializedLength();
     570           1 :     const Value* elems = getDenseElements();
     571        1001 :     for (uint32_t i = 0; i < len; i++) {
     572        1000 :         if (!elems[i].isMagic(JS_ELEMENTS_HOLE) && !--minimalDenseCount)
     573           0 :             return false;
     574             :     }
     575           1 :     return true;
     576             : }
     577             : 
     578             : /* static */ DenseElementResult
     579          98 : NativeObject::maybeDensifySparseElements(JSContext* cx, HandleNativeObject obj)
     580             : {
     581             :     /*
     582             :      * Wait until after the object goes into dictionary mode, which must happen
     583             :      * when sparsely packing any array with more than MIN_SPARSE_INDEX elements
     584             :      * (see PropertyTree::MAX_HEIGHT).
     585             :      */
     586          98 :     if (!obj->inDictionaryMode())
     587          98 :         return DenseElementResult::Incomplete;
     588             : 
     589             :     /*
     590             :      * Only measure the number of indexed properties every log(n) times when
     591             :      * populating the object.
     592             :      */
     593           0 :     uint32_t slotSpan = obj->slotSpan();
     594           0 :     if (slotSpan != RoundUpPow2(slotSpan))
     595           0 :         return DenseElementResult::Incomplete;
     596             : 
     597             :     /* Watch for conditions under which an object's elements cannot be dense. */
     598           0 :     if (!obj->nonProxyIsExtensible() || obj->watched())
     599           0 :         return DenseElementResult::Incomplete;
     600             : 
     601             :     /*
     602             :      * The indexes in the object need to be sufficiently dense before they can
     603             :      * be converted to dense mode.
     604             :      */
     605           0 :     uint32_t numDenseElements = 0;
     606           0 :     uint32_t newInitializedLength = 0;
     607             : 
     608           0 :     RootedShape shape(cx, obj->lastProperty());
     609           0 :     while (!shape->isEmptyShape()) {
     610             :         uint32_t index;
     611           0 :         if (IdIsIndex(shape->propid(), &index)) {
     612           0 :             if (shape->attributes() == JSPROP_ENUMERATE &&
     613           0 :                 shape->hasDefaultGetter() &&
     614           0 :                 shape->hasDefaultSetter())
     615             :             {
     616           0 :                 numDenseElements++;
     617           0 :                 newInitializedLength = Max(newInitializedLength, index + 1);
     618             :             } else {
     619             :                 /*
     620             :                  * For simplicity, only densify the object if all indexed
     621             :                  * properties can be converted to dense elements.
     622             :                  */
     623           0 :                 return DenseElementResult::Incomplete;
     624             :             }
     625             :         }
     626           0 :         shape = shape->previous();
     627             :     }
     628             : 
     629           0 :     if (numDenseElements * SPARSE_DENSITY_RATIO < newInitializedLength)
     630           0 :         return DenseElementResult::Incomplete;
     631             : 
     632           0 :     if (newInitializedLength > MAX_DENSE_ELEMENTS_COUNT)
     633           0 :         return DenseElementResult::Incomplete;
     634             : 
     635             :     /*
     636             :      * This object meets all necessary restrictions, convert all indexed
     637             :      * properties into dense elements.
     638             :      */
     639             : 
     640           0 :     if (!obj->maybeCopyElementsForWrite(cx))
     641           0 :         return DenseElementResult::Failure;
     642             : 
     643           0 :     if (newInitializedLength > obj->getDenseCapacity()) {
     644           0 :         if (!obj->growElements(cx, newInitializedLength))
     645           0 :             return DenseElementResult::Failure;
     646             :     }
     647             : 
     648           0 :     obj->ensureDenseInitializedLength(cx, newInitializedLength, 0);
     649             : 
     650           0 :     RootedValue value(cx);
     651             : 
     652           0 :     shape = obj->lastProperty();
     653           0 :     while (!shape->isEmptyShape()) {
     654           0 :         jsid id = shape->propid();
     655             :         uint32_t index;
     656           0 :         if (IdIsIndex(id, &index)) {
     657           0 :             value = obj->getSlot(shape->slot());
     658             : 
     659             :             /*
     660             :              * When removing a property from a dictionary, the specified
     661             :              * property will be removed from the dictionary list and the
     662             :              * last property will then be changed due to reshaping the object.
     663             :              * Compute the next shape in the traverse, watching for such
     664             :              * removals from the list.
     665             :              */
     666           0 :             if (shape != obj->lastProperty()) {
     667           0 :                 shape = shape->previous();
     668           0 :                 if (!NativeObject::removeProperty(cx, obj, id))
     669           0 :                     return DenseElementResult::Failure;
     670             :             } else {
     671           0 :                 if (!NativeObject::removeProperty(cx, obj, id))
     672           0 :                     return DenseElementResult::Failure;
     673           0 :                 shape = obj->lastProperty();
     674             :             }
     675             : 
     676           0 :             obj->setDenseElement(index, value);
     677             :         } else {
     678           0 :             shape = shape->previous();
     679             :         }
     680             :     }
     681             : 
     682             :     /*
     683             :      * All indexed properties on the object are now dense, clear the indexed
     684             :      * flag so that we will not start using sparse indexes again if we need
     685             :      * to grow the object.
     686             :      */
     687           0 :     if (!NativeObject::clearFlag(cx, obj, BaseShape::INDEXED))
     688           0 :         return DenseElementResult::Failure;
     689             : 
     690           0 :     return DenseElementResult::Success;
     691             : }
     692             : 
     693             : void
     694           0 : NativeObject::moveShiftedElements()
     695             : {
     696           0 :     ObjectElements* header = getElementsHeader();
     697           0 :     uint32_t numShifted = header->numShiftedElements();
     698           0 :     MOZ_ASSERT(numShifted > 0);
     699             : 
     700           0 :     uint32_t initLength = header->initializedLength;
     701             : 
     702           0 :     ObjectElements* newHeader = static_cast<ObjectElements*>(getUnshiftedElementsHeader());
     703           0 :     memmove(newHeader, header, sizeof(ObjectElements));
     704             : 
     705           0 :     newHeader->clearShiftedElements();
     706           0 :     newHeader->capacity += numShifted;
     707           0 :     elements_ = newHeader->elements();
     708             : 
     709             :     // To move the elements, temporarily update initializedLength to include
     710             :     // the shifted elements.
     711           0 :     newHeader->initializedLength += numShifted;
     712             : 
     713             :     // Move the elements. Initialize to |undefined| to ensure pre-barriers
     714             :     // don't see garbage.
     715           0 :     for (size_t i = 0; i < numShifted; i++)
     716           0 :         initDenseElement(i, UndefinedValue());
     717           0 :     moveDenseElements(0, numShifted, initLength);
     718             : 
     719             :     // Restore the initialized length. We use setDenseInitializedLength to
     720             :     // make sure prepareElementRangeForOverwrite is called on the shifted
     721             :     // elements.
     722           0 :     setDenseInitializedLength(initLength);
     723           0 : }
     724             : 
     725             : void
     726           4 : NativeObject::maybeMoveShiftedElements()
     727             : {
     728           4 :     ObjectElements* header = getElementsHeader();
     729           4 :     MOZ_ASSERT(header->numShiftedElements() > 0);
     730             : 
     731             :     // Move the elements if less than a third of the allocated space is in use.
     732           4 :     if (header->capacity < header->numAllocatedElements() / 3)
     733           0 :         moveShiftedElements();
     734           4 : }
     735             : 
     736             : bool
     737          14 : NativeObject::tryUnshiftDenseElements(uint32_t count)
     738             : {
     739          14 :     MOZ_ASSERT(count > 0);
     740             : 
     741          14 :     ObjectElements* header = getElementsHeader();
     742          14 :     uint32_t numShifted = header->numShiftedElements();
     743             : 
     744          14 :     if (count > numShifted) {
     745             :         // We need more elements than are easily available. Try to make space
     746             :         // for more elements than we need (and shift the remaining ones) so
     747             :         // that unshifting more elements later will be fast.
     748             : 
     749             :         // Don't bother reserving elements if the number of elements is small.
     750             :         // Note that there's no technical reason for using this particular
     751             :         // limit.
     752          28 :         if (header->initializedLength <= 10 ||
     753           0 :             header->isCopyOnWrite() ||
     754           0 :             header->isFrozen() ||
     755          14 :             header->hasNonwritableArrayLength() ||
     756           0 :             MOZ_UNLIKELY(count > ObjectElements::MaxShiftedElements))
     757             :         {
     758          14 :             return false;
     759             :         }
     760             : 
     761           0 :         MOZ_ASSERT(header->capacity >= header->initializedLength);
     762           0 :         uint32_t unusedCapacity = header->capacity - header->initializedLength;
     763             : 
     764             :         // Determine toShift, the number of extra elements we want to make
     765             :         // available.
     766           0 :         uint32_t toShift = count - numShifted;
     767           0 :         MOZ_ASSERT(toShift <= ObjectElements::MaxShiftedElements,
     768             :                    "count <= MaxShiftedElements so toShift <= MaxShiftedElements");
     769             : 
     770             :         // Give up if we need to allocate more elements.
     771           0 :         if (toShift > unusedCapacity)
     772           0 :             return false;
     773             : 
     774             :         // Move more elements than we need, so that other unshift calls will be
     775             :         // fast. We just have to make sure we don't exceed unusedCapacity.
     776           0 :         toShift = Min(toShift + unusedCapacity / 2, unusedCapacity);
     777             : 
     778             :         // Ensure |numShifted + toShift| does not exceed MaxShiftedElements.
     779           0 :         if (numShifted + toShift > ObjectElements::MaxShiftedElements)
     780           0 :             toShift = ObjectElements::MaxShiftedElements - numShifted;
     781             : 
     782           0 :         MOZ_ASSERT(count <= numShifted + toShift);
     783           0 :         MOZ_ASSERT(numShifted + toShift <= ObjectElements::MaxShiftedElements);
     784           0 :         MOZ_ASSERT(toShift <= unusedCapacity);
     785             : 
     786             :         // Now move/unshift the elements.
     787           0 :         uint32_t initLen = header->initializedLength;
     788           0 :         setDenseInitializedLength(initLen + toShift);
     789           0 :         for (uint32_t i = 0; i < toShift; i++)
     790           0 :             initDenseElement(initLen + i, UndefinedValue());
     791           0 :         moveDenseElements(toShift, 0, initLen);
     792             : 
     793             :         // Shift the elements we just prepended.
     794           0 :         shiftDenseElementsUnchecked(toShift);
     795             : 
     796             :         // We can now fall-through to the fast path below.
     797           0 :         header = getElementsHeader();
     798           0 :         MOZ_ASSERT(header->numShiftedElements() == numShifted + toShift);
     799             : 
     800           0 :         numShifted = header->numShiftedElements();
     801           0 :         MOZ_ASSERT(count <= numShifted);
     802             :     }
     803             : 
     804           0 :     elements_ -= count;
     805           0 :     ObjectElements* newHeader = getElementsHeader();
     806           0 :     memmove(newHeader, header, sizeof(ObjectElements));
     807             : 
     808           0 :     newHeader->unshiftShiftedElements(count);
     809             : 
     810             :     // Initialize to |undefined| to ensure pre-barriers don't see garbage.
     811           0 :     for (uint32_t i = 0; i < count; i++)
     812           0 :         initDenseElement(i, UndefinedValue());
     813             : 
     814           0 :     return true;
     815             : }
     816             : 
     817             : // Given a requested capacity (in elements) and (potentially) the length of an
     818             : // array for which elements are being allocated, compute an actual allocation
     819             : // amount (in elements).  (Allocation amounts include space for an
     820             : // ObjectElements instance, so a return value of |N| implies
     821             : // |N - ObjectElements::VALUES_PER_HEADER| usable elements.)
     822             : //
     823             : // The requested/actual allocation distinction is meant to:
     824             : //
     825             : //   * preserve amortized O(N) time to add N elements;
     826             : //   * minimize the number of unused elements beyond an array's length, and
     827             : //   * provide at least SLOT_CAPACITY_MIN elements no matter what (so adding
     828             : //     the first several elements to small arrays only needs one allocation).
     829             : //
     830             : // Note: the structure and behavior of this method follow along with
     831             : // UnboxedArrayObject::chooseCapacityIndex. Changes to the allocation strategy
     832             : // in one should generally be matched by the other.
     833             : /* static */ bool
     834         656 : NativeObject::goodElementsAllocationAmount(JSContext* cx, uint32_t reqCapacity,
     835             :                                            uint32_t length, uint32_t* goodAmount)
     836             : {
     837         656 :     if (reqCapacity > MAX_DENSE_ELEMENTS_COUNT) {
     838           0 :         ReportOutOfMemory(cx);
     839           0 :         return false;
     840             :     }
     841             : 
     842         656 :     uint32_t reqAllocated = reqCapacity + ObjectElements::VALUES_PER_HEADER;
     843             : 
     844             :     // Handle "small" requests primarily by doubling.
     845         656 :     const uint32_t Mebi = 1 << 20;
     846         656 :     if (reqAllocated < Mebi) {
     847         656 :         uint32_t amount = mozilla::AssertedCast<uint32_t>(RoundUpPow2(reqAllocated));
     848             : 
     849             :         // If |amount| would be 2/3 or more of the array's length, adjust
     850             :         // it (up or down) to be equal to the array's length.  This avoids
     851             :         // allocating excess elements that aren't likely to be needed, either
     852             :         // in this resizing or a subsequent one.  The 2/3 factor is chosen so
     853             :         // that exceptional resizings will at most triple the capacity, as
     854             :         // opposed to the usual doubling.
     855         656 :         uint32_t goodCapacity = amount - ObjectElements::VALUES_PER_HEADER;
     856         656 :         if (length >= reqCapacity && goodCapacity > (length / 3) * 2)
     857          57 :             amount = length + ObjectElements::VALUES_PER_HEADER;
     858             : 
     859         656 :         if (amount < SLOT_CAPACITY_MIN)
     860          23 :             amount = SLOT_CAPACITY_MIN;
     861             : 
     862         656 :         *goodAmount = amount;
     863             : 
     864         656 :         return true;
     865             :     }
     866             : 
     867             :     // The almost-doubling above wastes a lot of space for larger bucket sizes.
     868             :     // For large amounts, switch to bucket sizes that obey this formula:
     869             :     //
     870             :     //   count(n+1) = Math.ceil(count(n) * 1.125)
     871             :     //
     872             :     // where |count(n)| is the size of the nth bucket, measured in 2**20 slots.
     873             :     // These bucket sizes still preserve amortized O(N) time to add N elements,
     874             :     // just with a larger constant factor.
     875             :     //
     876             :     // The bucket size table below was generated with this JavaScript (and
     877             :     // manual reformatting):
     878             :     //
     879             :     //   for (let n = 1, i = 0; i < 34; i++) {
     880             :     //     print('0x' + (n * (1 << 20)).toString(16) + ', ');
     881             :     //     n = Math.ceil(n * 1.125);
     882             :     //   }
     883             :     static const uint32_t BigBuckets[] = {
     884             :         0x100000, 0x200000, 0x300000, 0x400000, 0x500000, 0x600000, 0x700000,
     885             :         0x800000, 0x900000, 0xb00000, 0xd00000, 0xf00000, 0x1100000, 0x1400000,
     886             :         0x1700000, 0x1a00000, 0x1e00000, 0x2200000, 0x2700000, 0x2c00000,
     887             :         0x3200000, 0x3900000, 0x4100000, 0x4a00000, 0x5400000, 0x5f00000,
     888             :         0x6b00000, 0x7900000, 0x8900000, 0x9b00000, 0xaf00000, 0xc500000,
     889             :         0xde00000, 0xfa00000
     890             :     };
     891           0 :     MOZ_ASSERT(BigBuckets[ArrayLength(BigBuckets) - 1] <= MAX_DENSE_ELEMENTS_ALLOCATION);
     892             : 
     893             :     // Pick the first bucket that'll fit |reqAllocated|.
     894           0 :     for (uint32_t b : BigBuckets) {
     895           0 :         if (b >= reqAllocated) {
     896           0 :             *goodAmount = b;
     897           0 :             return true;
     898             :         }
     899             :     }
     900             : 
     901             :     // Otherwise, return the maximum bucket size.
     902           0 :     *goodAmount = MAX_DENSE_ELEMENTS_ALLOCATION;
     903           0 :     return true;
     904             : }
     905             : 
     906             : bool
     907         637 : NativeObject::growElements(JSContext* cx, uint32_t reqCapacity)
     908             : {
     909         637 :     MOZ_ASSERT(nonProxyIsExtensible());
     910         637 :     MOZ_ASSERT(canHaveNonEmptyElements());
     911         637 :     MOZ_ASSERT(!denseElementsAreFrozen());
     912         637 :     if (denseElementsAreCopyOnWrite())
     913           0 :         MOZ_CRASH();
     914             : 
     915             :     // If there are shifted elements, consider moving them first. If we don't
     916             :     // move them here, the code below will include the shifted elements in the
     917             :     // resize.
     918         637 :     uint32_t numShifted = getElementsHeader()->numShiftedElements();
     919         637 :     if (numShifted > 0) {
     920             :         // If the number of elements is small, it's cheaper to just move them as
     921             :         // it may avoid a malloc/realloc. Note that there's no technical reason
     922             :         // for using this particular value, but it works well in real-world use
     923             :         // cases.
     924             :         static const size_t MaxElementsToMoveEagerly = 20;
     925             : 
     926           1 :         if (getElementsHeader()->initializedLength <= MaxElementsToMoveEagerly)
     927           0 :             moveShiftedElements();
     928             :         else
     929           1 :             maybeMoveShiftedElements();
     930           1 :         if (getDenseCapacity() >= reqCapacity)
     931           0 :             return true;
     932           1 :         numShifted = getElementsHeader()->numShiftedElements();
     933             : 
     934             :         // If |reqCapacity + numShifted| overflows, we just move all shifted
     935             :         // elements to avoid the problem.
     936           1 :         CheckedInt<uint32_t> checkedReqCapacity(reqCapacity);
     937           1 :         checkedReqCapacity += numShifted;
     938           1 :         if (MOZ_UNLIKELY(!checkedReqCapacity.isValid())) {
     939           0 :             moveShiftedElements();
     940           0 :             numShifted = 0;
     941             :         }
     942             :     }
     943             : 
     944         637 :     uint32_t oldCapacity = getDenseCapacity();
     945         637 :     MOZ_ASSERT(oldCapacity < reqCapacity);
     946             : 
     947         637 :     uint32_t newAllocated = 0;
     948         637 :     if (is<ArrayObject>() && !as<ArrayObject>().lengthIsWritable()) {
     949           0 :         MOZ_ASSERT(reqCapacity <= as<ArrayObject>().length());
     950           0 :         MOZ_ASSERT(reqCapacity <= MAX_DENSE_ELEMENTS_COUNT);
     951             :         // Preserve the |capacity <= length| invariant for arrays with
     952             :         // non-writable length.  See also js::ArraySetLength which initially
     953             :         // enforces this requirement.
     954           0 :         newAllocated = reqCapacity + numShifted + ObjectElements::VALUES_PER_HEADER;
     955             :     } else {
     956         637 :         if (!goodElementsAllocationAmount(cx, reqCapacity + numShifted,
     957         637 :                                           getElementsHeader()->length,
     958             :                                           &newAllocated))
     959             :         {
     960           0 :             return false;
     961             :         }
     962             :     }
     963             : 
     964         637 :     uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER - numShifted;
     965         637 :     MOZ_ASSERT(newCapacity > oldCapacity && newCapacity >= reqCapacity);
     966             : 
     967             :     // If newCapacity exceeds MAX_DENSE_ELEMENTS_COUNT, the array should become
     968             :     // sparse.
     969         637 :     MOZ_ASSERT(newCapacity <= MAX_DENSE_ELEMENTS_COUNT);
     970             : 
     971         637 :     uint32_t initlen = getDenseInitializedLength();
     972             : 
     973         637 :     HeapSlot* oldHeaderSlots = reinterpret_cast<HeapSlot*>(getUnshiftedElementsHeader());
     974             :     HeapSlot* newHeaderSlots;
     975         637 :     if (hasDynamicElements()) {
     976          76 :         MOZ_ASSERT(oldCapacity <= MAX_DENSE_ELEMENTS_COUNT);
     977          76 :         uint32_t oldAllocated = oldCapacity + ObjectElements::VALUES_PER_HEADER + numShifted;
     978             : 
     979          76 :         newHeaderSlots = ReallocateObjectBuffer<HeapSlot>(cx, this, oldHeaderSlots, oldAllocated, newAllocated);
     980          76 :         if (!newHeaderSlots)
     981           0 :             return false;   // Leave elements at its old size.
     982             :     } else {
     983         561 :         newHeaderSlots = AllocateObjectBuffer<HeapSlot>(cx, this, newAllocated);
     984         561 :         if (!newHeaderSlots)
     985           0 :             return false;   // Leave elements at its old size.
     986         561 :         PodCopy(newHeaderSlots, oldHeaderSlots,
     987        1122 :                 ObjectElements::VALUES_PER_HEADER + initlen + numShifted);
     988             :     }
     989             : 
     990         637 :     ObjectElements* newheader = reinterpret_cast<ObjectElements*>(newHeaderSlots);
     991         637 :     elements_ = newheader->elements() + numShifted;
     992         637 :     getElementsHeader()->capacity = newCapacity;
     993             : 
     994         637 :     Debug_SetSlotRangeToCrashOnTouch(elements_ + initlen, newCapacity - initlen);
     995             : 
     996         637 :     return true;
     997             : }
     998             : 
     999             : void
    1000          18 : NativeObject::shrinkElements(JSContext* cx, uint32_t reqCapacity)
    1001             : {
    1002          18 :     MOZ_ASSERT(canHaveNonEmptyElements());
    1003          18 :     if (denseElementsAreCopyOnWrite())
    1004           0 :         MOZ_CRASH();
    1005             : 
    1006          18 :     if (!hasDynamicElements())
    1007          29 :         return;
    1008             : 
    1009             :     // If we have shifted elements, consider moving them.
    1010           7 :     uint32_t numShifted = getElementsHeader()->numShiftedElements();
    1011           7 :     if (numShifted > 0) {
    1012           3 :         maybeMoveShiftedElements();
    1013           3 :         numShifted = getElementsHeader()->numShiftedElements();
    1014             :     }
    1015             : 
    1016           7 :     uint32_t oldCapacity = getDenseCapacity();
    1017           7 :     MOZ_ASSERT(reqCapacity < oldCapacity);
    1018             : 
    1019           7 :     uint32_t newAllocated = 0;
    1020           7 :     MOZ_ALWAYS_TRUE(goodElementsAllocationAmount(cx, reqCapacity + numShifted, 0, &newAllocated));
    1021           7 :     MOZ_ASSERT(oldCapacity <= MAX_DENSE_ELEMENTS_COUNT);
    1022             : 
    1023           7 :     uint32_t oldAllocated = oldCapacity + ObjectElements::VALUES_PER_HEADER + numShifted;
    1024           7 :     if (newAllocated == oldAllocated)
    1025           7 :         return;  // Leave elements at its old size.
    1026             : 
    1027           0 :     MOZ_ASSERT(newAllocated > ObjectElements::VALUES_PER_HEADER);
    1028           0 :     uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER - numShifted;
    1029           0 :     MOZ_ASSERT(newCapacity <= MAX_DENSE_ELEMENTS_COUNT);
    1030             : 
    1031           0 :     HeapSlot* oldHeaderSlots = reinterpret_cast<HeapSlot*>(getUnshiftedElementsHeader());
    1032           0 :     HeapSlot* newHeaderSlots = ReallocateObjectBuffer<HeapSlot>(cx, this, oldHeaderSlots,
    1033           0 :                                                                 oldAllocated, newAllocated);
    1034           0 :     if (!newHeaderSlots) {
    1035           0 :         cx->recoverFromOutOfMemory();
    1036           0 :         return;  // Leave elements at its old size.
    1037             :     }
    1038             : 
    1039           0 :     ObjectElements* newheader = reinterpret_cast<ObjectElements*>(newHeaderSlots);
    1040           0 :     elements_ = newheader->elements() + numShifted;
    1041           0 :     getElementsHeader()->capacity = newCapacity;
    1042             : }
    1043             : 
    1044             : /* static */ bool
    1045          12 : NativeObject::CopyElementsForWrite(JSContext* cx, NativeObject* obj)
    1046             : {
    1047          12 :     MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());
    1048          12 :     MOZ_ASSERT(!obj->denseElementsAreFrozen());
    1049             : 
    1050             :     // The original owner of a COW elements array should never be modified.
    1051          12 :     MOZ_ASSERT(obj->getElementsHeader()->ownerObject() != obj);
    1052             : 
    1053          12 :     uint32_t initlen = obj->getDenseInitializedLength();
    1054          12 :     uint32_t newAllocated = 0;
    1055          12 :     if (!goodElementsAllocationAmount(cx, initlen, 0, &newAllocated))
    1056           0 :         return false;
    1057             : 
    1058          12 :     uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
    1059             : 
    1060             :     // COPY_ON_WRITE flags is set only if obj is a dense array.
    1061          12 :     MOZ_ASSERT(newCapacity <= MAX_DENSE_ELEMENTS_COUNT);
    1062             : 
    1063          12 :     JSObject::writeBarrierPre(obj->getElementsHeader()->ownerObject());
    1064             : 
    1065          12 :     HeapSlot* newHeaderSlots = AllocateObjectBuffer<HeapSlot>(cx, obj, newAllocated);
    1066          12 :     if (!newHeaderSlots)
    1067           0 :         return false;
    1068          12 :     ObjectElements* newheader = reinterpret_cast<ObjectElements*>(newHeaderSlots);
    1069          12 :     js_memcpy(newheader, obj->getElementsHeader(),
    1070          12 :               (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
    1071             : 
    1072          12 :     newheader->capacity = newCapacity;
    1073          12 :     newheader->clearCopyOnWrite();
    1074          12 :     obj->elements_ = newheader->elements();
    1075             : 
    1076          12 :     Debug_SetSlotRangeToCrashOnTouch(obj->elements_ + initlen, newCapacity - initlen);
    1077             : 
    1078          12 :     return true;
    1079             : }
    1080             : 
    1081             : /* static */ bool
    1082        1957 : NativeObject::allocDictionarySlot(JSContext* cx, HandleNativeObject obj, uint32_t* slotp)
    1083             : {
    1084        1957 :     MOZ_ASSERT(obj->inDictionaryMode());
    1085             : 
    1086        1957 :     uint32_t slot = obj->slotSpan();
    1087        1957 :     MOZ_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
    1088             : 
    1089             :     // Try to pull a free slot from the shape table's slot-number free list.
    1090             :     // Shapes without a ShapeTable have an empty free list, because we only
    1091             :     // purge ShapeTables with an empty free list.
    1092             :     {
    1093        3892 :         AutoCheckCannotGC nogc;
    1094        1957 :         if (ShapeTable* table = obj->lastProperty()->maybeTable(nogc)) {
    1095        1957 :             uint32_t last = table->freeList();
    1096        1957 :             if (last != SHAPE_INVALID_SLOT) {
    1097             : #ifdef DEBUG
    1098          22 :                 MOZ_ASSERT(last < slot);
    1099          22 :                 uint32_t next = obj->getSlot(last).toPrivateUint32();
    1100          22 :                 MOZ_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot);
    1101             : #endif
    1102             : 
    1103          22 :                 *slotp = last;
    1104             : 
    1105          22 :                 const Value& vref = obj->getSlot(last);
    1106          22 :                 table->setFreeList(vref.toPrivateUint32());
    1107          22 :                 obj->setSlot(last, UndefinedValue());
    1108          22 :                 return true;
    1109             :             }
    1110             :         }
    1111             :     }
    1112             : 
    1113        1935 :     if (slot >= SHAPE_MAXIMUM_SLOT) {
    1114           0 :         ReportOutOfMemory(cx);
    1115           0 :         return false;
    1116             :     }
    1117             : 
    1118        1935 :     *slotp = slot;
    1119             : 
    1120        1935 :     return obj->setSlotSpan(cx, slot + 1);
    1121             : }
    1122             : 
    1123             : void
    1124          41 : NativeObject::freeSlot(JSContext* cx, uint32_t slot)
    1125             : {
    1126          41 :     MOZ_ASSERT(slot < slotSpan());
    1127             : 
    1128          41 :     if (inDictionaryMode()) {
    1129             :         // Ensure we have a ShapeTable as it stores the object's free list (the
    1130             :         // list of available slots in dictionary objects).
    1131          32 :         AutoCheckCannotGC nogc;
    1132          32 :         if (ShapeTable* table = lastProperty()->ensureTableForDictionary(cx, nogc)) {
    1133          32 :             uint32_t last = table->freeList();
    1134             : 
    1135             :             // Can't afford to check the whole free list, but let's check the head.
    1136          32 :             MOZ_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan() && last != slot);
    1137             : 
    1138             :             // Place all freed slots other than reserved slots (bug 595230) on the
    1139             :             // dictionary's free list.
    1140          32 :             if (JSSLOT_FREE(getClass()) <= slot) {
    1141          32 :                 MOZ_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan());
    1142          32 :                 setSlot(slot, PrivateUint32Value(last));
    1143          32 :                 table->setFreeList(slot);
    1144          32 :                 return;
    1145             :             }
    1146             :         } else {
    1147             :             // OOM while creating the ShapeTable holding the free list. We can
    1148             :             // recover from it - it just means we won't be able to reuse this
    1149             :             // slot later.
    1150           0 :             cx->recoverFromOutOfMemory();
    1151             :         }
    1152             :     }
    1153           9 :     setSlot(slot, UndefinedValue());
    1154             : }
    1155             : 
    1156             : /* static */ Shape*
    1157           0 : NativeObject::addDataProperty(JSContext* cx, HandleNativeObject obj,
    1158             :                               jsid idArg, uint32_t slot, unsigned attrs)
    1159             : {
    1160           0 :     MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
    1161           0 :     RootedId id(cx, idArg);
    1162           0 :     return addProperty(cx, obj, id, nullptr, nullptr, slot, attrs, 0);
    1163             : }
    1164             : 
    1165             : /* static */ Shape*
    1166          74 : NativeObject::addDataProperty(JSContext* cx, HandleNativeObject obj,
    1167             :                               HandlePropertyName name, uint32_t slot, unsigned attrs)
    1168             : {
    1169          74 :     MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
    1170         148 :     RootedId id(cx, NameToId(name));
    1171         148 :     return addProperty(cx, obj, id, nullptr, nullptr, slot, attrs, 0);
    1172             : }
    1173             : 
    1174             : template <AllowGC allowGC>
    1175             : bool
    1176      132919 : js::NativeLookupOwnProperty(JSContext* cx,
    1177             :                             typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
    1178             :                             typename MaybeRooted<jsid, allowGC>::HandleType id,
    1179             :                             typename MaybeRooted<PropertyResult, allowGC>::MutableHandleType propp)
    1180             : {
    1181             :     bool done;
    1182      132919 :     return LookupOwnPropertyInline<allowGC>(cx, obj, id, propp, &done);
    1183             : }
    1184             : 
    1185             : template bool
    1186             : js::NativeLookupOwnProperty<CanGC>(JSContext* cx, HandleNativeObject obj, HandleId id,
    1187             :                                    MutableHandle<PropertyResult> propp);
    1188             : 
    1189             : template bool
    1190             : js::NativeLookupOwnProperty<NoGC>(JSContext* cx, NativeObject* const& obj, const jsid& id,
    1191             :                                   FakeMutableHandle<PropertyResult> propp);
    1192             : 
    1193             : /*** [[DefineOwnProperty]] ***********************************************************************/
    1194             : 
    1195             : static MOZ_ALWAYS_INLINE bool
    1196      126309 : CallAddPropertyHook(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value)
    1197             : {
    1198      126309 :     JSAddPropertyOp addProperty = obj->getClass()->getAddProperty();
    1199      126309 :     if (MOZ_UNLIKELY(addProperty)) {
    1200       13614 :         MOZ_ASSERT(!cx->helperThread());
    1201             : 
    1202       13614 :         if (!CallJSAddPropertyOp(cx, addProperty, obj, id, value)) {
    1203           0 :             NativeObject::removeProperty(cx, obj, id);
    1204           0 :             return false;
    1205             :         }
    1206             :     }
    1207      126309 :     return true;
    1208             : }
    1209             : 
    1210             : static MOZ_ALWAYS_INLINE bool
    1211        5057 : CallAddPropertyHookDense(JSContext* cx, HandleNativeObject obj, uint32_t index,
    1212             :                          HandleValue value)
    1213             : {
    1214             :     // Inline addProperty for array objects.
    1215        5057 :     if (obj->is<ArrayObject>()) {
    1216        4974 :         ArrayObject* arr = &obj->as<ArrayObject>();
    1217        4974 :         uint32_t length = arr->length();
    1218        4974 :         if (index >= length)
    1219         199 :             arr->setLength(cx, index + 1);
    1220        4974 :         return true;
    1221             :     }
    1222             : 
    1223          83 :     JSAddPropertyOp addProperty = obj->getClass()->getAddProperty();
    1224          83 :     if (MOZ_UNLIKELY(addProperty)) {
    1225           0 :         MOZ_ASSERT(!cx->helperThread());
    1226             : 
    1227           0 :         if (!obj->maybeCopyElementsForWrite(cx))
    1228           0 :             return false;
    1229             : 
    1230           0 :         RootedId id(cx, INT_TO_JSID(index));
    1231           0 :         if (!CallJSAddPropertyOp(cx, addProperty, obj, id, value)) {
    1232           0 :             obj->setDenseElementHole(cx, index);
    1233           0 :             return false;
    1234             :         }
    1235             :     }
    1236          83 :     return true;
    1237             : }
    1238             : 
    1239             : static MOZ_ALWAYS_INLINE void
    1240      127129 : UpdateShapeTypeAndValue(JSContext* cx, NativeObject* obj, Shape* shape, jsid id,
    1241             :                         const Value& value)
    1242             : {
    1243      127129 :     MOZ_ASSERT(id == shape->propid());
    1244             : 
    1245      127129 :     if (shape->hasSlot()) {
    1246      112178 :         obj->setSlotWithType(cx, shape, value, /* overwriting = */ false);
    1247             : 
    1248             :         // Per the acquired properties analysis, when the shape of a partially
    1249             :         // initialized object is changed to its fully initialized shape, its
    1250             :         // group can be updated as well.
    1251      112175 :         if (TypeNewScript* newScript = obj->groupRaw()->newScript()) {
    1252        1488 :             if (newScript->initializedShape() == shape)
    1253           1 :                 obj->setGroup(newScript->initializedGroup());
    1254             :         }
    1255             :     }
    1256      127127 :     if (!shape->hasSlot() || !shape->hasDefaultGetter() || !shape->hasDefaultSetter())
    1257       16331 :         MarkTypePropertyNonData(cx, obj, id);
    1258      127127 :     if (!shape->writable())
    1259       15179 :         MarkTypePropertyNonWritable(cx, obj, id);
    1260      127127 : }
    1261             : 
    1262             : void
    1263          76 : js::AddPropertyTypesAfterProtoChange(JSContext* cx, NativeObject* obj, ObjectGroup* oldGroup)
    1264             : {
    1265          76 :     MOZ_ASSERT(obj->group() != oldGroup);
    1266          76 :     MOZ_ASSERT(!obj->group()->unknownProperties());
    1267             : 
    1268             :     // First copy the dynamic flags.
    1269          76 :     MarkObjectGroupFlags(cx, obj, oldGroup->flags() & OBJECT_FLAG_DYNAMIC_MASK);
    1270             : 
    1271             :     // Now update all property types. If the object has many properties, this
    1272             :     // function may be slow so we mark all properties as unknown.
    1273             :     static const size_t MaxPropertyCount = 40;
    1274             : 
    1275          76 :     size_t nprops = obj->getDenseInitializedLength();
    1276          76 :     if (nprops > MaxPropertyCount) {
    1277           0 :         MarkObjectGroupUnknownProperties(cx, obj->group());
    1278           0 :         return;
    1279             :     }
    1280             : 
    1281             :     // Add dense element types.
    1282          76 :     for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
    1283           0 :         Value val = obj->getDenseElement(i);
    1284           0 :         if (!val.isMagic(JS_ELEMENTS_HOLE))
    1285           0 :             AddTypePropertyId(cx, obj, JSID_VOID, val);
    1286             :     }
    1287             : 
    1288             :     // Add property types.
    1289          80 :     for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront()) {
    1290           4 :         Shape* shape = &r.front();
    1291           4 :         jsid id = shape->propid();
    1292           4 :         if (JSID_IS_EMPTY(id))
    1293           0 :             continue;
    1294             : 
    1295           4 :         if (nprops++ > MaxPropertyCount) {
    1296           0 :             MarkObjectGroupUnknownProperties(cx, obj->group());
    1297           0 :             return;
    1298             :         }
    1299             : 
    1300           4 :         Value val = shape->hasSlot() ? obj->getSlot(shape->slot()) : UndefinedValue();
    1301           4 :         UpdateShapeTypeAndValue(cx, obj, shape, id, val);
    1302             :     }
    1303             : }
    1304             : 
    1305             : static bool
    1306       46713 : PurgeProtoChain(JSContext* cx, JSObject* objArg, HandleId id)
    1307             : {
    1308             :     /* Root locally so we can re-assign. */
    1309       93426 :     RootedObject obj(cx, objArg);
    1310             : 
    1311       93426 :     RootedShape shape(cx);
    1312      146659 :     while (obj) {
    1313             :         /* Lookups will not be cached through non-native protos. */
    1314       55213 :         if (!obj->isNative())
    1315        2303 :             break;
    1316             : 
    1317       52910 :         shape = obj->as<NativeObject>().lookup(cx, id);
    1318       52910 :         if (shape)
    1319        2937 :             return NativeObject::shadowingShapeChange(cx, obj.as<NativeObject>(), *shape);
    1320             : 
    1321       49973 :         obj = obj->staticPrototype();
    1322             :     }
    1323             : 
    1324       43776 :     return true;
    1325             : }
    1326             : 
    1327             : static bool
    1328       46713 : PurgeEnvironmentChainHelper(JSContext* cx, HandleObject objArg, HandleId id)
    1329             : {
    1330             :     /* Re-root locally so we can re-assign. */
    1331       93426 :     RootedObject obj(cx, objArg);
    1332             : 
    1333       46713 :     MOZ_ASSERT(obj->isNative());
    1334       46713 :     MOZ_ASSERT(obj->isDelegate());
    1335             : 
    1336             :     /* Lookups on integer ids cannot be cached through prototypes. */
    1337       46713 :     if (JSID_IS_INT(id))
    1338           0 :         return true;
    1339             : 
    1340       46713 :     if (!PurgeProtoChain(cx, obj->staticPrototype(), id))
    1341           0 :         return false;
    1342             : 
    1343             :     /*
    1344             :      * We must purge the environment chain only for Call objects as they are
    1345             :      * the only kind of cacheable non-global object that can gain properties
    1346             :      * after outer properties with the same names have been cached or
    1347             :      * traced. Call objects may gain such properties via eval introducing new
    1348             :      * vars; see bug 490364.
    1349             :      */
    1350       46713 :     if (obj->is<CallObject>()) {
    1351           0 :         while ((obj = obj->enclosingEnvironment()) != nullptr) {
    1352           0 :             if (!PurgeProtoChain(cx, obj, id))
    1353           0 :                 return false;
    1354             :         }
    1355             :     }
    1356             : 
    1357       46713 :     return true;
    1358             : }
    1359             : 
    1360             : /*
    1361             :  * PurgeEnvironmentChain does nothing if obj is not itself a prototype or
    1362             :  * parent environment, else it reshapes the scope and prototype chains it
    1363             :  * links. It calls PurgeEnvironmentChainHelper, which asserts that obj is
    1364             :  * flagged as a delegate (i.e., obj has ever been on a prototype or parent
    1365             :  * chain).
    1366             :  */
    1367             : static MOZ_ALWAYS_INLINE bool
    1368      131815 : PurgeEnvironmentChain(JSContext* cx, HandleObject obj, HandleId id)
    1369             : {
    1370      131815 :     if (obj->isDelegate() && obj->isNative())
    1371       46713 :         return PurgeEnvironmentChainHelper(cx, obj, id);
    1372       85102 :     return true;
    1373             : }
    1374             : 
    1375             : enum class IsAddOrChange { Add, AddOrChange };
    1376             : 
    1377             : template <IsAddOrChange AddOrChange>
    1378             : static MOZ_ALWAYS_INLINE bool
    1379      131367 : AddOrChangeProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
    1380             :                     Handle<PropertyDescriptor> desc)
    1381             : {
    1382      131367 :     desc.assertComplete();
    1383             : 
    1384      131367 :     if (!PurgeEnvironmentChain(cx, obj, id))
    1385           0 :         return false;
    1386             : 
    1387             :     // Use dense storage for new indexed properties where possible.
    1388      267885 :     if (JSID_IS_INT(id) &&
    1389       10242 :         !desc.getter() &&
    1390       10174 :         !desc.setter() &&
    1391       10174 :         desc.attributes() == JSPROP_ENUMERATE &&
    1392      141568 :         (!obj->isIndexed() || !obj->containsPure(id)) &&
    1393        5087 :         !obj->is<TypedArrayObject>())
    1394             :     {
    1395        5087 :         uint32_t index = JSID_TO_INT(id);
    1396        5087 :         DenseElementResult edResult = obj->ensureDenseElements(cx, index, 1);
    1397        5087 :         if (edResult == DenseElementResult::Failure)
    1398           0 :             return false;
    1399        5087 :         if (edResult == DenseElementResult::Success) {
    1400        5057 :             obj->setDenseElementWithType(cx, index, desc.value());
    1401        5057 :             if (!CallAddPropertyHookDense(cx, obj, index, desc.value()))
    1402           0 :                 return false;
    1403        5057 :             return true;
    1404             :         }
    1405             :     }
    1406             : 
    1407             :     // If we know this is a new property we can call addProperty instead of
    1408             :     // the slower putProperty.
    1409             :     Shape* shape;
    1410             :     if (AddOrChange == IsAddOrChange::Add) {
    1411      116919 :         shape = NativeObject::addProperty(cx, obj, id, desc.getter(), desc.setter(),
    1412             :                                           SHAPE_INVALID_SLOT, desc.attributes(), 0);
    1413             :     } else {
    1414        9389 :         shape = NativeObject::putProperty(cx, obj, id, desc.getter(), desc.setter(),
    1415             :                                           SHAPE_INVALID_SLOT, desc.attributes(), 0);
    1416             :     }
    1417      126311 :     if (!shape)
    1418           0 :         return false;
    1419             : 
    1420      126311 :     UpdateShapeTypeAndValue(cx, obj, shape, id, desc.value());
    1421             : 
    1422             :     // Clear any existing dense index after adding a sparse indexed property,
    1423             :     // and investigate converting the object to dense indexes.
    1424      126309 :     if (JSID_IS_INT(id)) {
    1425          98 :         if (!obj->maybeCopyElementsForWrite(cx))
    1426           0 :             return false;
    1427             : 
    1428          98 :         uint32_t index = JSID_TO_INT(id);
    1429          98 :         NativeObject::removeDenseElementForSparseIndex(cx, obj, index);
    1430             :         DenseElementResult edResult =
    1431          98 :             NativeObject::maybeDensifySparseElements(cx, obj);
    1432          98 :         if (edResult == DenseElementResult::Failure)
    1433           0 :             return false;
    1434          98 :         if (edResult == DenseElementResult::Success) {
    1435           0 :             MOZ_ASSERT(!desc.setter());
    1436           0 :             return CallAddPropertyHookDense(cx, obj, index, desc.value());
    1437             :         }
    1438             :     }
    1439             : 
    1440      126309 :     return CallAddPropertyHook(cx, obj, id, desc.value());
    1441             : }
    1442             : 
    1443       28342 : static bool IsConfigurable(unsigned attrs) { return (attrs & JSPROP_PERMANENT) == 0; }
    1444       10246 : static bool IsEnumerable(unsigned attrs) { return (attrs & JSPROP_ENUMERATE) != 0; }
    1445        9892 : static bool IsWritable(unsigned attrs) { return (attrs & JSPROP_READONLY) == 0; }
    1446             : 
    1447       18604 : static bool IsAccessorDescriptor(unsigned attrs) {
    1448       18604 :     return (attrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
    1449             : }
    1450             : 
    1451        9389 : static bool IsDataDescriptor(unsigned attrs) {
    1452        9389 :     MOZ_ASSERT((attrs & (JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY)) == 0);
    1453        9389 :     return !IsAccessorDescriptor(attrs);
    1454             : }
    1455             : 
    1456             : template <AllowGC allowGC>
    1457             : static MOZ_ALWAYS_INLINE bool
    1458             : GetExistingProperty(JSContext* cx,
    1459             :                     typename MaybeRooted<Value, allowGC>::HandleType receiver,
    1460             :                     typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
    1461             :                     typename MaybeRooted<Shape*, allowGC>::HandleType shape,
    1462             :                     typename MaybeRooted<Value, allowGC>::MutableHandleType vp);
    1463             : 
    1464             : static bool
    1465         795 : GetExistingPropertyValue(JSContext* cx, HandleNativeObject obj, HandleId id,
    1466             :                          Handle<PropertyResult> prop, MutableHandleValue vp)
    1467             : {
    1468         795 :     if (prop.isDenseOrTypedArrayElement()) {
    1469           0 :         vp.set(obj->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
    1470           0 :         return true;
    1471             :     }
    1472         795 :     MOZ_ASSERT(!cx->helperThread());
    1473             : 
    1474         795 :     MOZ_ASSERT(prop.shape()->propid() == id);
    1475         795 :     MOZ_ASSERT(obj->contains(cx, prop.shape()));
    1476             : 
    1477        1590 :     RootedValue receiver(cx, ObjectValue(*obj));
    1478        1590 :     RootedShape shape(cx, prop.shape());
    1479         795 :     return GetExistingProperty<CanGC>(cx, receiver, obj, shape, vp);
    1480             : }
    1481             : 
    1482             : /*
    1483             :  * If desc is redundant with an existing own property obj[id], then set
    1484             :  * |*redundant = true| and return true.
    1485             :  */
    1486             : static bool
    1487       10238 : DefinePropertyIsRedundant(JSContext* cx, HandleNativeObject obj, HandleId id,
    1488             :                           Handle<PropertyResult> prop, unsigned shapeAttrs,
    1489             :                           Handle<PropertyDescriptor> desc, bool *redundant)
    1490             : {
    1491       10238 :     *redundant = false;
    1492             : 
    1493       10238 :     if (desc.hasConfigurable() && desc.configurable() != IsConfigurable(shapeAttrs))
    1494         860 :         return true;
    1495        9378 :     if (desc.hasEnumerable() && desc.enumerable() != IsEnumerable(shapeAttrs))
    1496           4 :         return true;
    1497        9374 :     if (desc.isDataDescriptor()) {
    1498        9215 :         if (IsAccessorDescriptor(shapeAttrs))
    1499           0 :             return true;
    1500        9215 :         if (desc.hasWritable() && desc.writable() != IsWritable(shapeAttrs))
    1501          55 :             return true;
    1502        9160 :         if (desc.hasValue()) {
    1503             :             // Get the current value of the existing property.
    1504        9941 :             RootedValue currentValue(cx);
    1505       27381 :             if (!prop.isDenseOrTypedArrayElement() &&
    1506       18254 :                 prop.shape()->hasSlot() &&
    1507        9127 :                 prop.shape()->hasDefaultGetter())
    1508             :             {
    1509             :                 // Inline GetExistingPropertyValue in order to omit a type
    1510             :                 // correctness assertion that's too strict for this particular
    1511             :                 // call site. For details, see bug 1125624 comments 13-16.
    1512        9127 :                 currentValue.set(obj->getSlot(prop.shape()->slot()));
    1513             :             } else {
    1514           0 :                 if (!GetExistingPropertyValue(cx, obj, id, prop, &currentValue))
    1515           0 :                     return false;
    1516             :             }
    1517             : 
    1518             :             // Don't call SameValue here to ensure we properly update distinct
    1519             :             // NaN values.
    1520        9127 :             if (desc.value() != currentValue)
    1521        8313 :                 return true;
    1522             :         }
    1523             : 
    1524             :         GetterOp existingGetterOp =
    1525         847 :             prop.isDenseOrTypedArrayElement() ? nullptr : prop.shape()->getter();
    1526         847 :         if (desc.getter() != existingGetterOp)
    1527           0 :             return true;
    1528             : 
    1529             :         SetterOp existingSetterOp =
    1530         847 :             prop.isDenseOrTypedArrayElement() ? nullptr : prop.shape()->setter();
    1531         847 :         if (desc.setter() != existingSetterOp)
    1532           0 :             return true;
    1533             :     } else {
    1534         574 :         if (desc.hasGetterObject() &&
    1535         355 :             (!(shapeAttrs & JSPROP_GETTER) || desc.getterObject() != prop.shape()->getterObject()))
    1536             :         {
    1537          78 :             return true;
    1538             :         }
    1539         322 :         if (desc.hasSetterObject() &&
    1540         318 :             (!(shapeAttrs & JSPROP_SETTER) || desc.setterObject() != prop.shape()->setterObject()))
    1541             :         {
    1542          79 :             return true;
    1543             :         }
    1544             :     }
    1545             : 
    1546         849 :     *redundant = true;
    1547         849 :     return true;
    1548             : }
    1549             : 
    1550             : bool
    1551      126569 : js::NativeDefineProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
    1552             :                          Handle<PropertyDescriptor> desc_,
    1553             :                          ObjectOpResult& result)
    1554             : {
    1555      126569 :     desc_.assertValid();
    1556             : 
    1557             :     // Section numbers and step numbers below refer to ES2018, draft rev
    1558             :     // 540b827fccf6122a984be99ab9af7be20e3b5562.
    1559             :     //
    1560             :     // This function aims to implement 9.1.6 [[DefineOwnProperty]] as well as
    1561             :     // the [[DefineOwnProperty]] methods described in 9.4.2.1 (arrays), 9.4.4.2
    1562             :     // (arguments), and 9.4.5.3 (typed array views).
    1563             : 
    1564             :     // Dispense with custom behavior of exotic native objects first.
    1565      126569 :     if (obj->is<ArrayObject>()) {
    1566             :         // 9.4.2.1 step 2. Redefining an array's length is very special.
    1567       28962 :         Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
    1568       14481 :         if (id == NameToId(cx->names().length)) {
    1569             :             // 9.1.6.3 ValidateAndApplyPropertyDescriptor, step 7.a.
    1570           0 :             if (desc_.isAccessorDescriptor())
    1571           0 :                 return result.fail(JSMSG_CANT_REDEFINE_PROP);
    1572             : 
    1573           0 :             MOZ_ASSERT(!cx->helperThread());
    1574           0 :             return ArraySetLength(cx, arr, id, desc_.attributes(), desc_.value(), result);
    1575             :         }
    1576             : 
    1577             :         // 9.4.2.1 step 3. Don't extend a fixed-length array.
    1578             :         uint32_t index;
    1579       14481 :         if (IdIsIndex(id, &index)) {
    1580        4962 :             if (WouldDefinePastNonwritableLength(obj, index))
    1581           0 :                 return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH);
    1582             :         }
    1583      112088 :     } else if (obj->is<TypedArrayObject>()) {
    1584             :         // 9.4.5.3 step 3. Indexed properties of typed arrays are special.
    1585             :         uint64_t index;
    1586           0 :         if (IsTypedArrayIndex(id, &index)) {
    1587           0 :             MOZ_ASSERT(!cx->helperThread());
    1588           0 :             return DefineTypedArrayElement(cx, obj, index, desc_, result);
    1589             :         }
    1590      112088 :     } else if (obj->is<ArgumentsObject>()) {
    1591         246 :         Rooted<ArgumentsObject*> argsobj(cx, &obj->as<ArgumentsObject>());
    1592         123 :         if (id == NameToId(cx->names().length)) {
    1593             :             // Either we are resolving the .length property on this object,
    1594             :             // or redefining it. In the latter case only, we must reify the
    1595             :             // property. To distinguish the two cases, we note that when
    1596             :             // resolving, the JSPROP_RESOLVING mask is set; whereas the first
    1597             :             // time it is redefined, it isn't set.
    1598          38 :             if ((desc_.attributes() & JSPROP_RESOLVING) == 0) {
    1599          17 :                 if (!ArgumentsObject::reifyLength(cx, argsobj))
    1600           0 :                     return false;
    1601             :             }
    1602          85 :         } else if (JSID_IS_SYMBOL(id) && JSID_TO_SYMBOL(id) == cx->wellKnownSymbols().iterator) {
    1603             :             // Do same thing as .length for [@@iterator].
    1604           0 :             if ((desc_.attributes() & JSPROP_RESOLVING) == 0) {
    1605           0 :                 if (!ArgumentsObject::reifyIterator(cx, argsobj))
    1606           0 :                     return false;
    1607             :             }
    1608          85 :         } else if (JSID_IS_INT(id)) {
    1609          85 :             if ((desc_.attributes() & JSPROP_RESOLVING) == 0)
    1610          17 :                 argsobj->markElementOverridden();
    1611             :         }
    1612             :     }
    1613             : 
    1614             :     // 9.1.6.1 OrdinaryDefineOwnProperty step 1.
    1615      253135 :     Rooted<PropertyResult> prop(cx);
    1616      126569 :     if (desc_.attributes() & JSPROP_RESOLVING) {
    1617             :         // We are being called from a resolve or enumerate hook to reify a
    1618             :         // lazily-resolved property. To avoid reentering the resolve hook and
    1619             :         // recursing forever, skip the resolve hook when doing this lookup.
    1620        8363 :         NativeLookupOwnPropertyNoResolve(cx, obj, id, &prop);
    1621             :     } else {
    1622      118206 :         if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &prop))
    1623           0 :             return false;
    1624             :     }
    1625             : 
    1626             :     // From this point, the step numbers refer to
    1627             :     // 9.1.6.3, ValidateAndApplyPropertyDescriptor.
    1628             :     // Step 1 is a redundant assertion.
    1629             : 
    1630             :     // Filling in desc: Here we make a copy of the desc_ argument. We will turn
    1631             :     // it into a complete descriptor before updating obj. The spec algorithm
    1632             :     // does not explicitly do this, but the end result is the same. Search for
    1633             :     // "fill in" below for places where the filling-in actually occurs.
    1634      253135 :     Rooted<PropertyDescriptor> desc(cx, desc_);
    1635             : 
    1636             :     // Step 2.
    1637      126569 :     if (!prop) {
    1638      116331 :         if (!obj->nonProxyIsExtensible())
    1639           0 :             return result.fail(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE);
    1640             : 
    1641             :         // Fill in missing desc fields with defaults.
    1642      116331 :         CompletePropertyDescriptor(&desc);
    1643             : 
    1644      116330 :         if (!AddOrChangeProperty<IsAddOrChange::Add>(cx, obj, id, desc))
    1645           0 :             return false;
    1646      116329 :         return result.succeed();
    1647             :     }
    1648             : 
    1649             :     // Step 3 and 7.a.i.3, 8.a.iii, 10 (partially). We use shapeAttrs as a
    1650             :     // stand-in for shape in many places below, since shape might not be a
    1651             :     // pointer to a real Shape (see IsImplicitDenseOrTypedArrayElement).
    1652       10238 :     unsigned shapeAttrs = GetPropertyAttributes(obj, prop);
    1653             :     bool redundant;
    1654       10238 :     if (!DefinePropertyIsRedundant(cx, obj, id, prop, shapeAttrs, desc, &redundant))
    1655           0 :         return false;
    1656       10238 :     if (redundant) {
    1657             :         // In cases involving JSOP_NEWOBJECT and JSOP_INITPROP, obj can have a
    1658             :         // type for this property that doesn't match the value in the slot.
    1659             :         // Update the type here, even though this DefineProperty call is
    1660             :         // otherwise a no-op. (See bug 1125624 comment 13.)
    1661         849 :         if (!prop.isDenseOrTypedArrayElement() && desc.hasValue())
    1662         814 :             UpdateShapeTypeAndValue(cx, obj, prop.shape(), id, desc.value());
    1663         849 :         return result.succeed();
    1664             :     }
    1665             : 
    1666             :     // Non-standard hack: Allow redefining non-configurable properties if
    1667             :     // JSPROP_REDEFINE_NONCONFIGURABLE is set _and_ the object is a non-DOM
    1668             :     // global. The idea is that a DOM object can never have such a thing on
    1669             :     // its proto chain directly on the web, so we should be OK optimizing
    1670             :     // access to accessors found on such an object. Bug 1105518 contemplates
    1671             :     // removing this hack.
    1672        9389 :     bool skipRedefineChecks = (desc.attributes() & JSPROP_REDEFINE_NONCONFIGURABLE) &&
    1673        9389 :                               obj->is<GlobalObject>() &&
    1674        9389 :                               !obj->getClass()->isDOMClass();
    1675             : 
    1676             :     // Step 4.
    1677        9389 :     if (!IsConfigurable(shapeAttrs) && !skipRedefineChecks) {
    1678          55 :         if (desc.hasConfigurable() && desc.configurable())
    1679           0 :             return result.fail(JSMSG_CANT_REDEFINE_PROP);
    1680          55 :         if (desc.hasEnumerable() && desc.enumerable() != IsEnumerable(shapeAttrs))
    1681           0 :             return result.fail(JSMSG_CANT_REDEFINE_PROP);
    1682             :     }
    1683             : 
    1684             :     // Fill in desc.[[Configurable]] and desc.[[Enumerable]] if missing.
    1685        9389 :     if (!desc.hasConfigurable())
    1686           5 :         desc.setConfigurable(IsConfigurable(shapeAttrs));
    1687        9389 :     if (!desc.hasEnumerable())
    1688         848 :         desc.setEnumerable(IsEnumerable(shapeAttrs));
    1689             : 
    1690             :     // Steps 5-8.
    1691        9389 :     if (desc.isGenericDescriptor()) {
    1692             :         // Step 5. No further validation is required.
    1693             : 
    1694             :         // Fill in desc. A generic descriptor has none of these fields, so copy
    1695             :         // everything from shape.
    1696         675 :         MOZ_ASSERT(!desc.hasValue());
    1697         675 :         MOZ_ASSERT(!desc.hasWritable());
    1698         675 :         MOZ_ASSERT(!desc.hasGetterObject());
    1699         675 :         MOZ_ASSERT(!desc.hasSetterObject());
    1700         675 :         if (IsDataDescriptor(shapeAttrs)) {
    1701        1244 :             RootedValue currentValue(cx);
    1702         622 :             if (!GetExistingPropertyValue(cx, obj, id, prop, &currentValue))
    1703           0 :                 return false;
    1704         622 :             desc.setValue(currentValue);
    1705         622 :             desc.setWritable(IsWritable(shapeAttrs));
    1706             :         } else {
    1707          53 :             desc.setGetterObject(prop.shape()->getterObject());
    1708          53 :             desc.setSetterObject(prop.shape()->setterObject());
    1709             :         }
    1710        8714 :     } else if (desc.isDataDescriptor() != IsDataDescriptor(shapeAttrs)) {
    1711             :         // Step 6.
    1712          23 :         if (!IsConfigurable(shapeAttrs) && !skipRedefineChecks)
    1713           0 :             return result.fail(JSMSG_CANT_REDEFINE_PROP);
    1714             : 
    1715          23 :         if (prop.isDenseOrTypedArrayElement()) {
    1716           0 :             MOZ_ASSERT(!obj->is<TypedArrayObject>());
    1717           0 :             if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
    1718           0 :                 return false;
    1719           0 :             prop.setNativeProperty(obj->lookup(cx, id));
    1720             :         }
    1721             : 
    1722             :         // Fill in desc fields with default values (steps 6.b.i and 6.c.i).
    1723          23 :         CompletePropertyDescriptor(&desc);
    1724        8691 :     } else if (desc.isDataDescriptor()) {
    1725             :         // Step 7.
    1726        8553 :         bool frozen = !IsConfigurable(shapeAttrs) && !IsWritable(shapeAttrs);
    1727             : 
    1728             :         // Step 7.a.i.1.
    1729        8553 :         if (frozen && desc.hasWritable() && desc.writable() && !skipRedefineChecks)
    1730           0 :             return result.fail(JSMSG_CANT_REDEFINE_PROP);
    1731             : 
    1732        8553 :         if (frozen || !desc.hasValue()) {
    1733         173 :             if (prop.isDenseOrTypedArrayElement()) {
    1734           0 :                 MOZ_ASSERT(!obj->is<TypedArrayObject>());
    1735           0 :                 if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
    1736           0 :                     return false;
    1737           0 :                 prop.setNativeProperty(obj->lookup(cx, id));
    1738             :             }
    1739             : 
    1740         346 :             RootedValue currentValue(cx);
    1741         173 :             if (!GetExistingPropertyValue(cx, obj, id, prop, &currentValue))
    1742           0 :                 return false;
    1743             : 
    1744         173 :             if (!desc.hasValue()) {
    1745             :                 // Fill in desc.[[Value]].
    1746         173 :                 desc.setValue(currentValue);
    1747             :             } else {
    1748             :                 // Step 7.a.i.2.
    1749             :                 bool same;
    1750           0 :                 MOZ_ASSERT(!cx->helperThread());
    1751           0 :                 if (!SameValue(cx, desc.value(), currentValue, &same))
    1752           0 :                     return false;
    1753           0 :                 if (!same && !skipRedefineChecks)
    1754           0 :                     return result.fail(JSMSG_CANT_REDEFINE_PROP);
    1755             :             }
    1756             :         }
    1757             : 
    1758             :         // Step 7.a.i.3.
    1759        8553 :         if (frozen && !skipRedefineChecks)
    1760           0 :             return result.succeed();
    1761             : 
    1762        8553 :         if (!desc.hasWritable())
    1763           0 :             desc.setWritable(IsWritable(shapeAttrs));
    1764             :     } else {
    1765             :         // Step 8.
    1766         138 :         MOZ_ASSERT(prop.shape()->isAccessorDescriptor());
    1767         138 :         MOZ_ASSERT(desc.isAccessorDescriptor());
    1768             : 
    1769             :         // The spec says to use SameValue, but since the values in
    1770             :         // question are objects, we can just compare pointers.
    1771         138 :         if (desc.hasSetterObject()) {
    1772             :             // Step 8.a.i.
    1773         320 :             if (!IsConfigurable(shapeAttrs) &&
    1774         240 :                 desc.setterObject() != prop.shape()->setterObject() &&
    1775           0 :                 !skipRedefineChecks)
    1776             :             {
    1777           0 :                 return result.fail(JSMSG_CANT_REDEFINE_PROP);
    1778             :             }
    1779             :         } else {
    1780             :             // Fill in desc.[[Set]] from shape.
    1781          58 :             desc.setSetterObject(prop.shape()->setterObject());
    1782             :         }
    1783         138 :         if (desc.hasGetterObject()) {
    1784             :             // Step 8.a.ii.
    1785         236 :             if (!IsConfigurable(shapeAttrs) &&
    1786         177 :                 desc.getterObject() != prop.shape()->getterObject() &&
    1787           0 :                 !skipRedefineChecks)
    1788             :             {
    1789           0 :                 return result.fail(JSMSG_CANT_REDEFINE_PROP);
    1790             :             }
    1791             :         } else {
    1792             :             // Fill in desc.[[Get]] from shape.
    1793          79 :             desc.setGetterObject(prop.shape()->getterObject());
    1794             :         }
    1795             : 
    1796             :         // Step 8.a.iii (Omitted).
    1797             :     }
    1798             : 
    1799             :     // Step 9.
    1800        9389 :     if (!AddOrChangeProperty<IsAddOrChange::AddOrChange>(cx, obj, id, desc))
    1801           0 :         return false;
    1802             : 
    1803             :     // Step 10.
    1804        9389 :     return result.succeed();
    1805             : }
    1806             : 
    1807             : bool
    1808       35274 : js::NativeDefineProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
    1809             :                          HandleValue value, GetterOp getter, SetterOp setter, unsigned attrs,
    1810             :                          ObjectOpResult& result)
    1811             : {
    1812       70548 :     Rooted<PropertyDescriptor> desc(cx);
    1813       35274 :     desc.initFields(nullptr, value, attrs, getter, setter);
    1814       70548 :     return NativeDefineProperty(cx, obj, id, desc, result);
    1815             : }
    1816             : 
    1817             : bool
    1818           0 : js::NativeDefineProperty(JSContext* cx, HandleNativeObject obj, PropertyName* name,
    1819             :                          HandleValue value, GetterOp getter, SetterOp setter, unsigned attrs,
    1820             :                          ObjectOpResult& result)
    1821             : {
    1822           0 :     RootedId id(cx, NameToId(name));
    1823           0 :     return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs, result);
    1824             : }
    1825             : 
    1826             : bool
    1827           0 : js::NativeDefineElement(JSContext* cx, HandleNativeObject obj, uint32_t index,
    1828             :                         HandleValue value, GetterOp getter, SetterOp setter, unsigned attrs,
    1829             :                         ObjectOpResult& result)
    1830             : {
    1831           0 :     RootedId id(cx);
    1832           0 :     if (index <= JSID_INT_MAX) {
    1833           0 :         id = INT_TO_JSID(index);
    1834           0 :         return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs, result);
    1835             :     }
    1836             : 
    1837           0 :     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
    1838             : 
    1839           0 :     if (!IndexToId(cx, index, &id))
    1840           0 :         return false;
    1841             : 
    1842           0 :     return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs, result);
    1843             : }
    1844             : 
    1845             : bool
    1846       35257 : js::NativeDefineProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
    1847             :                          HandleValue value, JSGetterOp getter, JSSetterOp setter,
    1848             :                          unsigned attrs)
    1849             : {
    1850       35257 :     ObjectOpResult result;
    1851       35257 :     if (!NativeDefineProperty(cx, obj, id, value, getter, setter, attrs, result))
    1852           0 :         return false;
    1853       35257 :     if (!result) {
    1854             :         // Off-thread callers should not get here: they must call this
    1855             :         // function only with known-valid arguments. Populating a new
    1856             :         // PlainObject with configurable properties is fine.
    1857           0 :         MOZ_ASSERT(!cx->helperThread());
    1858           0 :         result.reportError(cx, obj, id);
    1859           0 :         return false;
    1860             :     }
    1861       35257 :     return true;
    1862             : }
    1863             : 
    1864             : bool
    1865         220 : js::NativeDefineProperty(JSContext* cx, HandleNativeObject obj, PropertyName* name,
    1866             :                          HandleValue value, JSGetterOp getter, JSSetterOp setter,
    1867             :                          unsigned attrs)
    1868             : {
    1869         440 :     RootedId id(cx, NameToId(name));
    1870         440 :     return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs);
    1871             : }
    1872             : 
    1873             : static bool
    1874        5648 : DefineNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
    1875             :                           Handle<PropertyDescriptor> desc, ObjectOpResult& result)
    1876             : {
    1877        5648 :     desc.assertComplete();
    1878             : 
    1879             :     // Optimized NativeDefineProperty() version for known absent properties.
    1880             : 
    1881             :     // Dispense with custom behavior of exotic native objects first.
    1882        5648 :     if (obj->is<ArrayObject>()) {
    1883             :         // Array's length property is non-configurable, so we shouldn't
    1884             :         // encounter it in this function.
    1885          12 :         MOZ_ASSERT(id != NameToId(cx->names().length));
    1886             : 
    1887             :         // 9.4.2.1 step 3. Don't extend a fixed-length array.
    1888             :         uint32_t index;
    1889          12 :         if (IdIsIndex(id, &index)) {
    1890          12 :             if (WouldDefinePastNonwritableLength(obj, index))
    1891           0 :                 return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH);
    1892             :         }
    1893        5636 :     } else if (obj->is<TypedArrayObject>()) {
    1894             :         // 9.4.5.3 step 3. Indexed properties of typed arrays are special.
    1895             :         uint64_t index;
    1896           0 :         if (IsTypedArrayIndex(id, &index)) {
    1897             :             // This method is only called for non-existent properties, which
    1898             :             // means any absent indexed property must be out of range.
    1899           0 :             MOZ_ASSERT(index >= obj->as<TypedArrayObject>().length());
    1900             : 
    1901             :             // We (wrongly) ignore out of range defines.
    1902           0 :             return result.succeed();
    1903             :         }
    1904        5636 :     } else if (obj->is<ArgumentsObject>()) {
    1905             :         // If this method is called with either |length| or |@@iterator|, the
    1906             :         // property was previously deleted and hence should already be marked
    1907             :         // as overridden.
    1908           0 :         MOZ_ASSERT_IF(id == NameToId(cx->names().length),
    1909             :                       obj->as<ArgumentsObject>().hasOverriddenLength());
    1910           0 :         MOZ_ASSERT_IF(JSID_IS_SYMBOL(id) && JSID_TO_SYMBOL(id) == cx->wellKnownSymbols().iterator,
    1911             :                       obj->as<ArgumentsObject>().hasOverriddenIterator());
    1912             : 
    1913             :         // We still need to mark any element properties as overridden.
    1914           0 :         if (JSID_IS_INT(id))
    1915           0 :             obj->as<ArgumentsObject>().markElementOverridden();
    1916             :     }
    1917             : 
    1918             : #ifdef DEBUG
    1919       11296 :     Rooted<PropertyResult> prop(cx);
    1920        5648 :     NativeLookupOwnPropertyNoResolve(cx, obj, id, &prop);
    1921        5648 :     MOZ_ASSERT(!prop, "didn't expect to find an existing property");
    1922             : #endif
    1923             : 
    1924             :     // 9.1.6.3, ValidateAndApplyPropertyDescriptor.
    1925             :     // Step 1 is a redundant assertion, step 3 and later don't apply here.
    1926             : 
    1927             :     // Step 2.
    1928        5648 :     if (!obj->nonProxyIsExtensible())
    1929           0 :         return result.fail(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE);
    1930             : 
    1931        5648 :     if (!AddOrChangeProperty<IsAddOrChange::Add>(cx, obj, id, desc))
    1932           0 :         return false;
    1933        5648 :     return result.succeed();
    1934             : }
    1935             : 
    1936             : 
    1937             : /*** [[HasProperty]] *****************************************************************************/
    1938             : 
    1939             : // ES6 draft rev31 9.1.7.1 OrdinaryHasProperty
    1940             : bool
    1941       12642 : js::NativeHasProperty(JSContext* cx, HandleNativeObject obj, HandleId id, bool* foundp)
    1942             : {
    1943       25284 :     RootedNativeObject pobj(cx, obj);
    1944       25284 :     Rooted<PropertyResult> prop(cx);
    1945             : 
    1946             :     // This loop isn't explicit in the spec algorithm. See the comment on step
    1947             :     // 7.a. below.
    1948             :     for (;;) {
    1949             :         // Steps 2-3. ('done' is a SpiderMonkey-specific thing, used below.)
    1950             :         bool done;
    1951       17126 :         if (!LookupOwnPropertyInline<CanGC>(cx, pobj, id, &prop, &done))
    1952       12642 :             return false;
    1953             : 
    1954             :         // Step 4.
    1955       17126 :         if (prop) {
    1956        4326 :             *foundp = true;
    1957        4326 :             return true;
    1958             :         }
    1959             : 
    1960             :         // Step 5-6. The check for 'done' on this next line is tricky.
    1961             :         // done can be true in exactly these unlikely-sounding cases:
    1962             :         // - We're looking up an element, and pobj is a TypedArray that
    1963             :         //   doesn't have that many elements.
    1964             :         // - We're being called from a resolve hook to assign to the property
    1965             :         //   being resolved.
    1966             :         // What they all have in common is we do not want to keep walking
    1967             :         // the prototype chain, and always claim that the property
    1968             :         // doesn't exist.
    1969       17284 :         RootedObject proto(cx, done ? nullptr : pobj->staticPrototype());
    1970             : 
    1971             :         // Step 8.
    1972       12800 :         if (!proto) {
    1973        8316 :             *foundp = false;
    1974        8316 :             return true;
    1975             :         }
    1976             : 
    1977             :         // Step 7.a. If the prototype is also native, this step is a
    1978             :         // recursive tail call, and we don't need to go through all the
    1979             :         // plumbing of HasProperty; the top of the loop is where
    1980             :         // we're going to end up anyway. But if pobj is non-native,
    1981             :         // that optimization would be incorrect.
    1982        4484 :         if (!proto->isNative())
    1983           0 :             return HasProperty(cx, proto, id, foundp);
    1984             : 
    1985        4484 :         pobj = &proto->as<NativeObject>();
    1986        4484 :     }
    1987             : }
    1988             : 
    1989             : 
    1990             : /*** [[GetOwnPropertyDescriptor]] ****************************************************************/
    1991             : 
    1992             : bool
    1993        9863 : js::NativeGetOwnPropertyDescriptor(JSContext* cx, HandleNativeObject obj, HandleId id,
    1994             :                                    MutableHandle<PropertyDescriptor> desc)
    1995             : {
    1996       19726 :     Rooted<PropertyResult> prop(cx);
    1997        9863 :     if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &prop))
    1998           0 :         return false;
    1999        9863 :     if (!prop) {
    2000        7419 :         desc.object().set(nullptr);
    2001        7419 :         return true;
    2002             :     }
    2003             : 
    2004        2444 :     desc.setAttributes(GetPropertyAttributes(obj, prop));
    2005        2444 :     if (desc.isAccessorDescriptor()) {
    2006         511 :         MOZ_ASSERT(desc.isShared());
    2007             : 
    2008             :         // The result of GetOwnPropertyDescriptor() must be either undefined or
    2009             :         // a complete property descriptor (per ES6 draft rev 32 (2015 Feb 2)
    2010             :         // 6.1.7.3, Invariants of the Essential Internal Methods).
    2011             :         //
    2012             :         // It is an unfortunate fact that in SM, properties can exist that have
    2013             :         // JSPROP_GETTER or JSPROP_SETTER but not both. In these cases, rather
    2014             :         // than return true with desc incomplete, we fill out the missing
    2015             :         // getter or setter with a null, following CompletePropertyDescriptor.
    2016         511 :         if (desc.hasGetterObject()) {
    2017         511 :             desc.setGetterObject(prop.shape()->getterObject());
    2018             :         } else {
    2019           0 :             desc.setGetterObject(nullptr);
    2020           0 :             desc.attributesRef() |= JSPROP_GETTER;
    2021             :         }
    2022         511 :         if (desc.hasSetterObject()) {
    2023         511 :             desc.setSetterObject(prop.shape()->setterObject());
    2024             :         } else {
    2025           0 :             desc.setSetterObject(nullptr);
    2026           0 :             desc.attributesRef() |= JSPROP_SETTER;
    2027             :         }
    2028             : 
    2029         511 :         desc.value().setUndefined();
    2030             :     } else {
    2031             :         // This is either a straight-up data property or (rarely) a
    2032             :         // property with a JSGetterOp/JSSetterOp. The latter must be
    2033             :         // reported to the caller as a plain data property, so clear
    2034             :         // desc.getter/setter, and mask away the SHARED bit.
    2035        1933 :         desc.setGetter(nullptr);
    2036        1933 :         desc.setSetter(nullptr);
    2037        1933 :         desc.attributesRef() &= ~JSPROP_SHARED;
    2038             : 
    2039        1933 :         if (prop.isDenseOrTypedArrayElement()) {
    2040           0 :             desc.value().set(obj->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
    2041             :         } else {
    2042        3866 :             RootedShape shape(cx, prop.shape());
    2043        1933 :             if (!NativeGetExistingProperty(cx, obj, obj, shape, desc.value()))
    2044           0 :                 return false;
    2045             :         }
    2046             :     }
    2047             : 
    2048        2444 :     desc.object().set(obj);
    2049        2444 :     desc.assertComplete();
    2050        2444 :     return true;
    2051             : }
    2052             : 
    2053             : 
    2054             : /*** [[Get]] *************************************************************************************/
    2055             : 
    2056             : static inline bool
    2057        6420 : CallGetter(JSContext* cx, HandleObject obj, HandleValue receiver, HandleShape shape,
    2058             :            MutableHandleValue vp)
    2059             : {
    2060        6420 :     MOZ_ASSERT(!shape->hasDefaultGetter());
    2061             : 
    2062        6420 :     if (shape->hasGetterValue()) {
    2063       11376 :         RootedValue getter(cx, shape->getterValue());
    2064        5688 :         return js::CallGetter(cx, receiver, getter, vp);
    2065             :     }
    2066             : 
    2067             :     // In contrast to normal getters JSGetterOps always want the holder.
    2068        1464 :     RootedId id(cx, shape->propid());
    2069         732 :     return CallJSGetterOp(cx, shape->getterOp(), obj, id, vp);
    2070             : }
    2071             : 
    2072             : template <AllowGC allowGC>
    2073             : static MOZ_ALWAYS_INLINE bool
    2074       54661 : GetExistingProperty(JSContext* cx,
    2075             :                     typename MaybeRooted<Value, allowGC>::HandleType receiver,
    2076             :                     typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
    2077             :                     typename MaybeRooted<Shape*, allowGC>::HandleType shape,
    2078             :                     typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
    2079             : {
    2080       54661 :     if (shape->hasSlot()) {
    2081       48216 :         vp.set(obj->getSlot(shape->slot()));
    2082       48216 :         MOZ_ASSERT_IF(!vp.isMagic(JS_UNINITIALIZED_LEXICAL) &&
    2083             :                       !obj->isSingleton() &&
    2084             :                       !obj->template is<EnvironmentObject>() &&
    2085             :                       shape->hasDefaultGetter(),
    2086             :                       ObjectGroupHasProperty(cx, obj->group(), shape->propid(), vp));
    2087             :     } else {
    2088        6445 :         vp.setUndefined();
    2089             :     }
    2090       54661 :     if (shape->hasDefaultGetter())
    2091       48216 :         return true;
    2092             : 
    2093             :     {
    2094             :         jsbytecode* pc;
    2095        6445 :         JSScript* script = cx->currentScript(&pc);
    2096        6445 :         if (script && script->hasBaselineScript()) {
    2097         778 :             switch (JSOp(*pc)) {
    2098             :               case JSOP_GETPROP:
    2099             :               case JSOP_CALLPROP:
    2100             :               case JSOP_LENGTH:
    2101         715 :                 script->baselineScript()->noteAccessedGetter(script->pcToOffset(pc));
    2102         715 :                 break;
    2103             :               default:
    2104          63 :                 break;
    2105             :             }
    2106             :         }
    2107             :     }
    2108             : 
    2109             :     if (!allowGC)
    2110          25 :         return false;
    2111             : 
    2112        6420 :     if (!CallGetter(cx,
    2113             :                     MaybeRooted<JSObject*, allowGC>::toHandle(obj),
    2114             :                     MaybeRooted<Value, allowGC>::toHandle(receiver),
    2115             :                     MaybeRooted<Shape*, allowGC>::toHandle(shape),
    2116             :                     MaybeRooted<Value, allowGC>::toMutableHandle(vp)))
    2117             :     {
    2118           6 :         return false;
    2119             :     }
    2120             : 
    2121             :     // Ancient nonstandard extension: via the JSAPI it's possible to create a
    2122             :     // data property that has both a slot and a getter. In that case, copy the
    2123             :     // value returned by the getter back into the slot.
    2124        6414 :     if (shape->hasSlot() && obj->contains(cx, shape))
    2125           0 :         obj->setSlot(shape->slot(), vp);
    2126             : 
    2127        6414 :     return true;
    2128             : }
    2129             : 
    2130             : bool
    2131        2768 : js::NativeGetExistingProperty(JSContext* cx, HandleObject receiver, HandleNativeObject obj,
    2132             :                               HandleShape shape, MutableHandleValue vp)
    2133             : {
    2134        5536 :     RootedValue receiverValue(cx, ObjectValue(*receiver));
    2135        5536 :     return GetExistingProperty<CanGC>(cx, receiverValue, obj, shape, vp);
    2136             : }
    2137             : 
    2138             : /*
    2139             :  * Given pc pointing after a property accessing bytecode, return true if the
    2140             :  * access is "property-detecting" -- that is, if we shouldn't warn about it
    2141             :  * even if no such property is found and strict warnings are enabled.
    2142             :  */
    2143             : static bool
    2144           0 : Detecting(JSContext* cx, JSScript* script, jsbytecode* pc)
    2145             : {
    2146           0 :     MOZ_ASSERT(script->containsPC(pc));
    2147             : 
    2148             :     // Skip jump target opcodes.
    2149           0 :     while (pc < script->codeEnd() && BytecodeIsJumpTarget(JSOp(*pc)))
    2150           0 :         pc = GetNextPc(pc);
    2151             : 
    2152           0 :     MOZ_ASSERT(script->containsPC(pc));
    2153           0 :     if (pc >= script->codeEnd())
    2154           0 :         return false;
    2155             : 
    2156             :     // General case: a branch or equality op follows the access.
    2157           0 :     JSOp op = JSOp(*pc);
    2158           0 :     if (CodeSpec[op].format & JOF_DETECTING)
    2159           0 :         return true;
    2160             : 
    2161           0 :     jsbytecode* endpc = script->codeEnd();
    2162             : 
    2163           0 :     if (op == JSOP_NULL) {
    2164             :         // Special case #1: don't warn about (obj.prop == null).
    2165           0 :         if (++pc < endpc) {
    2166           0 :             op = JSOp(*pc);
    2167           0 :             return op == JSOP_EQ || op == JSOP_NE;
    2168             :         }
    2169           0 :         return false;
    2170             :     }
    2171             : 
    2172           0 :     if (op == JSOP_GETGNAME || op == JSOP_GETNAME) {
    2173             :         // Special case #2: don't warn about (obj.prop == undefined).
    2174           0 :         JSAtom* atom = script->getAtom(GET_UINT32_INDEX(pc));
    2175           0 :         if (atom == cx->names().undefined &&
    2176           0 :             (pc += CodeSpec[op].length) < endpc) {
    2177           0 :             op = JSOp(*pc);
    2178           0 :             return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
    2179             :         }
    2180             :     }
    2181             : 
    2182           0 :     return false;
    2183             : }
    2184             : 
    2185             : enum IsNameLookup { NotNameLookup = false, NameLookup = true };
    2186             : 
    2187             : /*
    2188             :  * Finish getting the property `receiver[id]` after looking at every object on
    2189             :  * the prototype chain and not finding any such property.
    2190             :  *
    2191             :  * Per the spec, this should just set the result to `undefined` and call it a
    2192             :  * day. However:
    2193             :  *
    2194             :  * 1.  We add support for the nonstandard JSClass::getProperty hook.
    2195             :  *
    2196             :  * 2.  This function also runs when we're evaluating an expression that's an
    2197             :  *     Identifier (that is, an unqualified name lookup), so we need to figure
    2198             :  *     out if that's what's happening and throw a ReferenceError if so.
    2199             :  *
    2200             :  * 3.  We also emit an optional warning for this. (It's not super useful on the
    2201             :  *     web, as there are too many false positives, but anecdotally useful in
    2202             :  *     Gecko code.)
    2203             :  */
    2204             : static bool
    2205        3736 : GetNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
    2206             :                        HandleValue receiver, IsNameLookup nameLookup, MutableHandleValue vp)
    2207             : {
    2208        3736 :     vp.setUndefined();
    2209             : 
    2210             :     // Non-standard extension: Call the getProperty hook. If it sets vp to a
    2211             :     // value other than undefined, we're done. If not, fall through to the
    2212             :     // warning/error checks below.
    2213        3736 :     if (JSGetterOp getProperty = obj->getClass()->getGetProperty()) {
    2214           6 :         if (!CallJSGetterOp(cx, getProperty, obj, id, vp))
    2215           0 :             return false;
    2216             : 
    2217           6 :         if (!vp.isUndefined())
    2218           6 :             return true;
    2219             :     }
    2220             : 
    2221             :     // If we are doing a name lookup, this is a ReferenceError.
    2222        3730 :     if (nameLookup)
    2223           0 :         return ReportIsNotDefined(cx, id);
    2224             : 
    2225             :     // Give a strict warning if foo.bar is evaluated by a script for an object
    2226             :     // foo with no property named 'bar'.
    2227             :     //
    2228             :     // Don't warn if extra warnings not enabled or for random getprop
    2229             :     // operations.
    2230        3730 :     if (!cx->compartment()->behaviors().extraWarnings(cx))
    2231        3730 :         return true;
    2232             : 
    2233             :     jsbytecode* pc;
    2234           0 :     RootedScript script(cx, cx->currentScript(&pc));
    2235           0 :     if (!script)
    2236           0 :         return true;
    2237             : 
    2238           0 :     if (*pc != JSOP_GETPROP && *pc != JSOP_GETELEM)
    2239           0 :         return true;
    2240             : 
    2241             :     // Don't warn repeatedly for the same script.
    2242           0 :     if (script->warnedAboutUndefinedProp())
    2243           0 :         return true;
    2244             : 
    2245             :     // Don't warn in self-hosted code (where the further presence of
    2246             :     // JS::RuntimeOptions::werror() would result in impossible-to-avoid
    2247             :     // errors to entirely-innocent client code).
    2248           0 :     if (script->selfHosted())
    2249           0 :         return true;
    2250             : 
    2251             :     // We may just be checking if that object has an iterator.
    2252           0 :     if (JSID_IS_ATOM(id, cx->names().iteratorIntrinsic))
    2253           0 :         return true;
    2254             : 
    2255             :     // Do not warn about tests like (obj[prop] == undefined).
    2256           0 :     pc += CodeSpec[*pc].length;
    2257           0 :     if (Detecting(cx, script, pc))
    2258           0 :         return true;
    2259             : 
    2260           0 :     unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT;
    2261           0 :     script->setWarnedAboutUndefinedProp();
    2262             : 
    2263             :     // Ok, bad undefined property reference: whine about it.
    2264           0 :     RootedValue val(cx, IdToValue(id));
    2265           0 :     return ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP, JSDVG_IGNORE_STACK, val,
    2266           0 :                                     nullptr, nullptr, nullptr);
    2267             : }
    2268             : 
    2269             : /* The NoGC version of GetNonexistentProperty, present only to make types line up. */
    2270             : bool
    2271         206 : GetNonexistentProperty(JSContext* cx, NativeObject* const& obj, const jsid& id, const Value& receiver,
    2272             :                        IsNameLookup nameLookup, FakeMutableHandle<Value> vp)
    2273             : {
    2274         206 :     return false;
    2275             : }
    2276             : 
    2277             : static inline bool
    2278         221 : GeneralizedGetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue receiver,
    2279             :                        IsNameLookup nameLookup, MutableHandleValue vp)
    2280             : {
    2281         221 :     if (!CheckRecursionLimit(cx))
    2282           0 :         return false;
    2283         221 :     if (nameLookup) {
    2284             :         // When nameLookup is true, GetProperty implements ES6 rev 34 (2015 Feb
    2285             :         // 20) 8.1.1.2.6 GetBindingValue, with step 3 (the call to HasProperty)
    2286             :         // and step 6 (the call to Get) fused so that only a single lookup is
    2287             :         // needed.
    2288             :         //
    2289             :         // If we get here, we've reached a non-native object. Fall back on the
    2290             :         // algorithm as specified, with two separate lookups. (Note that we
    2291             :         // throw ReferenceErrors regardless of strictness, technically a bug.)
    2292             : 
    2293             :         bool found;
    2294           0 :         if (!HasProperty(cx, obj, id, &found))
    2295           0 :             return false;
    2296           0 :         if (!found)
    2297           0 :             return ReportIsNotDefined(cx, id);
    2298             :     }
    2299             : 
    2300         221 :     return GetProperty(cx, obj, receiver, id, vp);
    2301             : }
    2302             : 
    2303             : static inline bool
    2304           0 : GeneralizedGetProperty(JSContext* cx, JSObject* obj, jsid id, const Value& receiver,
    2305             :                        IsNameLookup nameLookup, FakeMutableHandle<Value> vp)
    2306             : {
    2307           0 :     if (!CheckRecursionLimitDontReport(cx))
    2308           0 :         return false;
    2309           0 :     if (nameLookup)
    2310           0 :         return false;
    2311           0 :     return GetPropertyNoGC(cx, obj, receiver, id, vp.address());
    2312             : }
    2313             : 
    2314             : template <AllowGC allowGC>
    2315             : static MOZ_ALWAYS_INLINE bool
    2316       59048 : NativeGetPropertyInline(JSContext* cx,
    2317             :                         typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
    2318             :                         typename MaybeRooted<Value, allowGC>::HandleType receiver,
    2319             :                         typename MaybeRooted<jsid, allowGC>::HandleType id,
    2320             :                         IsNameLookup nameLookup,
    2321             :                         typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
    2322             : {
    2323      115174 :     typename MaybeRooted<NativeObject*, allowGC>::RootType pobj(cx, obj);
    2324      115174 :     typename MaybeRooted<PropertyResult, allowGC>::RootType prop(cx);
    2325             : 
    2326             :     // This loop isn't explicit in the spec algorithm. See the comment on step
    2327             :     // 4.d below.
    2328       25784 :     for (;;) {
    2329             :         // Steps 2-3. ('done' is a SpiderMonkey-specific thing, used below.)
    2330             :         bool done;
    2331       84832 :         if (!LookupOwnPropertyInline<allowGC>(cx, pobj, id, &prop, &done))
    2332       59202 :             return false;
    2333             : 
    2334       84678 :         if (prop) {
    2335             :             // Steps 5-8. Special case for dense elements because
    2336             :             // GetExistingProperty doesn't support those.
    2337       54731 :             if (prop.isDenseOrTypedArrayElement()) {
    2338        3633 :                 vp.set(pobj->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
    2339        3633 :                 return true;
    2340             :             }
    2341             : 
    2342      101047 :             typename MaybeRooted<Shape*, allowGC>::RootType shape(cx, prop.shape());
    2343       51098 :             return GetExistingProperty<allowGC>(cx, receiver, pobj, shape, vp);
    2344             :         }
    2345             : 
    2346             :         // Steps 4.a-b. The check for 'done' on this next line is tricky.
    2347             :         // done can be true in exactly these unlikely-sounding cases:
    2348             :         // - We're looking up an element, and pobj is a TypedArray that
    2349             :         //   doesn't have that many elements.
    2350             :         // - We're being called from a resolve hook to assign to the property
    2351             :         //   being resolved.
    2352             :         // What they all have in common is we do not want to keep walking
    2353             :         // the prototype chain.
    2354       55731 :         RootedObject proto(cx, done ? nullptr : pobj->staticPrototype());
    2355             : 
    2356             :         // Step 4.c. The spec algorithm simply returns undefined if proto is
    2357             :         // null, but see the comment on GetNonexistentProperty.
    2358       29947 :         if (!proto)
    2359        3942 :             return GetNonexistentProperty(cx, obj, id, receiver, nameLookup, vp);
    2360             : 
    2361             :         // Step 4.d. If the prototype is also native, this step is a
    2362             :         // recursive tail call, and we don't need to go through all the
    2363             :         // plumbing of JSObject::getGeneric; the top of the loop is where
    2364             :         // we're going to end up anyway. But if pobj is non-native,
    2365             :         // that optimization would be incorrect.
    2366       26005 :         if (proto->getOpsGetProperty())
    2367         221 :             return GeneralizedGetProperty(cx, proto, id, receiver, nameLookup, vp);
    2368             : 
    2369       25784 :         pobj = &proto->as<NativeObject>();
    2370             :     }
    2371             : }
    2372             : 
    2373             : bool
    2374       56126 : js::NativeGetProperty(JSContext* cx, HandleNativeObject obj, HandleValue receiver, HandleId id,
    2375             :                       MutableHandleValue vp)
    2376             : {
    2377       56126 :     return NativeGetPropertyInline<CanGC>(cx, obj, receiver, id, NotNameLookup, vp);
    2378             : }
    2379             : 
    2380             : bool
    2381        2922 : js::NativeGetPropertyNoGC(JSContext* cx, NativeObject* obj, const Value& receiver, jsid id, Value* vp)
    2382             : {
    2383        5844 :     AutoAssertNoException noexc(cx);
    2384        5844 :     return NativeGetPropertyInline<NoGC>(cx, obj, receiver, id, NotNameLookup, vp);
    2385             : }
    2386             : 
    2387             : bool
    2388           0 : js::GetNameBoundInEnvironment(JSContext* cx, HandleObject envArg, HandleId id, MutableHandleValue vp)
    2389             : {
    2390             :     // Manually unwrap 'with' environments to prevent looking up @@unscopables
    2391             :     // twice.
    2392             :     //
    2393             :     // This is unfortunate because internally, the engine does not distinguish
    2394             :     // HasProperty from HasBinding: both are implemented as a HasPropertyOp
    2395             :     // hook on a WithEnvironmentObject.
    2396             :     //
    2397             :     // In the case of attempting to get the value of a binding already looked
    2398             :     // up via BINDNAME, calling HasProperty on the WithEnvironmentObject is
    2399             :     // equivalent to calling HasBinding a second time. This results in the
    2400             :     // incorrect behavior of performing the @@unscopables check again.
    2401           0 :     RootedObject env(cx, MaybeUnwrapWithEnvironment(envArg));
    2402           0 :     RootedValue receiver(cx, ObjectValue(*env));
    2403           0 :     if (env->getOpsGetProperty())
    2404           0 :         return GeneralizedGetProperty(cx, env, id, receiver, NameLookup, vp);
    2405           0 :     return NativeGetPropertyInline<CanGC>(cx, env.as<NativeObject>(), receiver, id, NameLookup, vp);
    2406             : }
    2407             : 
    2408             : 
    2409             : /*** [[Set]] *************************************************************************************/
    2410             : 
    2411             : static bool
    2412           0 : MaybeReportUndeclaredVarAssignment(JSContext* cx, HandleString propname)
    2413             : {
    2414             :     unsigned flags;
    2415             :     {
    2416             :         jsbytecode* pc;
    2417           0 :         JSScript* script = cx->currentScript(&pc, JSContext::ALLOW_CROSS_COMPARTMENT);
    2418           0 :         if (!script)
    2419           0 :             return true;
    2420             : 
    2421             :         // If the code is not strict and extra warnings aren't enabled, then no
    2422             :         // check is needed.
    2423           0 :         if (IsStrictSetPC(pc))
    2424           0 :             flags = JSREPORT_ERROR;
    2425           0 :         else if (cx->compartment()->behaviors().extraWarnings(cx))
    2426           0 :             flags = JSREPORT_WARNING | JSREPORT_STRICT;
    2427             :         else
    2428           0 :             return true;
    2429             :     }
    2430             : 
    2431           0 :     JSAutoByteString bytes;
    2432           0 :     if (!bytes.encodeUtf8(cx, propname))
    2433           0 :         return false;
    2434           0 :     return JS_ReportErrorFlagsAndNumberUTF8(cx, flags, GetErrorMessage, nullptr,
    2435           0 :                                             JSMSG_UNDECLARED_VAR, bytes.ptr());
    2436             : }
    2437             : 
    2438             : /*
    2439             :  * Finish assignment to a shapeful data property of a native object obj. This
    2440             :  * conforms to no standard and there is a lot of legacy baggage here.
    2441             :  */
    2442             : static bool
    2443        2944 : NativeSetExistingDataProperty(JSContext* cx, HandleNativeObject obj, HandleShape shape,
    2444             :                               HandleValue v, HandleValue receiver, ObjectOpResult& result)
    2445             : {
    2446        2944 :     MOZ_ASSERT(obj->isNative());
    2447        2944 :     MOZ_ASSERT(shape->isDataDescriptor());
    2448             : 
    2449        2944 :     if (shape->hasDefaultSetter()) {
    2450        2872 :         if (shape->hasSlot()) {
    2451             :             // The common path. Standard data property.
    2452             : 
    2453             :             // Global properties declared with 'var' will be initially
    2454             :             // defined with an undefined value, so don't treat the initial
    2455             :             // assignments to such properties as overwrites.
    2456        2872 :             bool overwriting = !obj->is<GlobalObject>() || !obj->getSlot(shape->slot()).isUndefined();
    2457        2872 :             obj->setSlotWithType(cx, shape, v, overwriting);
    2458        2872 :             return result.succeed();
    2459             :         }
    2460             : 
    2461             :         // Bizarre: shared (slotless) property that's writable but has no
    2462             :         // JSSetterOp. JS code can't define such a property, but it can be done
    2463             :         // through the JSAPI. Treat it as non-writable.
    2464           0 :         return result.fail(JSMSG_GETTER_ONLY);
    2465             :     }
    2466             : 
    2467          72 :     MOZ_ASSERT(!obj->is<WithEnvironmentObject>());  // See bug 1128681.
    2468             : 
    2469          72 :     uint32_t sample = cx->propertyRemovals;
    2470         144 :     RootedId id(cx, shape->propid());
    2471         144 :     RootedValue value(cx, v);
    2472          72 :     if (!CallJSSetterOp(cx, shape->setterOp(), obj, id, &value, result))
    2473           0 :         return false;
    2474             : 
    2475             :     // Update any slot for the shape with the value produced by the setter,
    2476             :     // unless the setter deleted the shape.
    2477          84 :     if (shape->hasSlot() &&
    2478           6 :         (MOZ_LIKELY(cx->propertyRemovals == sample) ||
    2479           0 :          obj->contains(cx, shape)))
    2480             :     {
    2481           6 :         obj->setSlot(shape->slot(), value);
    2482             :     }
    2483             : 
    2484          72 :     return true;  // result is populated by CallJSSetterOp above.
    2485             : }
    2486             : 
    2487             : static bool
    2488           0 : CallSetPropertyHookAfterDefining(JSContext* cx, HandleObject receiver, HandleId id, HandleValue v,
    2489             :                                  ObjectOpResult& result)
    2490             : {
    2491           0 :     MOZ_ASSERT(receiver->is<NativeObject>());
    2492           0 :     MOZ_ASSERT(receiver->getClass()->getSetProperty());
    2493             : 
    2494           0 :     if (!result)
    2495           0 :         return true;
    2496             : 
    2497           0 :     Rooted<NativeObject*> nativeReceiver(cx, &receiver->as<NativeObject>());
    2498           0 :     MOZ_ASSERT(!cx->helperThread());
    2499           0 :     RootedValue receiverValue(cx, ObjectValue(*receiver));
    2500             : 
    2501             :     // This lookup is a bit unfortunate, but not nearly the most
    2502             :     // unfortunate thing about Class getters and setters. Since the above
    2503             :     // DefineProperty call succeeded, receiver is native, and the property
    2504             :     // has a setter (and thus can't be a dense element), this lookup is
    2505             :     // guaranteed to succeed.
    2506           0 :     RootedShape shape(cx, nativeReceiver->lookup(cx, id));
    2507           0 :     MOZ_ASSERT(shape);
    2508           0 :     return NativeSetExistingDataProperty(cx, nativeReceiver, shape, v, receiverValue, result);
    2509             : }
    2510             : 
    2511             : /*
    2512             :  * When a [[Set]] operation finds no existing property with the given id
    2513             :  * or finds a writable data property on the prototype chain, we end up here.
    2514             :  * Finish the [[Set]] by defining a new property on receiver.
    2515             :  *
    2516             :  * This implements ES6 draft rev 28, 9.1.9 [[Set]] steps 5.b-f, but it
    2517             :  * is really old code and there are a few barnacles.
    2518             :  */
    2519             : bool
    2520         431 : js::SetPropertyByDefining(JSContext* cx, HandleId id, HandleValue v, HandleValue receiverValue,
    2521             :                           ObjectOpResult& result)
    2522             : {
    2523             :     // Step 5.b.
    2524         431 :     if (!receiverValue.isObject())
    2525           0 :         return result.fail(JSMSG_SET_NON_OBJECT_RECEIVER);
    2526         862 :     RootedObject receiver(cx, &receiverValue.toObject());
    2527             : 
    2528             :     bool existing;
    2529             :     {
    2530             :         // Steps 5.c-d.
    2531         862 :         Rooted<PropertyDescriptor> desc(cx);
    2532         431 :         if (!GetOwnPropertyDescriptor(cx, receiver, id, &desc))
    2533           0 :             return false;
    2534             : 
    2535         431 :         existing = !!desc.object();
    2536             : 
    2537             :         // Step 5.e.
    2538         431 :         if (existing) {
    2539             :             // Step 5.e.i.
    2540           0 :             if (desc.isAccessorDescriptor())
    2541           0 :                 return result.fail(JSMSG_OVERWRITING_ACCESSOR);
    2542             : 
    2543             :             // Step 5.e.ii.
    2544           0 :             if (!desc.writable())
    2545           0 :                 return result.fail(JSMSG_READ_ONLY);
    2546             :         }
    2547             :     }
    2548             : 
    2549             :     // Invalidate SpiderMonkey-specific caches or bail.
    2550         431 :     const Class* clasp = receiver->getClass();
    2551             : 
    2552             :     // Purge the property cache of now-shadowed id in receiver's environment chain.
    2553         431 :     if (!PurgeEnvironmentChain(cx, receiver, id))
    2554           0 :         return false;
    2555             : 
    2556             :     // Steps 5.e.iii-iv. and 5.f.i. Define the new data property.
    2557             :     unsigned attrs =
    2558             :         existing
    2559         431 :         ? JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_PERMANENT
    2560         431 :         : JSPROP_ENUMERATE;
    2561         431 :     JSGetterOp getter = clasp->getGetProperty();
    2562         431 :     JSSetterOp setter = clasp->getSetProperty();
    2563         431 :     MOZ_ASSERT(getter != JS_PropertyStub);
    2564         431 :     MOZ_ASSERT(setter != JS_StrictPropertyStub);
    2565         431 :     if (!DefineProperty(cx, receiver, id, v, getter, setter, attrs, result))
    2566           0 :         return false;
    2567             : 
    2568             :     // If the receiver is native, there is one more legacy wrinkle: the class
    2569             :     // JSSetterOp is called after defining the new property.
    2570         431 :     if (setter && receiver->is<NativeObject>())
    2571           0 :         return CallSetPropertyHookAfterDefining(cx, receiver, id, v, result);
    2572             : 
    2573         431 :     return true;
    2574             : }
    2575             : 
    2576             : // When setting |id| for |receiver| and |obj| has no property for id, continue
    2577             : // the search up the prototype chain.
    2578             : bool
    2579           0 : js::SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
    2580             :                        HandleValue receiver, ObjectOpResult& result)
    2581             : {
    2582           0 :     MOZ_ASSERT(!obj->is<ProxyObject>());
    2583             : 
    2584           0 :     RootedObject proto(cx, obj->staticPrototype());
    2585           0 :     if (proto)
    2586           0 :         return SetProperty(cx, proto, id, v, receiver, result);
    2587             : 
    2588           0 :     return SetPropertyByDefining(cx, id, v, receiver, result);
    2589             : }
    2590             : 
    2591             : /*
    2592             :  * Implement "the rest of" assignment to a property when no property receiver[id]
    2593             :  * was found anywhere on the prototype chain.
    2594             :  *
    2595             :  * FIXME: This should be updated to follow ES6 draft rev 28, section 9.1.9,
    2596             :  * steps 4.d.i and 5.
    2597             :  */
    2598             : static bool
    2599        5702 : SetNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v,
    2600             :                        HandleValue receiver, QualifiedBool qualified, ObjectOpResult& result)
    2601             : {
    2602        5702 :     if (!qualified && receiver.isObject() && receiver.toObject().isUnqualifiedVarObj()) {
    2603           0 :         RootedString idStr(cx, JSID_TO_STRING(id));
    2604           0 :         if (!MaybeReportUndeclaredVarAssignment(cx, idStr))
    2605           0 :             return false;
    2606             :     }
    2607             : 
    2608             :     // Pure optimization for the common case. There's no point performing the
    2609             :     // lookup in step 5.c again, as our caller just did it for us.
    2610        5702 :     if (qualified && receiver.isObject() && obj == &receiver.toObject()) {
    2611             :         // Ensure that a custom GetOwnPropertyOp, if present, doesn't
    2612             :         // introduce additional properties which weren't previously found by
    2613             :         // LookupOwnProperty.
    2614             : #ifdef DEBUG
    2615        5665 :         if (GetOwnPropertyOp op = obj->getOpsGetOwnPropertyDescriptor()) {
    2616           0 :             Rooted<PropertyDescriptor> desc(cx);
    2617           0 :             if (!op(cx, obj, id, &desc))
    2618           0 :                 return false;
    2619             : 
    2620           0 :             MOZ_ASSERT(!desc.object());
    2621             :         }
    2622             : #endif
    2623             : 
    2624             :         // Step 5.e. Define the new data property.
    2625             : 
    2626        5665 :         const Class* clasp = obj->getClass();
    2627        5665 :         JSGetterOp getter = clasp->getGetProperty();
    2628        5665 :         JSSetterOp setter = clasp->getSetProperty();
    2629        5665 :         MOZ_ASSERT(getter != JS_PropertyStub);
    2630        5665 :         MOZ_ASSERT(setter != JS_StrictPropertyStub);
    2631             : 
    2632       11330 :         Rooted<PropertyDescriptor> desc(cx);
    2633        5665 :         desc.initFields(nullptr, v, JSPROP_ENUMERATE, getter, setter);
    2634             : 
    2635        5665 :         if (DefinePropertyOp op = obj->getOpsDefineProperty()) {
    2636             :             // Purge the property cache of now-shadowed id in receiver's environment chain.
    2637          17 :             if (!PurgeEnvironmentChain(cx, obj, id))
    2638           0 :                 return false;
    2639             : 
    2640          17 :             MOZ_ASSERT(!cx->helperThread());
    2641          17 :             if (!op(cx, obj, id, desc, result))
    2642           0 :                 return false;
    2643             :         } else {
    2644        5648 :             if (!DefineNonexistentProperty(cx, obj, id, desc, result))
    2645           0 :                 return false;
    2646             :         }
    2647             : 
    2648             :         // There is one more legacy wrinkle: the class JSSetterOp is called
    2649             :         // after defining the new property.
    2650        5665 :         if (setter)
    2651           0 :             return CallSetPropertyHookAfterDefining(cx, obj, id, v, result);
    2652             : 
    2653        5665 :         return true;
    2654             :     }
    2655             : 
    2656          37 :     return SetPropertyByDefining(cx, id, v, receiver, result);
    2657             : }
    2658             : 
    2659             : /*
    2660             :  * Set an existing own property obj[index] that's a dense element or typed
    2661             :  * array element.
    2662             :  */
    2663             : static bool
    2664         246 : SetDenseOrTypedArrayElement(JSContext* cx, HandleNativeObject obj, uint32_t index, HandleValue v,
    2665             :                             ObjectOpResult& result)
    2666             : {
    2667         246 :     if (obj->is<TypedArrayObject>()) {
    2668             :         double d;
    2669           0 :         if (!ToNumber(cx, v, &d))
    2670           0 :             return false;
    2671             : 
    2672             :         // Silently do nothing for out-of-bounds sets, for consistency with
    2673             :         // current behavior.  (ES6 currently says to throw for this in
    2674             :         // strict mode code, so we may eventually need to change.)
    2675           0 :         uint32_t len = obj->as<TypedArrayObject>().length();
    2676           0 :         if (index < len)
    2677           0 :             TypedArrayObject::setElement(obj->as<TypedArrayObject>(), index, d);
    2678           0 :         return result.succeed();
    2679             :     }
    2680             : 
    2681         246 :     if (WouldDefinePastNonwritableLength(obj, index))
    2682           0 :         return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH);
    2683             : 
    2684         246 :     if (!obj->maybeCopyElementsForWrite(cx))
    2685           0 :         return false;
    2686             : 
    2687         246 :     obj->setDenseElementWithType(cx, index, v);
    2688         246 :     return result.succeed();
    2689             : }
    2690             : 
    2691             : /*
    2692             :  * Finish the assignment `receiver[id] = v` when an existing property (shape)
    2693             :  * has been found on a native object (pobj). This implements ES6 draft rev 32
    2694             :  * (2015 Feb 2) 9.1.9 steps 5 and 6.
    2695             :  *
    2696             :  * It is necessary to pass both id and shape because shape could be an implicit
    2697             :  * dense or typed array element (i.e. not actually a pointer to a Shape).
    2698             :  */
    2699             : static bool
    2700        3965 : SetExistingProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v,
    2701             :                     HandleValue receiver, HandleNativeObject pobj, Handle<PropertyResult> prop,
    2702             :                     ObjectOpResult& result)
    2703             : {
    2704             :     // Step 5 for dense elements.
    2705        3965 :     if (prop.isDenseOrTypedArrayElement()) {
    2706             :         // Step 5.a.
    2707         246 :         if (pobj->getElementsHeader()->isFrozen())
    2708           0 :             return result.fail(JSMSG_READ_ONLY);
    2709             : 
    2710             :         // Pure optimization for the common case:
    2711         246 :         if (receiver.isObject() && pobj == &receiver.toObject())
    2712         246 :             return SetDenseOrTypedArrayElement(cx, pobj, JSID_TO_INT(id), v, result);
    2713             : 
    2714             :         // Steps 5.b-f.
    2715           0 :         return SetPropertyByDefining(cx, id, v, receiver, result);
    2716             :     }
    2717             : 
    2718             :     // Step 5 for all other properties.
    2719        7437 :     RootedShape shape(cx, prop.shape());
    2720        3719 :     if (shape->isDataDescriptor()) {
    2721             :         // Step 5.a.
    2722        3408 :         if (!shape->writable())
    2723           0 :             return result.fail(JSMSG_READ_ONLY);
    2724             : 
    2725             :         // steps 5.c-f.
    2726        3408 :         if (receiver.isObject() && pobj == &receiver.toObject()) {
    2727             :             // Pure optimization for the common case. There's no point performing
    2728             :             // the lookup in step 5.c again, as our caller just did it for us. The
    2729             :             // result is |shape|.
    2730             : 
    2731             :             // Steps 5.e.i-ii.
    2732        3014 :             if (pobj->is<ArrayObject>() && id == NameToId(cx->names().length)) {
    2733         140 :                 Rooted<ArrayObject*> arr(cx, &pobj->as<ArrayObject>());
    2734          70 :                 return ArraySetLength(cx, arr, id, shape->attributes(), v, result);
    2735             :             }
    2736        2944 :             return NativeSetExistingDataProperty(cx, pobj, shape, v, receiver, result);
    2737             :         }
    2738             : 
    2739             :         // SpiderMonkey special case: assigning to an inherited slotless
    2740             :         // property causes the setter to be called, instead of shadowing,
    2741             :         // unless the existing property is JSPROP_SHADOWABLE (see bug 552432).
    2742         394 :         if (!shape->hasSlot() && !shape->hasShadowable()) {
    2743             :             // Even weirder sub-special-case: inherited slotless data property
    2744             :             // with default setter. Wut.
    2745           0 :             if (shape->hasDefaultSetter())
    2746           0 :                 return result.succeed();
    2747             : 
    2748           0 :             RootedValue valCopy(cx, v);
    2749           0 :             return CallJSSetterOp(cx, shape->setterOp(), obj, id, &valCopy, result);
    2750             :         }
    2751             : 
    2752             :         // Shadow pobj[id] by defining a new data property receiver[id].
    2753             :         // Delegate everything to SetPropertyByDefining.
    2754         394 :         return SetPropertyByDefining(cx, id, v, receiver, result);
    2755             :     }
    2756             : 
    2757             :     // Steps 6-11.
    2758         311 :     MOZ_ASSERT(shape->isAccessorDescriptor());
    2759         311 :     MOZ_ASSERT_IF(!shape->hasSetterObject(), shape->hasDefaultSetter());
    2760         311 :     if (shape->hasDefaultSetter())
    2761          11 :         return result.fail(JSMSG_GETTER_ONLY);
    2762             : 
    2763         599 :     RootedValue setter(cx, ObjectValue(*shape->setterObject()));
    2764         300 :     if (!js::CallSetter(cx, receiver, setter, v))
    2765           0 :         return false;
    2766             : 
    2767         299 :     return result.succeed();
    2768             : }
    2769             : 
    2770             : bool
    2771        9711 : js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value,
    2772             :                       HandleValue receiver, QualifiedBool qualified, ObjectOpResult& result)
    2773             : {
    2774             :     // Fire watchpoints, if any.
    2775       19421 :     RootedValue v(cx, value);
    2776        9711 :     if (MOZ_UNLIKELY(obj->watched())) {
    2777           0 :         WatchpointMap* wpmap = cx->compartment()->watchpointMap;
    2778           0 :         if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, &v))
    2779           0 :             return false;
    2780             :     }
    2781             : 
    2782             :     // Step numbers below reference ES6 rev 27 9.1.9, the [[Set]] internal
    2783             :     // method for ordinary objects. We substitute our own names for these names
    2784             :     // used in the spec: O -> pobj, P -> id, ownDesc -> shape.
    2785       19421 :     Rooted<PropertyResult> prop(cx);
    2786       19421 :     RootedNativeObject pobj(cx, obj);
    2787             : 
    2788             :     // This loop isn't explicit in the spec algorithm. See the comment on step
    2789             :     // 4.c.i below. (There's a very similar loop in the NativeGetProperty
    2790             :     // implementation, but unfortunately not similar enough to common up.)
    2791             :     for (;;) {
    2792             :         // Steps 2-3. ('done' is a SpiderMonkey-specific thing, used below.)
    2793             :         bool done;
    2794       19325 :         if (!LookupOwnPropertyInline<CanGC>(cx, pobj, id, &prop, &done))
    2795        9710 :             return false;
    2796             : 
    2797       19325 :         if (prop) {
    2798             :             // Steps 5-6.
    2799        3965 :             return SetExistingProperty(cx, obj, id, v, receiver, pobj, prop, result);
    2800             :         }
    2801             : 
    2802             :         // Steps 4.a-b. The check for 'done' on this next line is tricky.
    2803             :         // done can be true in exactly these unlikely-sounding cases:
    2804             :         // - We're looking up an element, and pobj is a TypedArray that
    2805             :         //   doesn't have that many elements.
    2806             :         // - We're being called from a resolve hook to assign to the property
    2807             :         //   being resolved.
    2808             :         // What they all have in common is we do not want to keep walking
    2809             :         // the prototype chain.
    2810       24974 :         RootedObject proto(cx, done ? nullptr : pobj->staticPrototype());
    2811       15360 :         if (!proto) {
    2812             :             // Step 4.d.i (and step 5).
    2813        5702 :             return SetNonexistentProperty(cx, obj, id, v, receiver, qualified, result);
    2814             :         }
    2815             : 
    2816             :         // Step 4.c.i. If the prototype is also native, this step is a
    2817             :         // recursive tail call, and we don't need to go through all the
    2818             :         // plumbing of SetProperty; the top of the loop is where we're going to
    2819             :         // end up anyway. But if pobj is non-native, that optimization would be
    2820             :         // incorrect.
    2821        9658 :         if (!proto->isNative()) {
    2822             :             // Unqualified assignments are not specified to go through [[Set]]
    2823             :             // at all, but they do go through this function. So check for
    2824             :             // unqualified assignment to a nonexistent global (a strict error).
    2825          44 :             if (!qualified) {
    2826             :                 bool found;
    2827           0 :                 if (!HasProperty(cx, proto, id, &found))
    2828           0 :                     return false;
    2829           0 :                 if (!found)
    2830           0 :                     return SetNonexistentProperty(cx, obj, id, v, receiver, qualified, result);
    2831             :             }
    2832             : 
    2833          44 :             return SetProperty(cx, proto, id, v, receiver, result);
    2834             :         }
    2835        9614 :         pobj = &proto->as<NativeObject>();
    2836        9614 :     }
    2837             : }
    2838             : 
    2839             : bool
    2840           0 : js::NativeSetElement(JSContext* cx, HandleNativeObject obj, uint32_t index, HandleValue v,
    2841             :                      HandleValue receiver, ObjectOpResult& result)
    2842             : {
    2843           0 :     RootedId id(cx);
    2844           0 :     if (!IndexToId(cx, index, &id))
    2845           0 :         return false;
    2846           0 :     return NativeSetProperty(cx, obj, id, v, receiver, Qualified, result);
    2847             : }
    2848             : 
    2849             : /*** [[Delete]] **********************************************************************************/
    2850             : 
    2851             : // ES6 draft rev31 9.1.10 [[Delete]]
    2852             : bool
    2853         477 : js::NativeDeleteProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
    2854             :                          ObjectOpResult& result)
    2855             : {
    2856             :     // Steps 2-3.
    2857         954 :     Rooted<PropertyResult> prop(cx);
    2858         477 :     if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &prop))
    2859           0 :         return false;
    2860             : 
    2861             :     // Step 4.
    2862         477 :     if (!prop) {
    2863             :         // If no property call the class's delProperty hook, passing succeeded
    2864             :         // as the result parameter. This always succeeds when there is no hook.
    2865          57 :         return CallJSDeletePropertyOp(cx, obj->getClass()->getDelProperty(), obj, id, result);
    2866             :     }
    2867             : 
    2868             :     // Step 6. Non-configurable property.
    2869         420 :     if (GetPropertyAttributes(obj, prop) & JSPROP_PERMANENT)
    2870           0 :         return result.failCantDelete();
    2871             : 
    2872         420 :     if (!CallJSDeletePropertyOp(cx, obj->getClass()->getDelProperty(), obj, id, result))
    2873           0 :         return false;
    2874         420 :     if (!result)
    2875           0 :         return true;
    2876             : 
    2877             :     // Step 5.
    2878         420 :     if (prop.isDenseOrTypedArrayElement()) {
    2879             :         // Typed array elements are non-configurable.
    2880           0 :         MOZ_ASSERT(!obj->is<TypedArrayObject>());
    2881             : 
    2882           0 :         if (!obj->maybeCopyElementsForWrite(cx))
    2883           0 :             return false;
    2884             : 
    2885           0 :         obj->setDenseElementHole(cx, JSID_TO_INT(id));
    2886             :     } else {
    2887         420 :         if (!NativeObject::removeProperty(cx, obj, id))
    2888           0 :             return false;
    2889             :     }
    2890             : 
    2891         420 :     return SuppressDeletedProperty(cx, obj, id);
    2892             : }

Generated by: LCOV version 1.13