LCOV - code coverage report
Current view: top level - js/src - jsarray.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 940 1896 49.6 %
Date: 2017-07-14 16:53:18 Functions: 111 253 43.9 %
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 "jsarrayinlines.h"
       8             : 
       9             : #include "mozilla/ArrayUtils.h"
      10             : #include "mozilla/CheckedInt.h"
      11             : #include "mozilla/DebugOnly.h"
      12             : #include "mozilla/FloatingPoint.h"
      13             : #include "mozilla/MathAlgorithms.h"
      14             : 
      15             : #include <algorithm>
      16             : 
      17             : #include "jsapi.h"
      18             : #include "jsatom.h"
      19             : #include "jscntxt.h"
      20             : #include "jsfriendapi.h"
      21             : #include "jsfun.h"
      22             : #include "jsiter.h"
      23             : #include "jsnum.h"
      24             : #include "jsobj.h"
      25             : #include "jstypes.h"
      26             : #include "jsutil.h"
      27             : 
      28             : #include "ds/Sort.h"
      29             : #include "gc/Heap.h"
      30             : #include "jit/InlinableNatives.h"
      31             : #include "js/Class.h"
      32             : #include "js/Conversions.h"
      33             : #include "vm/ArgumentsObject.h"
      34             : #include "vm/Interpreter.h"
      35             : #include "vm/SelfHosting.h"
      36             : #include "vm/Shape.h"
      37             : #include "vm/StringBuffer.h"
      38             : #include "vm/TypedArrayObject.h"
      39             : #include "vm/WrapperObject.h"
      40             : 
      41             : #include "jsatominlines.h"
      42             : 
      43             : #include "vm/ArgumentsObject-inl.h"
      44             : #include "vm/ArrayObject-inl.h"
      45             : #include "vm/Caches-inl.h"
      46             : #include "vm/Interpreter-inl.h"
      47             : #include "vm/NativeObject-inl.h"
      48             : #include "vm/UnboxedObject-inl.h"
      49             : 
      50             : using namespace js;
      51             : using namespace js::gc;
      52             : 
      53             : using mozilla::Abs;
      54             : using mozilla::ArrayLength;
      55             : using mozilla::CeilingLog2;
      56             : using mozilla::CheckedInt;
      57             : using mozilla::DebugOnly;
      58             : using mozilla::IsNaN;
      59             : 
      60             : using JS::AutoCheckCannotGC;
      61             : using JS::IsArrayAnswer;
      62             : using JS::ToUint32;
      63             : 
      64             : static inline bool
      65        2489 : IsBoxedOrUnboxedArray(const JSObject* obj)
      66             : {
      67        2489 :     return obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>();
      68             : }
      69             : 
      70             : bool
      71         466 : JS::IsArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer)
      72             : {
      73         466 :     if (IsBoxedOrUnboxedArray(obj)) {
      74         315 :         *answer = IsArrayAnswer::Array;
      75         315 :         return true;
      76             :     }
      77             : 
      78         151 :     if (obj->is<ProxyObject>())
      79          55 :         return Proxy::isArray(cx, obj, answer);
      80             : 
      81          96 :     *answer = IsArrayAnswer::NotArray;
      82          96 :     return true;
      83             : }
      84             : 
      85             : bool
      86         415 : JS::IsArray(JSContext* cx, HandleObject obj, bool* isArray)
      87             : {
      88             :     IsArrayAnswer answer;
      89         415 :     if (!IsArray(cx, obj, &answer))
      90           0 :         return false;
      91             : 
      92         415 :     if (answer == IsArrayAnswer::RevokedProxy) {
      93           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
      94           0 :         return false;
      95             :     }
      96             : 
      97         415 :     *isArray = answer == IsArrayAnswer::Array;
      98         415 :     return true;
      99             : }
     100             : 
     101             : // ES2017 7.1.15 ToLength, but clamped to the [0,2^32-2] range.
     102             : static bool
     103          59 : ToLengthClamped(JSContext* cx, HandleValue v, uint32_t* out)
     104             : {
     105          59 :     if (v.isInt32()) {
     106          59 :         int32_t i = v.toInt32();
     107          59 :         *out = i < 0 ? 0 : i;
     108          59 :         return true;
     109             :     }
     110             :     double d;
     111           0 :     if (v.isDouble()) {
     112           0 :         d = v.toDouble();
     113             :     } else {
     114           0 :         if (!ToNumber(cx, v, &d))
     115           0 :             return false;
     116             :     }
     117           0 :     d = JS::ToInteger(d);
     118           0 :     if (d <= 0.0)
     119           0 :         *out = 0;
     120           0 :     else if (d < double(UINT32_MAX - 1))
     121           0 :         *out = uint32_t(d);
     122             :     else
     123           0 :         *out = UINT32_MAX;
     124           0 :     return true;
     125             : }
     126             : 
     127             : bool
     128        1168 : js::GetLengthProperty(JSContext* cx, HandleObject obj, uint32_t* lengthp)
     129             : {
     130        1168 :     if (obj->is<ArrayObject>()) {
     131        1109 :         *lengthp = obj->as<ArrayObject>().length();
     132        1109 :         return true;
     133             :     }
     134             : 
     135          59 :     if (obj->is<UnboxedArrayObject>()) {
     136           0 :         *lengthp = obj->as<UnboxedArrayObject>().length();
     137           0 :         return true;
     138             :     }
     139             : 
     140          59 :     if (obj->is<ArgumentsObject>()) {
     141          17 :         ArgumentsObject& argsobj = obj->as<ArgumentsObject>();
     142          17 :         if (!argsobj.hasOverriddenLength()) {
     143           0 :             *lengthp = argsobj.initialLength();
     144           0 :             return true;
     145             :         }
     146             :     }
     147             : 
     148         118 :     RootedValue value(cx);
     149          59 :     if (!GetProperty(cx, obj, obj, cx->names().length, &value))
     150           0 :         return false;
     151             : 
     152          59 :     if (!ToLengthClamped(cx, value, lengthp))
     153           0 :         return false;
     154             : 
     155          59 :     return true;
     156             : }
     157             : 
     158             : // ES2017 7.1.15 ToLength.
     159             : static bool
     160           2 : ToLength(JSContext* cx, HandleValue v, uint64_t* out)
     161             : {
     162           2 :     if (v.isInt32()) {
     163           2 :         int32_t i = v.toInt32();
     164           2 :         *out = i < 0 ? 0 : i;
     165           2 :         return true;
     166             :     }
     167             : 
     168             :     double d;
     169           0 :     if (v.isDouble()) {
     170           0 :         d = v.toDouble();
     171             :     } else {
     172           0 :         if (!ToNumber(cx, v, &d))
     173           0 :             return false;
     174             :     }
     175             : 
     176           0 :     d = JS::ToInteger(d);
     177           0 :     if (d <= 0.0)
     178           0 :         *out = 0;
     179             :     else
     180           0 :         *out = uint64_t(Min(d, DOUBLE_INTEGRAL_PRECISION_LIMIT - 1));
     181           0 :     return true;
     182             : }
     183             : 
     184             : static bool
     185        2348 : GetLengthProperty(JSContext* cx, HandleObject obj, uint64_t* lengthp)
     186             : {
     187        2348 :     if (obj->is<ArrayObject>()) {
     188        2297 :         *lengthp = obj->as<ArrayObject>().length();
     189        2297 :         return true;
     190             :     }
     191             : 
     192          51 :     if (obj->is<UnboxedArrayObject>()) {
     193           0 :         *lengthp = obj->as<UnboxedArrayObject>().length();
     194           0 :         return true;
     195             :     }
     196             : 
     197          51 :     if (obj->is<ArgumentsObject>()) {
     198          49 :         ArgumentsObject& argsobj = obj->as<ArgumentsObject>();
     199          49 :         if (!argsobj.hasOverriddenLength()) {
     200          49 :             *lengthp = argsobj.initialLength();
     201          49 :             return true;
     202             :         }
     203             :     }
     204             : 
     205           4 :     RootedValue value(cx);
     206           2 :     if (!GetProperty(cx, obj, obj, cx->names().length, &value))
     207           0 :         return false;
     208             : 
     209           2 :     return ToLength(cx, value, lengthp);
     210             : }
     211             : 
     212             : /*
     213             :  * Determine if the id represents an array index.
     214             :  *
     215             :  * An id is an array index according to ECMA by (15.4):
     216             :  *
     217             :  * "Array objects give special treatment to a certain class of property names.
     218             :  * A property name P (in the form of a string value) is an array index if and
     219             :  * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
     220             :  * to 2^32-1."
     221             :  *
     222             :  * This means the largest allowed index is actually 2^32-2 (4294967294).
     223             :  *
     224             :  * In our implementation, it would be sufficient to check for id.isInt32()
     225             :  * except that by using signed 31-bit integers we miss the top half of the
     226             :  * valid range. This function checks the string representation itself; note
     227             :  * that calling a standard conversion routine might allow strings such as
     228             :  * "08" or "4.0" as array indices, which they are not.
     229             :  *
     230             :  */
     231             : template <typename CharT>
     232             : static bool
     233         337 : StringIsArrayIndex(const CharT* s, uint32_t length, uint32_t* indexp)
     234             : {
     235         337 :     const CharT* end = s + length;
     236             : 
     237         337 :     if (length == 0 || length > (sizeof("4294967294") - 1) || !JS7_ISDEC(*s))
     238         337 :         return false;
     239             : 
     240           0 :     uint32_t c = 0, previous = 0;
     241           0 :     uint32_t index = JS7_UNDEC(*s++);
     242             : 
     243             :     /* Don't allow leading zeros. */
     244           0 :     if (index == 0 && s != end)
     245           0 :         return false;
     246             : 
     247           0 :     for (; s < end; s++) {
     248           0 :         if (!JS7_ISDEC(*s))
     249           0 :             return false;
     250             : 
     251           0 :         previous = index;
     252           0 :         c = JS7_UNDEC(*s);
     253           0 :         index = 10 * index + c;
     254             :     }
     255             : 
     256             :     /* Make sure we didn't overflow. */
     257           0 :     if (previous < (MAX_ARRAY_INDEX / 10) || (previous == (MAX_ARRAY_INDEX / 10) &&
     258             :         c <= (MAX_ARRAY_INDEX % 10))) {
     259           0 :         MOZ_ASSERT(index <= MAX_ARRAY_INDEX);
     260           0 :         *indexp = index;
     261           0 :         return true;
     262             :     }
     263             : 
     264           0 :     return false;
     265             : }
     266             : 
     267             : JS_FRIEND_API(bool)
     268         337 : js::StringIsArrayIndex(JSLinearString* str, uint32_t* indexp)
     269             : {
     270         674 :     AutoCheckCannotGC nogc;
     271         337 :     return str->hasLatin1Chars()
     272         337 :            ? ::StringIsArrayIndex(str->latin1Chars(nogc), str->length(), indexp)
     273         674 :            : ::StringIsArrayIndex(str->twoByteChars(nogc), str->length(), indexp);
     274             : }
     275             : 
     276             : template <typename T>
     277             : static bool
     278             : ToId(JSContext* cx, T index, MutableHandleId id);
     279             : 
     280             : template <>
     281             : bool
     282           0 : ToId(JSContext* cx, uint32_t index, MutableHandleId id)
     283             : {
     284           0 :     return IndexToId(cx, index, id);
     285             : }
     286             : 
     287             : template <>
     288             : bool
     289          95 : ToId(JSContext* cx, uint64_t index, MutableHandleId id)
     290             : {
     291          95 :     MOZ_ASSERT(index < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT));
     292             : 
     293          95 :     if (index == uint32_t(index))
     294          95 :         return IndexToId(cx, uint32_t(index), id);
     295             : 
     296           0 :     Value tmp = DoubleValue(index);
     297           0 :     return ValueToId<CanGC>(cx, HandleValue::fromMarkedLocation(&tmp), id);
     298             : }
     299             : 
     300             : /*
     301             :  * If the property at the given index exists, get its value into |vp| and set
     302             :  * |*hole| to false. Otherwise set |*hole| to true and |vp| to Undefined.
     303             :  */
     304             : template <typename T>
     305             : static bool
     306         113 : HasAndGetElement(JSContext* cx, HandleObject obj, HandleObject receiver, T index, bool* hole,
     307             :                  MutableHandleValue vp)
     308             : {
     309         113 :     if (index < GetAnyBoxedOrUnboxedInitializedLength(obj)) {
     310           9 :         vp.set(GetAnyBoxedOrUnboxedDenseElement(obj, size_t(index)));
     311           9 :         if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
     312           9 :             *hole = false;
     313           9 :             return true;
     314             :         }
     315             :     }
     316         104 :     if (obj->is<ArgumentsObject>() && index <= UINT32_MAX) {
     317          75 :         if (obj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) {
     318          75 :             *hole = false;
     319          75 :             return true;
     320             :         }
     321             :     }
     322             : 
     323          58 :     RootedId id(cx);
     324          29 :     if (!ToId(cx, index, &id))
     325           0 :         return false;
     326             : 
     327             :     bool found;
     328          29 :     if (!HasProperty(cx, obj, id, &found))
     329           0 :         return false;
     330             : 
     331          29 :     if (found) {
     332           0 :         if (!GetProperty(cx, obj, receiver, id, vp))
     333           0 :             return false;
     334             :     } else {
     335          29 :         vp.setUndefined();
     336             :     }
     337          29 :     *hole = !found;
     338          29 :     return true;
     339             : }
     340             : 
     341             : template <typename T>
     342             : static inline bool
     343         113 : HasAndGetElement(JSContext* cx, HandleObject obj, T index, bool* hole, MutableHandleValue vp)
     344             : {
     345         113 :     return HasAndGetElement(cx, obj, obj, index, hole, vp);
     346             : }
     347             : 
     348             : bool
     349          66 : ElementAdder::append(JSContext* cx, HandleValue v)
     350             : {
     351          66 :     MOZ_ASSERT(index_ < length_);
     352          66 :     if (resObj_) {
     353             :         DenseElementResult result =
     354           0 :             SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, resObj_, index_, v.address(), 1);
     355           0 :         if (result == DenseElementResult::Failure)
     356           0 :             return false;
     357           0 :         if (result == DenseElementResult::Incomplete) {
     358           0 :             if (!DefineElement(cx, resObj_, index_, v))
     359           0 :                 return false;
     360             :         }
     361             :     } else {
     362          66 :         vp_[index_] = v;
     363             :     }
     364          66 :     index_++;
     365          66 :     return true;
     366             : }
     367             : 
     368             : void
     369           0 : ElementAdder::appendHole()
     370             : {
     371           0 :     MOZ_ASSERT(getBehavior_ == ElementAdder::CheckHasElemPreserveHoles);
     372           0 :     MOZ_ASSERT(index_ < length_);
     373           0 :     if (!resObj_)
     374           0 :         vp_[index_].setMagic(JS_ELEMENTS_HOLE);
     375           0 :     index_++;
     376           0 : }
     377             : 
     378             : bool
     379          35 : js::GetElementsWithAdder(JSContext* cx, HandleObject obj, HandleObject receiver,
     380             :                          uint32_t begin, uint32_t end, ElementAdder* adder)
     381             : {
     382          35 :     MOZ_ASSERT(begin <= end);
     383             : 
     384          70 :     RootedValue val(cx);
     385         101 :     for (uint32_t i = begin; i < end; i++) {
     386          66 :         if (adder->getBehavior() == ElementAdder::CheckHasElemPreserveHoles) {
     387             :             bool hole;
     388           0 :             if (!HasAndGetElement(cx, obj, receiver, i, &hole, &val))
     389           0 :                 return false;
     390           0 :             if (hole) {
     391           0 :                 adder->appendHole();
     392           0 :                 continue;
     393             :             }
     394             :         } else {
     395          66 :             MOZ_ASSERT(adder->getBehavior() == ElementAdder::GetElement);
     396          66 :             if (!GetElement(cx, obj, receiver, i, &val))
     397           0 :                 return false;
     398             :         }
     399          66 :         if (!adder->append(cx, val))
     400           0 :             return false;
     401             :     }
     402             : 
     403          35 :     return true;
     404             : }
     405             : 
     406             : template <JSValueType Type>
     407             : DenseElementResult
     408         103 : GetBoxedOrUnboxedDenseElements(JSObject* aobj, uint32_t length, Value* vp)
     409             : {
     410         103 :     MOZ_ASSERT(!ObjectMayHaveExtraIndexedProperties(aobj));
     411             : 
     412         103 :     if (length > GetBoxedOrUnboxedInitializedLength<Type>(aobj))
     413           0 :         return DenseElementResult::Incomplete;
     414             : 
     415         387 :     for (size_t i = 0; i < length; i++) {
     416         284 :         vp[i] = GetBoxedOrUnboxedDenseElement<Type>(aobj, i);
     417             : 
     418             :         // No other indexed properties so hole => undefined.
     419         284 :         if (vp[i].isMagic(JS_ELEMENTS_HOLE))
     420           0 :             vp[i] = UndefinedValue();
     421             :     }
     422             : 
     423         103 :     return DenseElementResult::Success;
     424             : }
     425             : 
     426         206 : DefineBoxedOrUnboxedFunctor3(GetBoxedOrUnboxedDenseElements,
     427             :                              JSObject*, uint32_t, Value*);
     428             : 
     429             : bool
     430         155 : js::GetElements(JSContext* cx, HandleObject aobj, uint32_t length, Value* vp)
     431             : {
     432         155 :     if (!ObjectMayHaveExtraIndexedProperties(aobj)) {
     433         103 :         GetBoxedOrUnboxedDenseElementsFunctor functor(aobj, length, vp);
     434         103 :         DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, aobj);
     435         103 :         if (result != DenseElementResult::Incomplete)
     436         103 :             return result == DenseElementResult::Success;
     437             :     }
     438             : 
     439          52 :     if (aobj->is<ArgumentsObject>()) {
     440          17 :         ArgumentsObject& argsobj = aobj->as<ArgumentsObject>();
     441          17 :         if (!argsobj.hasOverriddenLength()) {
     442           0 :             if (argsobj.maybeGetElements(0, length, vp))
     443           0 :                 return true;
     444             :         }
     445             :     }
     446             : 
     447          52 :     if (js::GetElementsOp op = aobj->getOpsGetElements()) {
     448          70 :         ElementAdder adder(cx, vp, length, ElementAdder::GetElement);
     449          35 :         return op(cx, aobj, 0, length, &adder);
     450             :     }
     451             : 
     452          83 :     for (uint32_t i = 0; i < length; i++) {
     453          66 :         if (!GetElement(cx, aobj, aobj, i, MutableHandleValue::fromMarkedLocation(&vp[i])))
     454           0 :             return false;
     455             :     }
     456             : 
     457          17 :     return true;
     458             : }
     459             : 
     460             : static inline bool
     461           1 : GetArrayElement(JSContext* cx, HandleObject obj, uint64_t index, MutableHandleValue vp)
     462             : {
     463           1 :     if (index < GetAnyBoxedOrUnboxedInitializedLength(obj)) {
     464           1 :         vp.set(GetAnyBoxedOrUnboxedDenseElement(obj, size_t(index)));
     465           1 :         if (!vp.isMagic(JS_ELEMENTS_HOLE))
     466           1 :             return true;
     467             :     }
     468             : 
     469           0 :     if (obj->is<ArgumentsObject>() && index <= UINT32_MAX) {
     470           0 :         if (obj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp))
     471           0 :             return true;
     472             :     }
     473             : 
     474           0 :     RootedId id(cx);
     475           0 :     if (!ToId(cx, index, &id))
     476           0 :         return false;
     477           0 :     return GetProperty(cx, obj, obj, id, vp);
     478             : }
     479             : 
     480             : static inline bool
     481           0 : DefineArrayElement(JSContext* cx, HandleObject obj, uint64_t index, HandleValue value)
     482             : {
     483           0 :     RootedId id(cx);
     484           0 :     if (!ToId(cx, index, &id))
     485           0 :         return false;
     486           0 :     return DefineProperty(cx, obj, id, value);
     487             : }
     488             : 
     489             : // Set the value of the property at the given index to v.
     490             : static inline bool
     491          49 : SetArrayElement(JSContext* cx, HandleObject obj, uint64_t index, HandleValue v)
     492             : {
     493          98 :     RootedId id(cx);
     494          49 :     if (!ToId(cx, index, &id))
     495           0 :         return false;
     496             : 
     497          49 :     return SetProperty(cx, obj, id, v);
     498             : }
     499             : 
     500             : /*
     501             :  * Attempt to delete the element |index| from |obj| as if by
     502             :  * |obj.[[Delete]](index)|.
     503             :  *
     504             :  * If an error occurs while attempting to delete the element (that is, the call
     505             :  * to [[Delete]] threw), return false.
     506             :  *
     507             :  * Otherwise call result.succeed() or result.fail() to indicate whether the
     508             :  * deletion attempt succeeded (that is, whether the call to [[Delete]] returned
     509             :  * true or false).  (Deletes generally fail only when the property is
     510             :  * non-configurable, but proxies may implement different semantics.)
     511             :  */
     512             : static bool
     513          31 : DeleteArrayElement(JSContext* cx, HandleObject obj, uint64_t index, ObjectOpResult& result)
     514             : {
     515          62 :     if (obj->is<ArrayObject>() && !obj->isIndexed() &&
     516          31 :         !obj->as<NativeObject>().denseElementsAreFrozen())
     517             :     {
     518          31 :         ArrayObject* aobj = &obj->as<ArrayObject>();
     519          31 :         if (index <= UINT32_MAX) {
     520          31 :             uint32_t idx = uint32_t(index);
     521          31 :             if (idx < aobj->getDenseInitializedLength()) {
     522           1 :                 if (!aobj->maybeCopyElementsForWrite(cx))
     523           0 :                     return false;
     524           1 :                 if (idx+1 == aobj->getDenseInitializedLength()) {
     525           1 :                     aobj->setDenseInitializedLength(idx);
     526             :                 } else {
     527           0 :                     aobj->markDenseElementsNotPacked(cx);
     528           0 :                     aobj->setDenseElement(idx, MagicValue(JS_ELEMENTS_HOLE));
     529             :                 }
     530           1 :                 if (!SuppressDeletedElement(cx, obj, idx))
     531           0 :                     return false;
     532             :             }
     533             :         }
     534             : 
     535          31 :         return result.succeed();
     536             :     }
     537             : 
     538           0 :     RootedId id(cx);
     539           0 :     if (!ToId(cx, index, &id))
     540           0 :         return false;
     541           0 :     return DeleteProperty(cx, obj, id, result);
     542             : }
     543             : 
     544             : /* ES6 draft rev 32 (2 Febr 2015) 7.3.7 */
     545             : static bool
     546          31 : DeletePropertyOrThrow(JSContext* cx, HandleObject obj, uint64_t index)
     547             : {
     548          31 :     ObjectOpResult success;
     549          31 :     if (!DeleteArrayElement(cx, obj, index, success))
     550           0 :         return false;
     551          31 :     if (!success) {
     552           0 :         RootedId id(cx);
     553           0 :         if (!ToId(cx, index, &id))
     554           0 :             return false;
     555           0 :         return success.reportError(cx, obj, id);
     556             :     }
     557          31 :     return true;
     558             : }
     559             : 
     560             : static bool
     561          62 : SetLengthProperty(JSContext* cx, HandleObject obj, uint64_t length)
     562             : {
     563          62 :     MOZ_ASSERT(length < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT));
     564             : 
     565         124 :     RootedValue v(cx, NumberValue(length));
     566         124 :     return SetProperty(cx, obj, cx->names().length, v);
     567             : }
     568             : 
     569             : bool
     570           0 : js::SetLengthProperty(JSContext* cx, HandleObject obj, uint32_t length)
     571             : {
     572           0 :     RootedValue v(cx, NumberValue(length));
     573           0 :     return SetProperty(cx, obj, cx->names().length, v);
     574             : }
     575             : 
     576             : static bool
     577         593 : array_length_getter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
     578             : {
     579         593 :     vp.setNumber(obj->as<ArrayObject>().length());
     580         593 :     return true;
     581             : }
     582             : 
     583             : static bool
     584           0 : array_length_setter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
     585             :                     ObjectOpResult& result)
     586             : {
     587           0 :     if (!obj->is<ArrayObject>()) {
     588             :         // This array .length property was found on the prototype
     589             :         // chain. Ideally the setter should not have been called, but since
     590             :         // we're here, do an impression of SetPropertyByDefining.
     591           0 :         const Class* clasp = obj->getClass();
     592           0 :         return DefineProperty(cx, obj, cx->names().length, vp,
     593             :                               clasp->getGetProperty(), clasp->getSetProperty(),
     594           0 :                               JSPROP_ENUMERATE, result);
     595             :     }
     596             : 
     597           0 :     Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
     598           0 :     MOZ_ASSERT(arr->lengthIsWritable(),
     599             :                "setter shouldn't be called if property is non-writable");
     600             : 
     601           0 :     return ArraySetLength(cx, arr, id, JSPROP_PERMANENT, vp, result);
     602             : }
     603             : 
     604             : struct ReverseIndexComparator
     605             : {
     606           0 :     bool operator()(const uint32_t& a, const uint32_t& b, bool* lessOrEqualp) {
     607           0 :         MOZ_ASSERT(a != b, "how'd we get duplicate indexes?");
     608           0 :         *lessOrEqualp = b <= a;
     609           0 :         return true;
     610             :     }
     611             : };
     612             : 
     613             : static bool
     614          53 : MaybeInIteration(HandleObject obj, JSContext* cx)
     615             : {
     616             :     /*
     617             :      * Don't optimize if the array might be in the midst of iteration.  We
     618             :      * rely on this to be able to safely move dense array elements around with
     619             :      * just a memmove (see NativeObject::moveDenseArrayElements), without worrying
     620             :      * about updating any in-progress enumerators for properties implicitly
     621             :      * deleted if a hole is moved from one location to another location not yet
     622             :      * visited.  See bug 690622.
     623             :      *
     624             :      * Note that it's fine to optimize if |obj| is on the prototype of another
     625             :      * object: SuppressDeletedProperty only suppresses properties deleted from
     626             :      * the iterated object itself.
     627             :      */
     628             : 
     629          53 :     if (MOZ_LIKELY(!cx->compartment()->objectMaybeInIteration(obj)))
     630          53 :         return false;
     631             : 
     632           0 :     ObjectGroup* group = JSObject::getGroup(cx, obj);
     633           0 :     if (MOZ_UNLIKELY(!group)) {
     634           0 :         cx->recoverFromOutOfMemory();
     635           0 :         return true;
     636             :     }
     637             : 
     638           0 :     if (MOZ_UNLIKELY(group->hasAllFlags(OBJECT_FLAG_ITERATED)))
     639           0 :         return true;
     640             : 
     641           0 :     return false;
     642             : }
     643             : 
     644             : bool
     645         123 : js::CanonicalizeArrayLengthValue(JSContext* cx, HandleValue v, uint32_t* newLen)
     646             : {
     647             :     double d;
     648             : 
     649         123 :     if (!ToUint32(cx, v, newLen))
     650           0 :         return false;
     651             : 
     652         123 :     if (!ToNumber(cx, v, &d))
     653           0 :         return false;
     654             : 
     655         123 :     if (d == *newLen)
     656         123 :         return true;
     657             : 
     658           0 :     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
     659           0 :     return false;
     660             : }
     661             : 
     662             : /* ES6 draft rev 34 (2015 Feb 20) 9.4.2.4 ArraySetLength */
     663             : bool
     664         123 : js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
     665             :                    unsigned attrs, HandleValue value, ObjectOpResult& result)
     666             : {
     667         123 :     MOZ_ASSERT(id == NameToId(cx->names().length));
     668             : 
     669         123 :     if (!arr->maybeCopyElementsForWrite(cx))
     670           0 :         return false;
     671             : 
     672             :     // Step 1.
     673             :     uint32_t newLen;
     674         123 :     if (attrs & JSPROP_IGNORE_VALUE) {
     675           0 :         MOZ_ASSERT(value.isUndefined());
     676             : 
     677             :         // The spec has us calling OrdinaryDefineOwnProperty if
     678             :         // Desc.[[Value]] is absent, but our implementation is so different that
     679             :         // this is impossible. Instead, set newLen to the current length and
     680             :         // proceed to step 9.
     681           0 :         newLen = arr->length();
     682             :     } else {
     683             :         // Step 2 is irrelevant in our implementation.
     684             : 
     685             :         // Steps 3-7.
     686         123 :         if (!CanonicalizeArrayLengthValue(cx, value, &newLen))
     687           0 :             return false;
     688             : 
     689             :         // Step 8 is irrelevant in our implementation.
     690             :     }
     691             : 
     692             :     // Steps 9-11.
     693         123 :     bool lengthIsWritable = arr->lengthIsWritable();
     694             : #ifdef DEBUG
     695             :     {
     696         246 :         RootedShape lengthShape(cx, arr->lookupPure(id));
     697         123 :         MOZ_ASSERT(lengthShape);
     698         123 :         MOZ_ASSERT(lengthShape->writable() == lengthIsWritable);
     699             :     }
     700             : #endif
     701         123 :     uint32_t oldLen = arr->length();
     702             : 
     703             :     // Part of steps 1.a, 12.a, and 16: Fail if we're being asked to change
     704             :     // enumerability or configurability, or otherwise break the object
     705             :     // invariants. (ES6 checks these by calling OrdinaryDefineOwnProperty, but
     706             :     // in SM, the array length property is hardly ordinary.)
     707         246 :     if ((attrs & (JSPROP_PERMANENT | JSPROP_IGNORE_PERMANENT)) == 0 ||
     708         246 :         (attrs & (JSPROP_ENUMERATE | JSPROP_IGNORE_ENUMERATE)) == JSPROP_ENUMERATE ||
     709         246 :         (attrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0 ||
     710         123 :         (!lengthIsWritable && (attrs & (JSPROP_READONLY | JSPROP_IGNORE_READONLY)) == 0))
     711             :     {
     712           0 :         return result.fail(JSMSG_CANT_REDEFINE_PROP);
     713             :     }
     714             : 
     715             :     // Steps 12-13 for arrays with non-writable length.
     716         123 :     if (!lengthIsWritable) {
     717           0 :         if (newLen == oldLen)
     718           0 :             return result.succeed();
     719             : 
     720           0 :         return result.fail(JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
     721             :     }
     722             : 
     723             :     // Step 19.
     724         123 :     bool succeeded = true;
     725             :     do {
     726             :         // The initialized length and capacity of an array only need updating
     727             :         // when non-hole elements are added or removed, which doesn't happen
     728             :         // when array length stays the same or increases.
     729         123 :         if (newLen >= oldLen)
     730         112 :             break;
     731             : 
     732             :         // Attempt to propagate dense-element optimization tricks, if possible,
     733             :         // and avoid the generic (and accordingly slow) deletion code below.
     734             :         // We can only do this if there are only densely-indexed elements.
     735             :         // Once there's a sparse indexed element, there's no good way to know,
     736             :         // save by enumerating all the properties to find it.  But we *have* to
     737             :         // know in case that sparse indexed element is non-configurable, as
     738             :         // that element must prevent any deletions below it.  Bug 586842 should
     739             :         // fix this inefficiency by moving indexed storage to be entirely
     740             :         // separate from non-indexed storage.
     741             :         // A second reason for this optimization to be invalid is an active
     742             :         // for..in iteration over the array. Keys deleted before being reached
     743             :         // during the iteration must not be visited, and suppressing them here
     744             :         // would be too costly.
     745          11 :         if (!arr->isIndexed() && !MaybeInIteration(arr, cx)) {
     746          11 :             if (!arr->maybeCopyElementsForWrite(cx))
     747           0 :                 return false;
     748             : 
     749          11 :             uint32_t oldCapacity = arr->getDenseCapacity();
     750          11 :             uint32_t oldInitializedLength = arr->getDenseInitializedLength();
     751          11 :             MOZ_ASSERT(oldCapacity >= oldInitializedLength);
     752          11 :             if (oldInitializedLength > newLen)
     753           0 :                 arr->setDenseInitializedLength(newLen);
     754          11 :             if (oldCapacity > newLen)
     755           9 :                 arr->shrinkElements(cx, newLen);
     756             : 
     757             :             // We've done the work of deleting any dense elements needing
     758             :             // deletion, and there are no sparse elements.  Thus we can skip
     759             :             // straight to defining the length.
     760          11 :             break;
     761             :         }
     762             : 
     763             :         // Step 15.
     764             :         //
     765             :         // Attempt to delete all elements above the new length, from greatest
     766             :         // to least.  If any of these deletions fails, we're supposed to define
     767             :         // the length to one greater than the index that couldn't be deleted,
     768             :         // *with the property attributes specified*.  This might convert the
     769             :         // length to be not the value specified, yet non-writable.  (You may be
     770             :         // forgiven for thinking these are interesting semantics.)  Example:
     771             :         //
     772             :         //   var arr =
     773             :         //     Object.defineProperty([0, 1, 2, 3], 1, { writable: false });
     774             :         //   Object.defineProperty(arr, "length",
     775             :         //                         { value: 0, writable: false });
     776             :         //
     777             :         // will convert |arr| to an array of non-writable length two, then
     778             :         // throw a TypeError.
     779             :         //
     780             :         // We implement this behavior, in the relevant lops below, by setting
     781             :         // |succeeded| to false.  Then we exit the loop, define the length
     782             :         // appropriately, and only then throw a TypeError, if necessary.
     783           0 :         uint32_t gap = oldLen - newLen;
     784           0 :         const uint32_t RemoveElementsFastLimit = 1 << 24;
     785           0 :         if (gap < RemoveElementsFastLimit) {
     786             :             // If we're removing a relatively small number of elements, just do
     787             :             // it exactly by the spec.
     788           0 :             while (newLen < oldLen) {
     789             :                 // Step 15a.
     790           0 :                 oldLen--;
     791             : 
     792             :                 // Steps 15b-d.
     793           0 :                 ObjectOpResult deleteSucceeded;
     794           0 :                 if (!DeleteElement(cx, arr, oldLen, deleteSucceeded))
     795           0 :                     return false;
     796           0 :                 if (!deleteSucceeded) {
     797           0 :                     newLen = oldLen + 1;
     798           0 :                     succeeded = false;
     799           0 :                     break;
     800             :                 }
     801             :             }
     802             :         } else {
     803             :             // If we're removing a large number of elements from an array
     804             :             // that's probably sparse, try a different tack.  Get all the own
     805             :             // property names, sift out the indexes in the deletion range into
     806             :             // a vector, sort the vector greatest to least, then delete the
     807             :             // indexes greatest to least using that vector.  See bug 322135.
     808             :             //
     809             :             // This heuristic's kind of a huge guess -- "large number of
     810             :             // elements" and "probably sparse" are completely unprincipled
     811             :             // predictions.  In the long run, bug 586842 will support the right
     812             :             // fix: store sparse elements in a sorted data structure that
     813             :             // permits fast in-reverse-order traversal and concurrent removals.
     814             : 
     815           0 :             Vector<uint32_t> indexes(cx);
     816             :             {
     817           0 :                 AutoIdVector props(cx);
     818           0 :                 if (!GetPropertyKeys(cx, arr, JSITER_OWNONLY | JSITER_HIDDEN, &props))
     819           0 :                     return false;
     820             : 
     821           0 :                 for (size_t i = 0; i < props.length(); i++) {
     822           0 :                     if (!CheckForInterrupt(cx))
     823           0 :                         return false;
     824             : 
     825             :                     uint32_t index;
     826           0 :                     if (!IdIsIndex(props[i], &index))
     827           0 :                         continue;
     828             : 
     829           0 :                     if (index >= newLen && index < oldLen) {
     830           0 :                         if (!indexes.append(index))
     831           0 :                             return false;
     832             :                     }
     833             :                 }
     834             :             }
     835             : 
     836           0 :             uint32_t count = indexes.length();
     837             :             {
     838             :                 // We should use radix sort to be O(n), but this is uncommon
     839             :                 // enough that we'll punt til someone complains.
     840           0 :                 Vector<uint32_t> scratch(cx);
     841           0 :                 if (!scratch.resize(count))
     842           0 :                     return false;
     843           0 :                 MOZ_ALWAYS_TRUE(MergeSort(indexes.begin(), count, scratch.begin(),
     844             :                                           ReverseIndexComparator()));
     845             :             }
     846             : 
     847           0 :             uint32_t index = UINT32_MAX;
     848           0 :             for (uint32_t i = 0; i < count; i++) {
     849           0 :                 MOZ_ASSERT(indexes[i] < index, "indexes should never repeat");
     850           0 :                 index = indexes[i];
     851             : 
     852             :                 // Steps 15b-d.
     853           0 :                 ObjectOpResult deleteSucceeded;
     854           0 :                 if (!DeleteElement(cx, arr, index, deleteSucceeded))
     855           0 :                     return false;
     856           0 :                 if (!deleteSucceeded) {
     857           0 :                     newLen = index + 1;
     858           0 :                     succeeded = false;
     859           0 :                     break;
     860             :                 }
     861             :             }
     862             :         }
     863             :     } while (false);
     864             : 
     865             :     // Update array length. Technically we should have been doing this
     866             :     // throughout the loop, in step 19.d.iii.
     867         123 :     arr->setLength(cx, newLen);
     868             : 
     869             :     // Step 20.
     870         123 :     if (attrs & JSPROP_READONLY) {
     871             :         // Yes, we totally drop a non-stub getter/setter from a defineProperty
     872             :         // API call on the floor here.  Given that getter/setter will go away in
     873             :         // the long run, with accessors replacing them both internally and at the
     874             :         // API level, just run with this.
     875           0 :         RootedShape lengthShape(cx, arr->lookup(cx, id));
     876           0 :         if (!NativeObject::changeProperty(cx, arr, lengthShape,
     877           0 :                                           lengthShape->attributes() | JSPROP_READONLY,
     878             :                                           array_length_getter, array_length_setter))
     879             :         {
     880           0 :             return false;
     881             :         }
     882             :     }
     883             : 
     884             :     // All operations past here until the |!succeeded| code must be infallible,
     885             :     // so that all element fields remain properly synchronized.
     886             : 
     887             :     // Trim the initialized length, if needed, to preserve the <= length
     888             :     // invariant.  (Capacity was already reduced during element deletion, if
     889             :     // necessary.)
     890         123 :     ObjectElements* header = arr->getElementsHeader();
     891         123 :     header->initializedLength = Min(header->initializedLength, newLen);
     892             : 
     893         123 :     if (attrs & JSPROP_READONLY)
     894           0 :         arr->setNonWritableLength(cx);
     895             : 
     896         123 :     if (!succeeded)
     897           0 :         return result.fail(JSMSG_CANT_TRUNCATE_ARRAY);
     898             : 
     899         123 :     return result.succeed();
     900             : }
     901             : 
     902             : bool
     903        5220 : js::WouldDefinePastNonwritableLength(HandleNativeObject obj, uint32_t index)
     904             : {
     905        5220 :     if (!obj->is<ArrayObject>())
     906          23 :         return false;
     907             : 
     908        5197 :     ArrayObject* arr = &obj->as<ArrayObject>();
     909        5197 :     return !arr->lengthIsWritable() && index >= arr->length();
     910             : }
     911             : 
     912             : static bool
     913        9517 : array_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
     914             : {
     915       19034 :     Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
     916             : 
     917             :     uint32_t index;
     918        9517 :     if (!IdIsIndex(id, &index))
     919        9517 :         return true;
     920             : 
     921           0 :     uint32_t length = arr->length();
     922           0 :     if (index >= length) {
     923           0 :         MOZ_ASSERT(arr->lengthIsWritable(),
     924             :                    "how'd this element get added if length is non-writable?");
     925           0 :         arr->setLength(cx, index + 1);
     926             :     }
     927           0 :     return true;
     928             : }
     929             : 
     930             : static inline bool
     931        7687 : ObjectMayHaveExtraIndexedOwnProperties(JSObject* obj)
     932             : {
     933       15374 :     return (!obj->isNative() && !obj->is<UnboxedArrayObject>()) ||
     934       15221 :            obj->isIndexed() ||
     935       22825 :            obj->is<TypedArrayObject>() ||
     936        7569 :            ClassMayResolveId(*obj->runtimeFromAnyThread()->commonNames,
     937        7687 :                              obj->getClass(), INT_TO_JSID(0), obj);
     938             : }
     939             : 
     940             : /*
     941             :  * Whether obj may have indexed properties anywhere besides its dense
     942             :  * elements. This includes other indexed properties in its shape hierarchy, and
     943             :  * indexed properties or elements along its prototype chain.
     944             :  */
     945             : bool
     946        2641 : js::ObjectMayHaveExtraIndexedProperties(JSObject* obj)
     947             : {
     948        2641 :     MOZ_ASSERT_IF(obj->hasDynamicPrototype(), !obj->isNative());
     949             : 
     950        2641 :     if (ObjectMayHaveExtraIndexedOwnProperties(obj))
     951         118 :         return true;
     952             : 
     953        5046 :     do {
     954        7569 :         MOZ_ASSERT(obj->hasStaticPrototype(),
     955             :                    "dynamic-prototype objects must be non-native, ergo must "
     956             :                    "have failed ObjectMayHaveExtraIndexedOwnProperties");
     957             : 
     958        7569 :         obj = obj->staticPrototype();
     959        7569 :         if (!obj)
     960        2523 :             return false; // no extra indexed properties found
     961             : 
     962        5046 :         if (ObjectMayHaveExtraIndexedOwnProperties(obj))
     963           0 :             return true;
     964        5046 :         if (GetAnyBoxedOrUnboxedInitializedLength(obj) != 0)
     965           0 :             return true;
     966             :     } while (true);
     967             : }
     968             : 
     969             : static bool
     970         316 : AddLengthProperty(JSContext* cx, HandleArrayObject obj)
     971             : {
     972             :     /*
     973             :      * Add the 'length' property for a newly created array,
     974             :      * and update the elements to be an empty array owned by the object.
     975             :      * The shared emptyObjectElements singleton cannot be used for slow arrays,
     976             :      * as accesses to 'length' will use the elements header.
     977             :      */
     978             : 
     979         632 :     RootedId lengthId(cx, NameToId(cx->names().length));
     980         316 :     MOZ_ASSERT(!obj->lookup(cx, lengthId));
     981             : 
     982         632 :     return NativeObject::addProperty(cx, obj, lengthId, array_length_getter, array_length_setter,
     983             :                                      SHAPE_INVALID_SLOT,
     984             :                                      JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_SHADOWABLE,
     985         632 :                                      0, /* allowDictionary = */ false);
     986             : }
     987             : 
     988             : static bool
     989          23 : IsArrayConstructor(const JSObject* obj)
     990             : {
     991             :     // This must only return true if v is *the* Array constructor for the
     992             :     // current compartment; we rely on the fact that any other Array
     993             :     // constructor would be represented as a wrapper.
     994          46 :     return obj->is<JSFunction>() &&
     995          46 :            obj->as<JSFunction>().isNative() &&
     996          46 :            obj->as<JSFunction>().native() == ArrayConstructor;
     997             : }
     998             : 
     999             : static bool
    1000          23 : IsArrayConstructor(const Value& v)
    1001             : {
    1002          23 :     return v.isObject() && IsArrayConstructor(&v.toObject());
    1003             : }
    1004             : 
    1005             : bool
    1006         151 : js::IsWrappedArrayConstructor(JSContext* cx, const Value& v, bool* result)
    1007             : {
    1008         151 :     if (!v.isObject()) {
    1009           0 :         *result = false;
    1010           0 :         return true;
    1011             :     }
    1012         151 :     if (v.toObject().is<WrapperObject>()) {
    1013           0 :         JSObject* obj = CheckedUnwrap(&v.toObject());
    1014           0 :         if (!obj) {
    1015           0 :             ReportAccessDenied(cx);
    1016           0 :             return false;
    1017             :         }
    1018             : 
    1019           0 :         *result = IsArrayConstructor(obj);
    1020             :     } else {
    1021         151 :         *result = false;
    1022             :     }
    1023         151 :     return true;
    1024             : }
    1025             : 
    1026             : static bool
    1027          57 : IsArraySpecies(JSContext* cx, HandleObject origArray)
    1028             : {
    1029          57 :     if (origArray->is<NativeObject>() && !origArray->is<ArrayObject>())
    1030          32 :         return true;
    1031             : 
    1032          50 :     RootedValue ctor(cx);
    1033          25 :     if (!GetPropertyPure(cx, origArray, NameToId(cx->names().constructor), ctor.address()))
    1034           2 :         return false;
    1035             : 
    1036          23 :     if (!IsArrayConstructor(ctor))
    1037           0 :         return ctor.isUndefined();
    1038             : 
    1039          46 :     RootedObject ctorObj(cx, &ctor.toObject());
    1040          46 :     RootedId speciesId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().species));
    1041             :     JSFunction* getter;
    1042          23 :     if (!GetGetterPure(cx, ctorObj, speciesId, &getter))
    1043           0 :         return false;
    1044             : 
    1045          23 :     if (!getter)
    1046           0 :         return false;
    1047             : 
    1048          23 :     return IsSelfHostedFunctionWithName(getter, cx->names().ArraySpecies);
    1049             : }
    1050             : 
    1051             : static bool
    1052           2 : ArraySpeciesCreate(JSContext* cx, HandleObject origArray, uint64_t length, MutableHandleObject arr)
    1053             : {
    1054           2 :     MOZ_ASSERT(length < DOUBLE_INTEGRAL_PRECISION_LIMIT);
    1055             : 
    1056           4 :     FixedInvokeArgs<2> args(cx);
    1057             : 
    1058           2 :     args[0].setObject(*origArray);
    1059           2 :     args[1].set(NumberValue(length));
    1060             : 
    1061           4 :     RootedValue rval(cx);
    1062           2 :     if (!CallSelfHostedFunction(cx, cx->names().ArraySpeciesCreate, UndefinedHandleValue, args,
    1063             :                                 &rval))
    1064             :     {
    1065           0 :         return false;
    1066             :     }
    1067             : 
    1068           2 :     MOZ_ASSERT(rval.isObject());
    1069           2 :     arr.set(&rval.toObject());
    1070           2 :     return true;
    1071             : }
    1072             : 
    1073             : #if JS_HAS_TOSOURCE
    1074             : 
    1075             : static bool
    1076           2 : array_toSource(JSContext* cx, unsigned argc, Value* vp)
    1077             : {
    1078           2 :     if (!CheckRecursionLimit(cx))
    1079           0 :         return false;
    1080             : 
    1081           2 :     CallArgs args = CallArgsFromVp(argc, vp);
    1082             : 
    1083           2 :     if (!args.thisv().isObject()) {
    1084           0 :         ReportIncompatible(cx, args);
    1085           0 :         return false;
    1086             :     }
    1087             : 
    1088           4 :     Rooted<JSObject*> obj(cx, &args.thisv().toObject());
    1089           4 :     RootedValue elt(cx);
    1090             : 
    1091           4 :     AutoCycleDetector detector(cx, obj);
    1092           2 :     if (!detector.init())
    1093           0 :         return false;
    1094             : 
    1095           4 :     StringBuffer sb(cx);
    1096             : 
    1097           2 :     if (detector.foundCycle()) {
    1098           0 :         if (!sb.append("[]"))
    1099           0 :             return false;
    1100           0 :         goto make_string;
    1101             :     }
    1102             : 
    1103           2 :     if (!sb.append('['))
    1104           0 :         return false;
    1105             : 
    1106             :     uint64_t length;
    1107           2 :     if (!GetLengthProperty(cx, obj, &length))
    1108           0 :         return false;
    1109             : 
    1110           5 :     for (uint64_t index = 0; index < length; index++) {
    1111             :         bool hole;
    1112          15 :         if (!CheckForInterrupt(cx) ||
    1113          15 :             !HasAndGetElement(cx, obj, index, &hole, &elt)) {
    1114           0 :             return false;
    1115             :         }
    1116             : 
    1117             :         /* Get element's character string. */
    1118             :         JSString* str;
    1119           3 :         if (hole) {
    1120           0 :             str = cx->runtime()->emptyString;
    1121             :         } else {
    1122           3 :             str = ValueToSource(cx, elt);
    1123           3 :             if (!str)
    1124           0 :                 return false;
    1125             :         }
    1126             : 
    1127             :         /* Append element to buffer. */
    1128           3 :         if (!sb.append(str))
    1129           0 :             return false;
    1130           3 :         if (index + 1 != length) {
    1131           1 :             if (!sb.append(", "))
    1132           0 :                 return false;
    1133           2 :         } else if (hole) {
    1134           0 :             if (!sb.append(','))
    1135           0 :                 return false;
    1136             :         }
    1137             :     }
    1138             : 
    1139             :     /* Finalize the buffer. */
    1140           2 :     if (!sb.append(']'))
    1141           0 :         return false;
    1142             : 
    1143             :   make_string:
    1144           2 :     JSString* str = sb.finishString();
    1145           2 :     if (!str)
    1146           0 :         return false;
    1147             : 
    1148           2 :     args.rval().setString(str);
    1149           2 :     return true;
    1150             : }
    1151             : 
    1152             : #endif
    1153             : 
    1154             : struct EmptySeparatorOp
    1155             : {
    1156        1024 :     bool operator()(JSContext*, StringBuffer& sb) { return true; }
    1157             : };
    1158             : 
    1159             : template <typename CharT>
    1160             : struct CharSeparatorOp
    1161             : {
    1162             :     const CharT sep;
    1163          50 :     explicit CharSeparatorOp(CharT sep) : sep(sep) {}
    1164         304 :     bool operator()(JSContext*, StringBuffer& sb) { return sb.append(sep); }
    1165             : };
    1166             : 
    1167             : struct StringSeparatorOp
    1168             : {
    1169             :     HandleLinearString sep;
    1170             : 
    1171           3 :     explicit StringSeparatorOp(HandleLinearString sep) : sep(sep) {}
    1172             : 
    1173          10 :     bool operator()(JSContext* cx, StringBuffer& sb) {
    1174          10 :         return sb.append(sep);
    1175             :     }
    1176             : };
    1177             : 
    1178             : template <typename SeparatorOp, JSValueType Type>
    1179             : static DenseElementResult
    1180         310 : ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint64_t length,
    1181             :                      StringBuffer& sb, uint32_t* numProcessed)
    1182             : {
    1183             :     // This loop handles all elements up to initializedLength. If
    1184             :     // length > initLength we rely on the second loop to add the
    1185             :     // other elements.
    1186         310 :     MOZ_ASSERT(*numProcessed == 0);
    1187         310 :     uint64_t initLength = Min<uint64_t>(GetBoxedOrUnboxedInitializedLength<Type>(obj), length);
    1188         310 :     MOZ_ASSERT(initLength <= UINT32_MAX, "initialized length shouldn't exceed UINT32_MAX");
    1189         310 :     uint32_t initLengthClamped = uint32_t(initLength);
    1190        3606 :     while (*numProcessed < initLengthClamped) {
    1191        1648 :         if (!CheckForInterrupt(cx))
    1192           0 :             return DenseElementResult::Failure;
    1193             : 
    1194             :         // Step 7.b.
    1195        1648 :         const Value& elem = GetBoxedOrUnboxedDenseElement<Type>(obj, *numProcessed);
    1196             : 
    1197             :         // Steps 7.c-d.
    1198        1648 :         if (elem.isString()) {
    1199        1648 :             if (!sb.append(elem.toString()))
    1200           0 :                 return DenseElementResult::Failure;
    1201           0 :         } else if (elem.isNumber()) {
    1202           0 :             if (!NumberValueToStringBuffer(cx, elem, sb))
    1203           0 :                 return DenseElementResult::Failure;
    1204           0 :         } else if (elem.isBoolean()) {
    1205           0 :             if (!BooleanToStringBuffer(elem.toBoolean(), sb))
    1206           0 :                 return DenseElementResult::Failure;
    1207           0 :         } else if (elem.isObject() || elem.isSymbol()) {
    1208             :             /*
    1209             :              * Object stringifying could modify the initialized length or make
    1210             :              * the array sparse. Delegate it to a separate loop to keep this
    1211             :              * one tight.
    1212             :              *
    1213             :              * Symbol stringifying is a TypeError, so into the slow path
    1214             :              * with those as well.
    1215             :              */
    1216           0 :             break;
    1217             :         } else {
    1218           0 :             MOZ_ASSERT(elem.isMagic(JS_ELEMENTS_HOLE) || elem.isNullOrUndefined());
    1219             :         }
    1220             : 
    1221             :         // Steps 7.a, 7.e.
    1222        1648 :         if (++(*numProcessed) != length && !sepOp(cx, sb))
    1223           0 :             return DenseElementResult::Failure;
    1224             :     }
    1225             : 
    1226         310 :     return DenseElementResult::Incomplete;
    1227             : }
    1228             : 
    1229             : template <typename SeparatorOp>
    1230             : struct ArrayJoinDenseKernelFunctor {
    1231             :     JSContext* cx;
    1232             :     SeparatorOp sepOp;
    1233             :     HandleObject obj;
    1234             :     uint64_t length;
    1235             :     StringBuffer& sb;
    1236             :     uint32_t* numProcessed;
    1237             : 
    1238         310 :     ArrayJoinDenseKernelFunctor(JSContext* cx, SeparatorOp sepOp, HandleObject obj,
    1239             :                                 uint64_t length, StringBuffer& sb, uint32_t* numProcessed)
    1240         310 :       : cx(cx), sepOp(sepOp), obj(obj), length(length), sb(sb), numProcessed(numProcessed)
    1241         310 :     {}
    1242             : 
    1243             :     template <JSValueType Type>
    1244         310 :     DenseElementResult operator()() {
    1245         310 :         return ArrayJoinDenseKernel<SeparatorOp, Type>(cx, sepOp, obj, length, sb, numProcessed);
    1246             :     }
    1247             : };
    1248             : 
    1249             : template <typename SeparatorOp>
    1250             : static bool
    1251         310 : ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint64_t length,
    1252             :                StringBuffer& sb)
    1253             : {
    1254             :     // Step 6.
    1255         310 :     uint32_t numProcessed = 0;
    1256             : 
    1257         310 :     if (!ObjectMayHaveExtraIndexedProperties(obj)) {
    1258         310 :         ArrayJoinDenseKernelFunctor<SeparatorOp> functor(cx, sepOp, obj, length, sb, &numProcessed);
    1259         310 :         DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
    1260         310 :         if (result == DenseElementResult::Failure)
    1261           0 :             return false;
    1262             :     }
    1263             : 
    1264             :     // Step 7.
    1265         310 :     if (numProcessed != length) {
    1266           0 :         RootedValue v(cx);
    1267           0 :         for (uint64_t i = numProcessed; i < length; ) {
    1268           0 :             if (!CheckForInterrupt(cx))
    1269           0 :                 return false;
    1270             : 
    1271             :             // Step 7.b.
    1272           0 :             if (!GetArrayElement(cx, obj, i, &v))
    1273           0 :                 return false;
    1274             : 
    1275             :             // Steps 7.c-d.
    1276           0 :             if (!v.isNullOrUndefined()) {
    1277           0 :                 if (!ValueToStringBuffer(cx, v, sb))
    1278           0 :                     return false;
    1279             :             }
    1280             : 
    1281             :             // Steps 7.a, 7.e.
    1282           0 :             if (++i != length && !sepOp(cx, sb))
    1283           0 :                 return false;
    1284             :         }
    1285             :     }
    1286             : 
    1287         310 :     return true;
    1288             : }
    1289             : 
    1290             : // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
    1291             : // 22.1.3.13 Array.prototype.join ( separator )
    1292             : bool
    1293         319 : js::array_join(JSContext* cx, unsigned argc, Value* vp)
    1294             : {
    1295         319 :     if (!CheckRecursionLimit(cx))
    1296           0 :         return false;
    1297             : 
    1298         638 :     AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.join");
    1299         319 :     CallArgs args = CallArgsFromVp(argc, vp);
    1300             : 
    1301             :     // Step 1.
    1302         638 :     RootedObject obj(cx, ToObject(cx, args.thisv()));
    1303         319 :     if (!obj)
    1304           0 :         return false;
    1305             : 
    1306         638 :     AutoCycleDetector detector(cx, obj);
    1307         319 :     if (!detector.init())
    1308           0 :         return false;
    1309             : 
    1310         319 :     if (detector.foundCycle()) {
    1311           0 :         args.rval().setString(cx->names().empty);
    1312           0 :         return true;
    1313             :     }
    1314             : 
    1315             :     // Step 2.
    1316             :     uint64_t length;
    1317         319 :     if (!GetLengthProperty(cx, obj, &length))
    1318           0 :         return false;
    1319             : 
    1320             :     // Steps 3-4.
    1321         638 :     RootedLinearString sepstr(cx);
    1322         319 :     if (args.hasDefined(0)) {
    1323         319 :         JSString *s = ToString<CanGC>(cx, args[0]);
    1324         319 :         if (!s)
    1325           0 :             return false;
    1326         319 :         sepstr = s->ensureLinear(cx);
    1327         319 :         if (!sepstr)
    1328           0 :             return false;
    1329             :     } else {
    1330           0 :         sepstr = cx->names().comma;
    1331             :     }
    1332             : 
    1333             :     // Steps 5-8 (When the length is zero, directly return the empty string).
    1334         319 :     if (length == 0) {
    1335           2 :         args.rval().setString(cx->emptyString());
    1336           2 :         return true;
    1337             :     }
    1338             : 
    1339             :     // An optimized version of a special case of steps 5-8: when length==1 and
    1340             :     // the 0th element is a string, ToString() of that element is a no-op and
    1341             :     // so it can be immediately returned as the result.
    1342         317 :     if (length == 1 && GetAnyBoxedOrUnboxedInitializedLength(obj) == 1) {
    1343           7 :         Value elem0 = GetAnyBoxedOrUnboxedDenseElement(obj, 0);
    1344           7 :         if (elem0.isString()) {
    1345           7 :             args.rval().set(elem0);
    1346           7 :             return true;
    1347             :         }
    1348             :     }
    1349             : 
    1350             :     // Step 5.
    1351         620 :     StringBuffer sb(cx);
    1352         310 :     if (sepstr->hasTwoByteChars() && !sb.ensureTwoByteChars())
    1353           0 :         return false;
    1354             : 
    1355             :     // The separator will be added |length - 1| times, reserve space for that
    1356             :     // so that we don't have to unnecessarily grow the buffer.
    1357         310 :     size_t seplen = sepstr->length();
    1358         310 :     if (seplen > 0) {
    1359          53 :         if (length > UINT32_MAX) {
    1360           0 :             ReportAllocationOverflow(cx);
    1361           0 :             return false;
    1362             :         }
    1363          53 :         CheckedInt<uint32_t> res = CheckedInt<uint32_t>(seplen) * (uint32_t(length) - 1);
    1364          53 :         if (!res.isValid()) {
    1365           0 :             ReportAllocationOverflow(cx);
    1366           0 :             return false;
    1367             :         }
    1368             : 
    1369          53 :         if (!sb.reserve(res.value()))
    1370           0 :             return false;
    1371             :     }
    1372             : 
    1373             :     // Various optimized versions of steps 6-7.
    1374         310 :     if (seplen == 0) {
    1375             :         EmptySeparatorOp op;
    1376         257 :         if (!ArrayJoinKernel(cx, op, obj, length, sb))
    1377           0 :             return false;
    1378          53 :     } else if (seplen == 1) {
    1379          50 :         char16_t c = sepstr->latin1OrTwoByteChar(0);
    1380          50 :         if (c <= JSString::MAX_LATIN1_CHAR) {
    1381          50 :             CharSeparatorOp<Latin1Char> op(c);
    1382          50 :             if (!ArrayJoinKernel(cx, op, obj, length, sb))
    1383           0 :                 return false;
    1384             :         } else {
    1385           0 :             CharSeparatorOp<char16_t> op(c);
    1386           0 :             if (!ArrayJoinKernel(cx, op, obj, length, sb))
    1387           0 :                 return false;
    1388             :         }
    1389             :     } else {
    1390           3 :         StringSeparatorOp op(sepstr);
    1391           3 :         if (!ArrayJoinKernel(cx, op, obj, length, sb))
    1392           0 :             return false;
    1393             :     }
    1394             : 
    1395             :     // Step 8.
    1396         310 :     JSString *str = sb.finishString();
    1397         310 :     if (!str)
    1398           0 :         return false;
    1399             : 
    1400         310 :     args.rval().setString(str);
    1401         310 :     return true;
    1402             : }
    1403             : 
    1404             : // ES2017 draft rev f8a9be8ea4bd97237d176907a1e3080dce20c68f
    1405             : // 22.1.3.27 Array.prototype.toLocaleString ([ reserved1 [ , reserved2 ] ])
    1406             : // ES2017 Intl draft rev 78bbe7d1095f5ff3760ac4017ed366026e4cb276
    1407             : // 13.4.1 Array.prototype.toLocaleString ([ locales [ , options ]])
    1408             : static bool
    1409           0 : array_toLocaleString(JSContext* cx, unsigned argc, Value* vp)
    1410             : {
    1411           0 :     if (!CheckRecursionLimit(cx))
    1412           0 :         return false;
    1413             : 
    1414           0 :     CallArgs args = CallArgsFromVp(argc, vp);
    1415             : 
    1416             :     // Step 1
    1417           0 :     RootedObject obj(cx, ToObject(cx, args.thisv()));
    1418           0 :     if (!obj)
    1419           0 :         return false;
    1420             : 
    1421             :     // Avoid calling into self-hosted code if the array is empty.
    1422           0 :     if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() == 0) {
    1423           0 :         args.rval().setString(cx->names().empty);
    1424           0 :         return true;
    1425             :     }
    1426           0 :     if (obj->is<UnboxedArrayObject>() && obj->as<UnboxedArrayObject>().length() == 0) {
    1427           0 :         args.rval().setString(cx->names().empty);
    1428           0 :         return true;
    1429             :     }
    1430             : 
    1431           0 :     AutoCycleDetector detector(cx, obj);
    1432           0 :     if (!detector.init())
    1433           0 :         return false;
    1434             : 
    1435           0 :     if (detector.foundCycle()) {
    1436           0 :         args.rval().setString(cx->names().empty);
    1437           0 :         return true;
    1438             :     }
    1439             : 
    1440           0 :     FixedInvokeArgs<2> args2(cx);
    1441             : 
    1442           0 :     args2[0].set(args.get(0));
    1443           0 :     args2[1].set(args.get(1));
    1444             : 
    1445             :     // Steps 2-10.
    1446           0 :     RootedValue thisv(cx, ObjectValue(*obj));
    1447           0 :     return CallSelfHostedFunction(cx, cx->names().ArrayToLocaleString, thisv, args2, args.rval());
    1448             : }
    1449             : 
    1450             : /* vector must point to rooted memory. */
    1451             : static bool
    1452          38 : SetArrayElements(JSContext* cx, HandleObject obj, uint64_t start,
    1453             :                  uint32_t count, const Value* vector,
    1454             :                  ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update)
    1455             : {
    1456          38 :     MOZ_ASSERT(count <= MAX_ARRAY_INDEX);
    1457          38 :     MOZ_ASSERT(start + count < uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT));
    1458             : 
    1459          38 :     if (count == 0)
    1460           2 :         return true;
    1461             : 
    1462          36 :     if (!ObjectMayHaveExtraIndexedProperties(obj) && start <= UINT32_MAX) {
    1463             :         DenseElementResult result =
    1464          38 :             SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(start), vector, count,
    1465          38 :                                                       updateTypes);
    1466          19 :         if (result != DenseElementResult::Incomplete)
    1467          19 :             return result == DenseElementResult::Success;
    1468             :     }
    1469             : 
    1470          34 :     RootedId id(cx);
    1471          17 :     const Value* end = vector + count;
    1472          51 :     while (vector < end) {
    1473          17 :         if (!CheckForInterrupt(cx))
    1474           0 :             return false;
    1475             : 
    1476          17 :         if (!ToId(cx, start++, &id))
    1477           0 :             return false;
    1478             : 
    1479          17 :         if (!SetProperty(cx, obj, id, HandleValue::fromMarkedLocation(vector++)))
    1480           0 :             return false;
    1481             :     }
    1482             : 
    1483          17 :     return true;
    1484             : }
    1485             : 
    1486             : template <JSValueType Type>
    1487             : DenseElementResult
    1488           0 : ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length)
    1489             : {
    1490             :     /* An empty array or an array with no elements is already reversed. */
    1491           0 :     if (length == 0 || GetBoxedOrUnboxedInitializedLength<Type>(obj) == 0)
    1492           0 :         return DenseElementResult::Success;
    1493             : 
    1494             :     if (Type == JSVAL_TYPE_MAGIC) {
    1495           0 :         if (obj->as<NativeObject>().denseElementsAreFrozen())
    1496           0 :             return DenseElementResult::Incomplete;
    1497             : 
    1498             :         /*
    1499             :          * It's actually surprisingly complicated to reverse an array due to the
    1500             :          * orthogonality of array length and array capacity while handling
    1501             :          * leading and trailing holes correctly.  Reversing seems less likely to
    1502             :          * be a common operation than other array mass-mutation methods, so for
    1503             :          * now just take a probably-small memory hit (in the absence of too many
    1504             :          * holes in the array at its start) and ensure that the capacity is
    1505             :          * sufficient to hold all the elements in the array if it were full.
    1506             :          */
    1507           0 :         DenseElementResult result = obj->as<NativeObject>().ensureDenseElements(cx, length, 0);
    1508           0 :         if (result != DenseElementResult::Success)
    1509           0 :             return result;
    1510             : 
    1511             :         /* Fill out the array's initialized length to its proper length. */
    1512           0 :         obj->as<NativeObject>().ensureDenseInitializedLength(cx, length, 0);
    1513             :     } else {
    1514             :         // Unboxed arrays can only be reversed here if their initialized length
    1515             :         // matches their actual length. Otherwise the reversal will place holes
    1516             :         // at the beginning of the array, which we don't support.
    1517           0 :         if (length != obj->as<UnboxedArrayObject>().initializedLength())
    1518           0 :             return DenseElementResult::Incomplete;
    1519             :     }
    1520             : 
    1521           0 :     RootedValue origlo(cx), orighi(cx);
    1522             : 
    1523           0 :     uint32_t lo = 0, hi = length - 1;
    1524           0 :     for (; lo < hi; lo++, hi--) {
    1525           0 :         origlo = GetBoxedOrUnboxedDenseElement<Type>(obj, lo);
    1526           0 :         orighi = GetBoxedOrUnboxedDenseElement<Type>(obj, hi);
    1527           0 :         SetBoxedOrUnboxedDenseElementNoTypeChange<Type>(obj, lo, orighi);
    1528           0 :         if (orighi.isMagic(JS_ELEMENTS_HOLE) &&
    1529           0 :             !SuppressDeletedProperty(cx, obj, INT_TO_JSID(lo)))
    1530             :         {
    1531           0 :             return DenseElementResult::Failure;
    1532             :         }
    1533           0 :         SetBoxedOrUnboxedDenseElementNoTypeChange<Type>(obj, hi, origlo);
    1534           0 :         if (origlo.isMagic(JS_ELEMENTS_HOLE) &&
    1535           0 :             !SuppressDeletedProperty(cx, obj, INT_TO_JSID(hi)))
    1536             :         {
    1537           0 :             return DenseElementResult::Failure;
    1538             :         }
    1539             :     }
    1540             : 
    1541           0 :     return DenseElementResult::Success;
    1542             : }
    1543             : 
    1544           0 : DefineBoxedOrUnboxedFunctor3(ArrayReverseDenseKernel,
    1545             :                              JSContext*, HandleObject, uint32_t);
    1546             : 
    1547             : // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
    1548             : // 22.1.3.21 Array.prototype.reverse ( )
    1549             : bool
    1550           0 : js::array_reverse(JSContext* cx, unsigned argc, Value* vp)
    1551             : {
    1552           0 :     AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.reverse");
    1553           0 :     CallArgs args = CallArgsFromVp(argc, vp);
    1554             : 
    1555             :     // Step 1.
    1556           0 :     RootedObject obj(cx, ToObject(cx, args.thisv()));
    1557           0 :     if (!obj)
    1558           0 :         return false;
    1559             : 
    1560             :     // Step 2.
    1561             :     uint64_t len;
    1562           0 :     if (!GetLengthProperty(cx, obj, &len))
    1563           0 :         return false;
    1564             : 
    1565           0 :     if (!ObjectMayHaveExtraIndexedProperties(obj) && len <= UINT32_MAX) {
    1566           0 :         ArrayReverseDenseKernelFunctor functor(cx, obj, uint32_t(len));
    1567           0 :         DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
    1568           0 :         if (result != DenseElementResult::Incomplete) {
    1569             :             /*
    1570             :              * Per ECMA-262, don't update the length of the array, even if the new
    1571             :              * array has trailing holes (and thus the original array began with
    1572             :              * holes).
    1573             :              */
    1574           0 :             args.rval().setObject(*obj);
    1575           0 :             return result == DenseElementResult::Success;
    1576             :         }
    1577             :     }
    1578             : 
    1579             :     // Steps 3-5.
    1580           0 :     RootedValue lowval(cx), hival(cx);
    1581           0 :     for (uint64_t i = 0, half = len / 2; i < half; i++) {
    1582             :         bool hole, hole2;
    1583           0 :         if (!CheckForInterrupt(cx) ||
    1584           0 :             !HasAndGetElement(cx, obj, i, &hole, &lowval) ||
    1585           0 :             !HasAndGetElement(cx, obj, len - i - 1, &hole2, &hival))
    1586             :         {
    1587           0 :             return false;
    1588             :         }
    1589             : 
    1590           0 :         if (!hole && !hole2) {
    1591           0 :             if (!SetArrayElement(cx, obj, i, hival))
    1592           0 :                 return false;
    1593           0 :             if (!SetArrayElement(cx, obj, len - i - 1, lowval))
    1594           0 :                 return false;
    1595           0 :         } else if (hole && !hole2) {
    1596           0 :             if (!SetArrayElement(cx, obj, i, hival))
    1597           0 :                 return false;
    1598           0 :             if (!DeletePropertyOrThrow(cx, obj, len - i - 1))
    1599           0 :                 return false;
    1600           0 :         } else if (!hole && hole2) {
    1601           0 :             if (!DeletePropertyOrThrow(cx, obj, i))
    1602           0 :                 return false;
    1603           0 :             if (!SetArrayElement(cx, obj, len - i - 1, lowval))
    1604           0 :                 return false;
    1605             :         } else {
    1606             :             // No action required.
    1607             :         }
    1608             :     }
    1609             : 
    1610             :     // Step 6.
    1611           0 :     args.rval().setObject(*obj);
    1612           0 :     return true;
    1613             : }
    1614             : 
    1615             : static inline bool
    1616          12 : CompareStringValues(JSContext* cx, const Value& a, const Value& b, bool* lessOrEqualp)
    1617             : {
    1618          12 :     if (!CheckForInterrupt(cx))
    1619           0 :         return false;
    1620             : 
    1621          12 :     JSString* astr = a.toString();
    1622          12 :     JSString* bstr = b.toString();
    1623             :     int32_t result;
    1624          12 :     if (!CompareStrings(cx, astr, bstr, &result))
    1625           0 :         return false;
    1626             : 
    1627          12 :     *lessOrEqualp = (result <= 0);
    1628          12 :     return true;
    1629             : }
    1630             : 
    1631             : static const uint64_t powersOf10[] = {
    1632             :     1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 1000000000000ULL
    1633             : };
    1634             : 
    1635             : static inline unsigned
    1636           0 : NumDigitsBase10(uint32_t n)
    1637             : {
    1638             :     /*
    1639             :      * This is just floor_log10(n) + 1
    1640             :      * Algorithm taken from
    1641             :      * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
    1642             :      */
    1643           0 :     uint32_t log2 = CeilingLog2(n);
    1644           0 :     uint32_t t = log2 * 1233 >> 12;
    1645           0 :     return t - (n < powersOf10[t]) + 1;
    1646             : }
    1647             : 
    1648             : static inline bool
    1649           0 : CompareLexicographicInt32(const Value& a, const Value& b, bool* lessOrEqualp)
    1650             : {
    1651           0 :     int32_t aint = a.toInt32();
    1652           0 :     int32_t bint = b.toInt32();
    1653             : 
    1654             :     /*
    1655             :      * If both numbers are equal ... trivial
    1656             :      * If only one of both is negative --> arithmetic comparison as char code
    1657             :      * of '-' is always less than any other digit
    1658             :      * If both numbers are negative convert them to positive and continue
    1659             :      * handling ...
    1660             :      */
    1661           0 :     if (aint == bint) {
    1662           0 :         *lessOrEqualp = true;
    1663           0 :     } else if ((aint < 0) && (bint >= 0)) {
    1664           0 :         *lessOrEqualp = true;
    1665           0 :     } else if ((aint >= 0) && (bint < 0)) {
    1666           0 :         *lessOrEqualp = false;
    1667             :     } else {
    1668           0 :         uint32_t auint = Abs(aint);
    1669           0 :         uint32_t buint = Abs(bint);
    1670             : 
    1671             :         /*
    1672             :          *  ... get number of digits of both integers.
    1673             :          * If they have the same number of digits --> arithmetic comparison.
    1674             :          * If digits_a > digits_b: a < b*10e(digits_a - digits_b).
    1675             :          * If digits_b > digits_a: a*10e(digits_b - digits_a) <= b.
    1676             :          */
    1677           0 :         unsigned digitsa = NumDigitsBase10(auint);
    1678           0 :         unsigned digitsb = NumDigitsBase10(buint);
    1679           0 :         if (digitsa == digitsb) {
    1680           0 :             *lessOrEqualp = (auint <= buint);
    1681           0 :         } else if (digitsa > digitsb) {
    1682           0 :             MOZ_ASSERT((digitsa - digitsb) < ArrayLength(powersOf10));
    1683           0 :             *lessOrEqualp = (uint64_t(auint) < uint64_t(buint) * powersOf10[digitsa - digitsb]);
    1684             :         } else { /* if (digitsb > digitsa) */
    1685           0 :             MOZ_ASSERT((digitsb - digitsa) < ArrayLength(powersOf10));
    1686           0 :             *lessOrEqualp = (uint64_t(auint) * powersOf10[digitsb - digitsa] <= uint64_t(buint));
    1687             :         }
    1688             :     }
    1689             : 
    1690           0 :     return true;
    1691             : }
    1692             : 
    1693             : template <typename Char1, typename Char2>
    1694             : static inline bool
    1695           0 : CompareSubStringValues(JSContext* cx, const Char1* s1, size_t len1, const Char2* s2, size_t len2,
    1696             :                        bool* lessOrEqualp)
    1697             : {
    1698           0 :     if (!CheckForInterrupt(cx))
    1699           0 :         return false;
    1700             : 
    1701           0 :     if (!s1 || !s2)
    1702           0 :         return false;
    1703             : 
    1704           0 :     int32_t result = CompareChars(s1, len1, s2, len2);
    1705           0 :     *lessOrEqualp = (result <= 0);
    1706           0 :     return true;
    1707             : }
    1708             : 
    1709             : namespace {
    1710             : 
    1711             : struct SortComparatorStrings
    1712             : {
    1713             :     JSContext*  const cx;
    1714             : 
    1715           1 :     explicit SortComparatorStrings(JSContext* cx)
    1716           1 :       : cx(cx) {}
    1717             : 
    1718          12 :     bool operator()(const Value& a, const Value& b, bool* lessOrEqualp) {
    1719          12 :         return CompareStringValues(cx, a, b, lessOrEqualp);
    1720             :     }
    1721             : };
    1722             : 
    1723             : struct SortComparatorLexicographicInt32
    1724             : {
    1725           0 :     bool operator()(const Value& a, const Value& b, bool* lessOrEqualp) {
    1726           0 :         return CompareLexicographicInt32(a, b, lessOrEqualp);
    1727             :     }
    1728             : };
    1729             : 
    1730             : struct StringifiedElement
    1731             : {
    1732             :     size_t charsBegin;
    1733             :     size_t charsEnd;
    1734             :     size_t elementIndex;
    1735             : };
    1736             : 
    1737             : struct SortComparatorStringifiedElements
    1738             : {
    1739             :     JSContext*          const cx;
    1740             :     const StringBuffer& sb;
    1741             : 
    1742           0 :     SortComparatorStringifiedElements(JSContext* cx, const StringBuffer& sb)
    1743           0 :       : cx(cx), sb(sb) {}
    1744             : 
    1745           0 :     bool operator()(const StringifiedElement& a, const StringifiedElement& b, bool* lessOrEqualp) {
    1746           0 :         size_t lenA = a.charsEnd - a.charsBegin;
    1747           0 :         size_t lenB = b.charsEnd - b.charsBegin;
    1748             : 
    1749           0 :         if (sb.isUnderlyingBufferLatin1()) {
    1750           0 :             return CompareSubStringValues(cx, sb.rawLatin1Begin() + a.charsBegin, lenA,
    1751           0 :                                           sb.rawLatin1Begin() + b.charsBegin, lenB,
    1752           0 :                                           lessOrEqualp);
    1753             :         }
    1754             : 
    1755           0 :         return CompareSubStringValues(cx, sb.rawTwoByteBegin() + a.charsBegin, lenA,
    1756           0 :                                       sb.rawTwoByteBegin() + b.charsBegin, lenB,
    1757           0 :                                       lessOrEqualp);
    1758             :     }
    1759             : };
    1760             : 
    1761             : struct NumericElement
    1762             : {
    1763             :     double dv;
    1764             :     size_t elementIndex;
    1765             : };
    1766             : 
    1767             : static bool
    1768           0 : ComparatorNumericLeftMinusRight(const NumericElement& a, const NumericElement& b,
    1769             :                                 bool* lessOrEqualp)
    1770             : {
    1771           0 :     *lessOrEqualp = (a.dv <= b.dv);
    1772           0 :     return true;
    1773             : }
    1774             : 
    1775             : static bool
    1776           0 : ComparatorNumericRightMinusLeft(const NumericElement& a, const NumericElement& b,
    1777             :                                 bool* lessOrEqualp)
    1778             : {
    1779           0 :     *lessOrEqualp = (b.dv <= a.dv);
    1780           0 :     return true;
    1781             : }
    1782             : 
    1783             : typedef bool (*ComparatorNumeric)(const NumericElement& a, const NumericElement& b,
    1784             :                                   bool* lessOrEqualp);
    1785             : 
    1786             : static const ComparatorNumeric SortComparatorNumerics[] = {
    1787             :     nullptr,
    1788             :     nullptr,
    1789             :     ComparatorNumericLeftMinusRight,
    1790             :     ComparatorNumericRightMinusLeft
    1791             : };
    1792             : 
    1793             : static bool
    1794           0 : ComparatorInt32LeftMinusRight(const Value& a, const Value& b, bool* lessOrEqualp)
    1795             : {
    1796           0 :     *lessOrEqualp = (a.toInt32() <= b.toInt32());
    1797           0 :     return true;
    1798             : }
    1799             : 
    1800             : static bool
    1801           0 : ComparatorInt32RightMinusLeft(const Value& a, const Value& b, bool* lessOrEqualp)
    1802             : {
    1803           0 :     *lessOrEqualp = (b.toInt32() <= a.toInt32());
    1804           0 :     return true;
    1805             : }
    1806             : 
    1807             : typedef bool (*ComparatorInt32)(const Value& a, const Value& b, bool* lessOrEqualp);
    1808             : 
    1809             : static const ComparatorInt32 SortComparatorInt32s[] = {
    1810             :     nullptr,
    1811             :     nullptr,
    1812             :     ComparatorInt32LeftMinusRight,
    1813             :     ComparatorInt32RightMinusLeft
    1814             : };
    1815             : 
    1816             : // Note: Values for this enum must match up with SortComparatorNumerics
    1817             : // and SortComparatorInt32s.
    1818             : enum ComparatorMatchResult {
    1819             :     Match_Failure = 0,
    1820             :     Match_None,
    1821             :     Match_LeftMinusRight,
    1822             :     Match_RightMinusLeft
    1823             : };
    1824             : 
    1825             : } // namespace
    1826             : 
    1827             : 
    1828             : /*
    1829             :  * Specialize behavior for comparator functions with particular common bytecode
    1830             :  * patterns: namely, |return x - y| and |return y - x|.
    1831             :  */
    1832             : static ComparatorMatchResult
    1833          15 : MatchNumericComparator(JSContext* cx, const Value& v)
    1834             : {
    1835          15 :     if (!v.isObject())
    1836           1 :         return Match_None;
    1837             : 
    1838          14 :     JSObject& obj = v.toObject();
    1839          14 :     if (!obj.is<JSFunction>())
    1840           0 :         return Match_None;
    1841             : 
    1842          28 :     RootedFunction fun(cx, &obj.as<JSFunction>());
    1843          14 :     if (!fun->isInterpreted() || fun->isClassConstructor())
    1844           0 :         return Match_None;
    1845             : 
    1846          14 :     JSScript* script = JSFunction::getOrCreateScript(cx, fun);
    1847          14 :     if (!script)
    1848           0 :         return Match_Failure;
    1849             : 
    1850          14 :     jsbytecode* pc = script->code();
    1851             : 
    1852             :     uint16_t arg0, arg1;
    1853          14 :     if (JSOp(*pc) != JSOP_GETARG)
    1854          12 :         return Match_None;
    1855           2 :     arg0 = GET_ARGNO(pc);
    1856           2 :     pc += JSOP_GETARG_LENGTH;
    1857             : 
    1858           2 :     if (JSOp(*pc) != JSOP_GETARG)
    1859           2 :         return Match_None;
    1860           0 :     arg1 = GET_ARGNO(pc);
    1861           0 :     pc += JSOP_GETARG_LENGTH;
    1862             : 
    1863           0 :     if (JSOp(*pc) != JSOP_SUB)
    1864           0 :         return Match_None;
    1865           0 :     pc += JSOP_SUB_LENGTH;
    1866             : 
    1867           0 :     if (JSOp(*pc) != JSOP_RETURN)
    1868           0 :         return Match_None;
    1869             : 
    1870           0 :     if (arg0 == 0 && arg1 == 1)
    1871           0 :         return Match_LeftMinusRight;
    1872             : 
    1873           0 :     if (arg0 == 1 && arg1 == 0)
    1874           0 :         return Match_RightMinusLeft;
    1875             : 
    1876           0 :     return Match_None;
    1877             : }
    1878             : 
    1879             : template <typename K, typename C>
    1880             : static inline bool
    1881           0 : MergeSortByKey(K keys, size_t len, K scratch, C comparator, MutableHandle<GCVector<Value>> vec)
    1882             : {
    1883           0 :     MOZ_ASSERT(vec.length() >= len);
    1884             : 
    1885             :     /* Sort keys. */
    1886           0 :     if (!MergeSort(keys, len, scratch, comparator))
    1887           0 :         return false;
    1888             : 
    1889             :     /*
    1890             :      * Reorder vec by keys in-place, going element by element.  When an out-of-
    1891             :      * place element is encountered, move that element to its proper position,
    1892             :      * displacing whatever element was at *that* point to its proper position,
    1893             :      * and so on until an element must be moved to the current position.
    1894             :      *
    1895             :      * At each outer iteration all elements up to |i| are sorted.  If
    1896             :      * necessary each inner iteration moves some number of unsorted elements
    1897             :      * (including |i|) directly to sorted position.  Thus on completion |*vec|
    1898             :      * is sorted, and out-of-position elements have moved once.  Complexity is
    1899             :      * Θ(len) + O(len) == O(2*len), with each element visited at most twice.
    1900             :      */
    1901           0 :     for (size_t i = 0; i < len; i++) {
    1902           0 :         size_t j = keys[i].elementIndex;
    1903           0 :         if (i == j)
    1904           0 :             continue; // fixed point
    1905             : 
    1906           0 :         MOZ_ASSERT(j > i, "Everything less than |i| should be in the right place!");
    1907           0 :         Value tv = vec[j];
    1908           0 :         do {
    1909           0 :             size_t k = keys[j].elementIndex;
    1910           0 :             keys[j].elementIndex = j;
    1911           0 :             vec[j].set(vec[k]);
    1912           0 :             j = k;
    1913           0 :         } while (j != i);
    1914             : 
    1915             :         // We could assert the loop invariant that |i == keys[i].elementIndex|
    1916             :         // here if we synced |keys[i].elementIndex|.  But doing so would render
    1917             :         // the assertion vacuous, so don't bother, even in debug builds.
    1918           0 :         vec[i].set(tv);
    1919             :     }
    1920             : 
    1921           0 :     return true;
    1922             : }
    1923             : 
    1924             : /*
    1925             :  * Sort Values as strings.
    1926             :  *
    1927             :  * To minimize #conversions, SortLexicographically() first converts all Values
    1928             :  * to strings at once, then sorts the elements by these cached strings.
    1929             :  */
    1930             : static bool
    1931           0 : SortLexicographically(JSContext* cx, MutableHandle<GCVector<Value>> vec, size_t len)
    1932             : {
    1933           0 :     MOZ_ASSERT(vec.length() >= len);
    1934             : 
    1935           0 :     StringBuffer sb(cx);
    1936           0 :     Vector<StringifiedElement, 0, TempAllocPolicy> strElements(cx);
    1937             : 
    1938             :     /* MergeSort uses the upper half as scratch space. */
    1939           0 :     if (!strElements.resize(2 * len))
    1940           0 :         return false;
    1941             : 
    1942             :     /* Convert Values to strings. */
    1943           0 :     size_t cursor = 0;
    1944           0 :     for (size_t i = 0; i < len; i++) {
    1945           0 :         if (!CheckForInterrupt(cx))
    1946           0 :             return false;
    1947             : 
    1948           0 :         if (!ValueToStringBuffer(cx, vec[i], sb))
    1949           0 :             return false;
    1950             : 
    1951           0 :         strElements[i] = { cursor, sb.length(), i };
    1952           0 :         cursor = sb.length();
    1953             :     }
    1954             : 
    1955             :     /* Sort Values in vec alphabetically. */
    1956           0 :     return MergeSortByKey(strElements.begin(), len, strElements.begin() + len,
    1957           0 :                           SortComparatorStringifiedElements(cx, sb), vec);
    1958             : }
    1959             : 
    1960             : /*
    1961             :  * Sort Values as numbers.
    1962             :  *
    1963             :  * To minimize #conversions, SortNumerically first converts all Values to
    1964             :  * numerics at once, then sorts the elements by these cached numerics.
    1965             :  */
    1966             : static bool
    1967           0 : SortNumerically(JSContext* cx, MutableHandle<GCVector<Value>> vec, size_t len,
    1968             :                 ComparatorMatchResult comp)
    1969             : {
    1970           0 :     MOZ_ASSERT(vec.length() >= len);
    1971             : 
    1972           0 :     Vector<NumericElement, 0, TempAllocPolicy> numElements(cx);
    1973             : 
    1974             :     /* MergeSort uses the upper half as scratch space. */
    1975           0 :     if (!numElements.resize(2 * len))
    1976           0 :         return false;
    1977             : 
    1978             :     /* Convert Values to numerics. */
    1979           0 :     for (size_t i = 0; i < len; i++) {
    1980           0 :         if (!CheckForInterrupt(cx))
    1981           0 :             return false;
    1982             : 
    1983             :         double dv;
    1984           0 :         if (!ToNumber(cx, vec[i], &dv))
    1985           0 :             return false;
    1986             : 
    1987           0 :         numElements[i] = { dv, i };
    1988             :     }
    1989             : 
    1990             :     /* Sort Values in vec numerically. */
    1991           0 :     return MergeSortByKey(numElements.begin(), len, numElements.begin() + len,
    1992           0 :                           SortComparatorNumerics[comp], vec);
    1993             : }
    1994             : 
    1995             : static bool
    1996           0 : FillWithUndefined(JSContext* cx, HandleObject obj, uint32_t start, uint32_t count)
    1997             : {
    1998           0 :     MOZ_ASSERT(start < start + count, "count > 0 and start + count doesn't overflow");
    1999             : 
    2000             :     do {
    2001             :         // Unboxed arrays can't store undefined values, so don't try to
    2002             :         // optimize them here.
    2003           0 :         if (!obj->is<NativeObject>())
    2004           0 :             break;
    2005           0 :         if (ObjectMayHaveExtraIndexedProperties(obj))
    2006           0 :             break;
    2007             : 
    2008           0 :         NativeObject* nobj = &obj->as<NativeObject>();
    2009           0 :         if (nobj->denseElementsAreFrozen())
    2010           0 :             break;
    2011             : 
    2012           0 :         if (obj->is<ArrayObject>() &&
    2013           0 :             !obj->as<ArrayObject>().lengthIsWritable() &&
    2014           0 :             start + count >= obj->as<ArrayObject>().length())
    2015             :         {
    2016           0 :             break;
    2017             :         }
    2018             : 
    2019           0 :         DenseElementResult result = nobj->ensureDenseElements(cx, start, count);
    2020           0 :         if (result != DenseElementResult::Success) {
    2021           0 :             if (result == DenseElementResult::Failure)
    2022           0 :                 return false;
    2023           0 :             MOZ_ASSERT(result == DenseElementResult::Incomplete);
    2024           0 :             break;
    2025             :         }
    2026             : 
    2027           0 :         if (obj->is<ArrayObject>() && start + count >= obj->as<ArrayObject>().length())
    2028           0 :             obj->as<ArrayObject>().setLengthInt32(start + count);
    2029             : 
    2030           0 :         for (uint32_t i = 0; i < count; i++)
    2031           0 :             nobj->setDenseElementWithType(cx, start + i, UndefinedHandleValue);
    2032             : 
    2033           0 :         return true;
    2034             :     } while (false);
    2035             : 
    2036           0 :     for (uint32_t i = 0; i < count; i++) {
    2037           0 :         if (!CheckForInterrupt(cx) || !SetArrayElement(cx, obj, start + i, UndefinedHandleValue))
    2038           0 :             return false;
    2039             :     }
    2040             : 
    2041           0 :     return true;
    2042             : }
    2043             : 
    2044             : bool
    2045          15 : js::array_sort(JSContext* cx, unsigned argc, Value* vp)
    2046             : {
    2047          15 :     CallArgs args = CallArgsFromVp(argc, vp);
    2048             : 
    2049          30 :     RootedValue fval(cx);
    2050             : 
    2051          15 :     if (args.hasDefined(0)) {
    2052          14 :         if (args[0].isPrimitive()) {
    2053           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_SORT_ARG);
    2054           0 :             return false;
    2055             :         }
    2056          14 :         fval = args[0];     /* non-default compare function */
    2057             :     } else {
    2058           1 :         fval.setNull();
    2059             :     }
    2060             : 
    2061          30 :     RootedObject obj(cx, ToObject(cx, args.thisv()));
    2062          15 :     if (!obj)
    2063           0 :         return false;
    2064             : 
    2065          15 :     ComparatorMatchResult comp = MatchNumericComparator(cx, fval);
    2066          15 :     if (comp == Match_Failure)
    2067           0 :         return false;
    2068             : 
    2069          15 :     if (!fval.isNull() && comp == Match_None) {
    2070             :         /*
    2071             :          * Non-optimized user supplied comparators perform much better when
    2072             :          * called from within a self-hosted sorting function.
    2073             :          */
    2074          28 :         FixedInvokeArgs<1> args2(cx);
    2075          14 :         args2[0].set(fval);
    2076             : 
    2077          28 :         RootedValue thisv(cx, ObjectValue(*obj));
    2078          14 :         return CallSelfHostedFunction(cx, cx->names().ArraySort, thisv, args2, args.rval());
    2079             :     }
    2080             : 
    2081             :     uint64_t length;
    2082           1 :     if (!GetLengthProperty(cx, obj, &length))
    2083           0 :         return false;
    2084           1 :     if (length < 2) {
    2085             :         /* [] and [a] remain unchanged when sorted. */
    2086           0 :         args.rval().setObject(*obj);
    2087           0 :         return true;
    2088             :     }
    2089             : 
    2090           1 :     if (length > UINT32_MAX) {
    2091           0 :         ReportAllocationOverflow(cx);
    2092           0 :         return false;
    2093             :     }
    2094           1 :     uint32_t len = uint32_t(length);
    2095             : 
    2096             :     /*
    2097             :      * We need a temporary array of 2 * len Value to hold the array elements
    2098             :      * and the scratch space for merge sort. Check that its size does not
    2099             :      * overflow size_t, which would allow for indexing beyond the end of the
    2100             :      * malloc'd vector.
    2101             :      */
    2102             : #if JS_BITS_PER_WORD == 32
    2103             :     if (size_t(len) > size_t(-1) / (2 * sizeof(Value))) {
    2104             :         ReportAllocationOverflow(cx);
    2105             :         return false;
    2106             :     }
    2107             : #endif
    2108             : 
    2109             :     size_t n, undefs;
    2110             :     {
    2111           2 :         Rooted<GCVector<Value>> vec(cx, GCVector<Value>(cx));
    2112           1 :         if (!vec.reserve(2 * size_t(len)))
    2113           0 :             return false;
    2114             : 
    2115             :         /*
    2116             :          * By ECMA 262, 15.4.4.11, a property that does not exist (which we
    2117             :          * call a "hole") is always greater than an existing property with
    2118             :          * value undefined and that is always greater than any other property.
    2119             :          * Thus to sort holes and undefs we simply count them, sort the rest
    2120             :          * of elements, append undefs after them and then make holes after
    2121             :          * undefs.
    2122             :          */
    2123           1 :         undefs = 0;
    2124           1 :         bool allStrings = true;
    2125           1 :         bool allInts = true;
    2126           1 :         bool extraIndexed = ObjectMayHaveExtraIndexedProperties(obj);
    2127           2 :         RootedValue v(cx);
    2128           7 :         for (uint32_t i = 0; i < len; i++) {
    2129           6 :             if (!CheckForInterrupt(cx))
    2130           0 :                 return false;
    2131             : 
    2132             :             bool hole;
    2133           6 :             if (!HasAndGetElement(cx, obj, i, &hole, &v))
    2134           0 :                 return false;
    2135           6 :             if (hole)
    2136           0 :                 continue;
    2137           6 :             if (v.isUndefined()) {
    2138           0 :                 ++undefs;
    2139           0 :                 continue;
    2140             :             }
    2141           6 :             vec.infallibleAppend(v);
    2142           6 :             allStrings = allStrings && v.isString();
    2143           6 :             allInts = allInts && v.isInt32();
    2144             :         }
    2145             : 
    2146             :         /*
    2147             :          * If the array only contains holes, we're done.  But if it contains
    2148             :          * undefs, those must be sorted to the front of the array.
    2149             :          */
    2150           1 :         n = vec.length();
    2151           1 :         if (n == 0 && undefs == 0) {
    2152           0 :             args.rval().setObject(*obj);
    2153           0 :             return true;
    2154             :         }
    2155             : 
    2156             :         /* Here len == n + undefs + number_of_holes. */
    2157           1 :         if (fval.isNull()) {
    2158             :             /*
    2159             :              * Sort using the default comparator converting all elements to
    2160             :              * strings.
    2161             :              */
    2162           1 :             if (allStrings) {
    2163           1 :                 JS_ALWAYS_TRUE(vec.resize(n * 2));
    2164           1 :                 if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorStrings(cx)))
    2165           0 :                     return false;
    2166           0 :             } else if (allInts) {
    2167           0 :                 JS_ALWAYS_TRUE(vec.resize(n * 2));
    2168           0 :                 if (!MergeSort(vec.begin(), n, vec.begin() + n,
    2169           0 :                                SortComparatorLexicographicInt32())) {
    2170           0 :                     return false;
    2171             :                 }
    2172             :             } else {
    2173           0 :                 if (!SortLexicographically(cx, &vec, n))
    2174           0 :                     return false;
    2175             :             }
    2176             :         } else {
    2177           0 :             if (allInts) {
    2178           0 :                 JS_ALWAYS_TRUE(vec.resize(n * 2));
    2179           0 :                 if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorInt32s[comp]))
    2180           0 :                     return false;
    2181             :             } else {
    2182           0 :                 if (!SortNumerically(cx, &vec, n, comp))
    2183           0 :                     return false;
    2184             :             }
    2185             :         }
    2186             : 
    2187           2 :         ShouldUpdateTypes updateTypes = !extraIndexed && (allStrings || allInts)
    2188           2 :                                         ? ShouldUpdateTypes::DontUpdate
    2189           1 :                                         : ShouldUpdateTypes::Update;
    2190           1 :         if (!SetArrayElements(cx, obj, 0, uint32_t(n), vec.begin(), updateTypes))
    2191           0 :             return false;
    2192             :     }
    2193             : 
    2194             :     /* Set undefs that sorted after the rest of elements. */
    2195           1 :     if (undefs > 0) {
    2196           0 :         if (!FillWithUndefined(cx, obj, n, undefs))
    2197           0 :             return false;
    2198           0 :         n += undefs;
    2199             :     }
    2200             : 
    2201             :     /* Re-create any holes that sorted to the end of the array. */
    2202           1 :     while (len > n) {
    2203           0 :         if (!CheckForInterrupt(cx) || !DeletePropertyOrThrow(cx, obj, --len))
    2204           0 :             return false;
    2205             :     }
    2206           1 :     args.rval().setObject(*obj);
    2207           1 :     return true;
    2208             : }
    2209             : 
    2210             : bool
    2211           0 : js::NewbornArrayPush(JSContext* cx, HandleObject obj, const Value& v)
    2212             : {
    2213           0 :     Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
    2214             : 
    2215           0 :     MOZ_ASSERT(!v.isMagic());
    2216           0 :     MOZ_ASSERT(arr->lengthIsWritable());
    2217             : 
    2218           0 :     uint32_t length = arr->length();
    2219           0 :     MOZ_ASSERT(length <= arr->getDenseCapacity());
    2220             : 
    2221           0 :     if (!arr->ensureElements(cx, length + 1))
    2222           0 :         return false;
    2223             : 
    2224           0 :     arr->setDenseInitializedLength(length + 1);
    2225           0 :     arr->setLengthInt32(length + 1);
    2226           0 :     arr->initDenseElementWithType(cx, length, v);
    2227           0 :     return true;
    2228             : }
    2229             : 
    2230             : // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
    2231             : // 22.1.3.18 Array.prototype.push ( ...items )
    2232             : bool
    2233        1929 : js::array_push(JSContext* cx, unsigned argc, Value* vp)
    2234             : {
    2235        3858 :     AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.push");
    2236        1929 :     CallArgs args = CallArgsFromVp(argc, vp);
    2237             : 
    2238             :     // Step 1.
    2239        3858 :     RootedObject obj(cx, ToObject(cx, args.thisv()));
    2240        1929 :     if (!obj)
    2241           0 :         return false;
    2242             : 
    2243             :     // Step 2.
    2244             :     uint64_t length;
    2245        1929 :     if (!GetLengthProperty(cx, obj, &length))
    2246           0 :         return false;
    2247             : 
    2248        1929 :     if (!ObjectMayHaveExtraIndexedProperties(obj) && length <= UINT32_MAX) {
    2249             :         DenseElementResult result =
    2250        5787 :             SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(length),
    2251        5787 :                                                       args.array(), args.length());
    2252        1929 :         if (result != DenseElementResult::Incomplete) {
    2253        1929 :             if (result == DenseElementResult::Failure)
    2254           0 :                 return false;
    2255             : 
    2256        1929 :             uint32_t newlength = uint32_t(length) + args.length();
    2257        1929 :             args.rval().setNumber(newlength);
    2258             : 
    2259             :             // SetOrExtendAnyBoxedOrUnboxedDenseElements takes care of updating the
    2260             :             // length for boxed and unboxed arrays. Handle updates to the length of
    2261             :             // non-arrays here.
    2262        1929 :             if (!IsBoxedOrUnboxedArray(obj)) {
    2263           0 :                 MOZ_ASSERT(obj->is<NativeObject>());
    2264           0 :                 return SetLengthProperty(cx, obj, newlength);
    2265             :             }
    2266             : 
    2267        1929 :             return true;
    2268             :         }
    2269             :     }
    2270             : 
    2271             :     // Step 5.
    2272           0 :     uint64_t newlength = length + args.length();
    2273           0 :     if (newlength >= uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT)) {
    2274           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TOO_LONG_ARRAY);
    2275           0 :         return false;
    2276             :     }
    2277             : 
    2278             :     // Steps 3-6.
    2279           0 :     if (!SetArrayElements(cx, obj, length, args.length(), args.array()))
    2280           0 :         return false;
    2281             : 
    2282             :     // Steps 7-8.
    2283           0 :     args.rval().setNumber(double(newlength));
    2284           0 :     return SetLengthProperty(cx, obj, newlength);
    2285             : }
    2286             : 
    2287             : // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
    2288             : // 22.1.3.17 Array.prototype.pop ( )
    2289             : bool
    2290           1 : js::array_pop(JSContext* cx, unsigned argc, Value* vp)
    2291             : {
    2292           2 :     AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.pop");
    2293           1 :     CallArgs args = CallArgsFromVp(argc, vp);
    2294             : 
    2295             :     // Step 1.
    2296           2 :     RootedObject obj(cx, ToObject(cx, args.thisv()));
    2297           1 :     if (!obj)
    2298           0 :         return false;
    2299             : 
    2300             :     // Step 2.
    2301             :     uint64_t index;
    2302           1 :     if (!GetLengthProperty(cx, obj, &index))
    2303           0 :         return false;
    2304             : 
    2305             :     // Steps 3-4.
    2306           1 :     if (index == 0) {
    2307             :         // Step 3.b.
    2308           0 :         args.rval().setUndefined();
    2309             :     } else {
    2310             :         // Steps 4.a-b.
    2311           1 :         index--;
    2312             : 
    2313             :         // Steps 4.c, 4.f.
    2314           1 :         if (!GetArrayElement(cx, obj, index, args.rval()))
    2315           0 :             return false;
    2316             : 
    2317             :         // Steps 4.d.
    2318           1 :         if (!DeletePropertyOrThrow(cx, obj, index))
    2319           0 :             return false;
    2320             :     }
    2321             : 
    2322             :     // Steps 3.a, 4.e.
    2323           1 :     return SetLengthProperty(cx, obj, index);
    2324             : }
    2325             : 
    2326             : template <JSValueType Type>
    2327             : static inline DenseElementResult
    2328           0 : ShiftMoveBoxedOrUnboxedDenseElements(JSObject* obj)
    2329             : {
    2330           0 :     MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj));
    2331             : 
    2332           0 :     size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
    2333           0 :     MOZ_ASSERT(initlen > 0);
    2334             : 
    2335             :     if (Type == JSVAL_TYPE_MAGIC) {
    2336           0 :         if (!obj->as<NativeObject>().tryShiftDenseElements(1))
    2337           0 :             obj->as<NativeObject>().moveDenseElementsNoPreBarrier(0, 1, initlen - 1);
    2338             :     } else {
    2339           0 :         uint8_t* data = obj->as<UnboxedArrayObject>().elements();
    2340           0 :         size_t elementSize = UnboxedTypeSize(Type);
    2341           0 :         memmove(data, data + elementSize, (initlen - 1) * elementSize);
    2342             :     }
    2343             : 
    2344           0 :     return DenseElementResult::Success;
    2345             : }
    2346             : 
    2347           0 : DefineBoxedOrUnboxedFunctor1(ShiftMoveBoxedOrUnboxedDenseElements, JSObject*);
    2348             : 
    2349             : void
    2350           0 : js::ArrayShiftMoveElements(JSObject* obj)
    2351             : {
    2352           0 :     MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().lengthIsWritable());
    2353             : 
    2354           0 :     ShiftMoveBoxedOrUnboxedDenseElementsFunctor functor(obj);
    2355           0 :     JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success);
    2356           0 : }
    2357             : 
    2358             : template <JSValueType Type>
    2359             : DenseElementResult
    2360           8 : ArrayShiftDenseKernel(JSContext* cx, HandleObject obj, MutableHandleValue rval)
    2361             : {
    2362           8 :     if (ObjectMayHaveExtraIndexedProperties(obj))
    2363           0 :         return DenseElementResult::Incomplete;
    2364             : 
    2365           8 :     if (MaybeInIteration(obj, cx))
    2366           0 :         return DenseElementResult::Incomplete;
    2367             : 
    2368           8 :     size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
    2369           8 :     if (initlen == 0)
    2370           1 :         return DenseElementResult::Incomplete;
    2371             : 
    2372           7 :     rval.set(GetBoxedOrUnboxedDenseElement<Type>(obj, 0));
    2373           7 :     if (rval.isMagic(JS_ELEMENTS_HOLE))
    2374           4 :         rval.setUndefined();
    2375             : 
    2376             :     if (Type == JSVAL_TYPE_MAGIC) {
    2377           7 :         if (obj->as<NativeObject>().tryShiftDenseElements(1))
    2378           7 :             return DenseElementResult::Success;
    2379             :     }
    2380             : 
    2381           0 :     DenseElementResult result = MoveBoxedOrUnboxedDenseElements<Type>(cx, obj, 0, 1, initlen - 1);
    2382           0 :     if (result != DenseElementResult::Success)
    2383           0 :         return result;
    2384             : 
    2385           0 :     SetBoxedOrUnboxedInitializedLength<Type>(cx, obj, initlen - 1);
    2386           0 :     return DenseElementResult::Success;
    2387             : }
    2388             : 
    2389          16 : DefineBoxedOrUnboxedFunctor3(ArrayShiftDenseKernel,
    2390             :                              JSContext*, HandleObject, MutableHandleValue);
    2391             : 
    2392             : // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
    2393             : // 22.1.3.22 Array.prototype.shift ( )
    2394             : bool
    2395           8 : js::array_shift(JSContext* cx, unsigned argc, Value* vp)
    2396             : {
    2397          16 :     AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.shift");
    2398           8 :     CallArgs args = CallArgsFromVp(argc, vp);
    2399             : 
    2400             :     // Step 1.
    2401          16 :     RootedObject obj(cx, ToObject(cx, args.thisv()));
    2402           8 :     if (!obj)
    2403           0 :         return false;
    2404             : 
    2405             :     // Step 2.
    2406             :     uint64_t len;
    2407           8 :     if (!GetLengthProperty(cx, obj, &len))
    2408           0 :         return false;
    2409             : 
    2410             :     // Step 3.
    2411           8 :     if (len == 0) {
    2412             :         // Step 3.a.
    2413           0 :         if (!SetLengthProperty(cx, obj, uint32_t(0)))
    2414           0 :             return false;
    2415             : 
    2416             :         // Step 3.b.
    2417           0 :         args.rval().setUndefined();
    2418           0 :         return true;
    2419             :     }
    2420             : 
    2421           8 :     uint64_t newlen = len - 1;
    2422             : 
    2423             :     /* Fast paths. */
    2424             :     uint64_t startIndex;
    2425           8 :     ArrayShiftDenseKernelFunctor functor(cx, obj, args.rval());
    2426           8 :     DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
    2427           8 :     if (result != DenseElementResult::Incomplete) {
    2428           7 :         if (result == DenseElementResult::Failure)
    2429           0 :             return false;
    2430             : 
    2431           7 :         if (len <= UINT32_MAX)
    2432           7 :             return SetLengthProperty(cx, obj, newlen);
    2433             : 
    2434           0 :         startIndex = UINT32_MAX - 1;
    2435             :     } else {
    2436             :         // Steps 4, 9.
    2437           1 :         if (!GetElement(cx, obj, 0, args.rval()))
    2438           0 :             return false;
    2439             : 
    2440           1 :         startIndex = 0;
    2441             :     }
    2442             : 
    2443             :     // Steps 5-6.
    2444           2 :     RootedValue value(cx);
    2445          30 :     for (uint64_t i = startIndex; i < newlen; i++) {
    2446          29 :         if (!CheckForInterrupt(cx))
    2447           0 :             return false;
    2448             :         bool hole;
    2449          29 :         if (!HasAndGetElement(cx, obj, i + 1, &hole, &value))
    2450           0 :             return false;
    2451          29 :         if (hole) {
    2452          29 :             if (!DeletePropertyOrThrow(cx, obj, i))
    2453           0 :                 return false;
    2454             :         } else {
    2455           0 :             if (!SetArrayElement(cx, obj, i, value))
    2456           0 :                 return false;
    2457             :         }
    2458             :     }
    2459             : 
    2460             :     // Step 7.
    2461           1 :     if (!DeletePropertyOrThrow(cx, obj, newlen))
    2462           0 :         return false;
    2463             : 
    2464             :     // Step 8.
    2465           1 :     return SetLengthProperty(cx, obj, newlen);
    2466             : }
    2467             : 
    2468             : // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
    2469             : // 22.1.3.29 Array.prototype.unshift ( ...items )
    2470             : bool
    2471          31 : js::array_unshift(JSContext* cx, unsigned argc, Value* vp)
    2472             : {
    2473          62 :     AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.unshift");
    2474          31 :     CallArgs args = CallArgsFromVp(argc, vp);
    2475             : 
    2476             :     // Step 1.
    2477          62 :     RootedObject obj(cx, ToObject(cx, args.thisv()));
    2478          31 :     if (!obj)
    2479           0 :         return false;
    2480             : 
    2481             :     // Step 2.
    2482             :     uint64_t length;
    2483          31 :     if (!GetLengthProperty(cx, obj, &length))
    2484           0 :         return false;
    2485             : 
    2486             :     // Steps 3-4.
    2487          31 :     if (args.length() > 0) {
    2488             :         // Only include a fast path for native objects. Unboxed arrays can't
    2489             :         // be optimized here because unshifting temporarily places holes at
    2490             :         // the start of the array.
    2491             :         // TODO: Implement unboxed array optimization similar to the one in
    2492             :         // array_splice_impl(), unshift() is a special version of splice():
    2493             :         // arr.unshift(...values) ~= arr.splice(0, 0, ...values).
    2494          31 :         bool optimized = false;
    2495             :         do {
    2496          31 :             if (length > UINT32_MAX)
    2497           0 :                 break;
    2498          31 :             if (!obj->isNative())
    2499           0 :                 break;
    2500          31 :             if (ObjectMayHaveExtraIndexedProperties(obj))
    2501          17 :                 break;
    2502          14 :             if (MaybeInIteration(obj, cx))
    2503           0 :                 break;
    2504          14 :             NativeObject* nobj = &obj->as<NativeObject>();
    2505          14 :             if (nobj->denseElementsAreFrozen())
    2506           0 :                 break;
    2507          14 :             if (nobj->is<ArrayObject>() && !nobj->as<ArrayObject>().lengthIsWritable())
    2508           0 :                 break;
    2509          14 :             if (!nobj->tryUnshiftDenseElements(args.length())) {
    2510          14 :                 DenseElementResult result = nobj->ensureDenseElements(cx, uint32_t(length), args.length());
    2511          14 :                 if (result != DenseElementResult::Success) {
    2512           0 :                     if (result == DenseElementResult::Failure)
    2513           0 :                         return false;
    2514           0 :                     MOZ_ASSERT(result == DenseElementResult::Incomplete);
    2515           0 :                     break;
    2516             :                 }
    2517          14 :                 if (length > 0)
    2518          14 :                     nobj->moveDenseElements(args.length(), 0, uint32_t(length));
    2519             :             }
    2520          28 :             for (uint32_t i = 0; i < args.length(); i++)
    2521          14 :                 nobj->setDenseElementWithType(cx, i, args[i]);
    2522          14 :             optimized = true;
    2523             :         } while (false);
    2524             : 
    2525          31 :         if (!optimized) {
    2526          17 :             if (length > 0) {
    2527          17 :                 uint64_t last = length;
    2528          17 :                 uint64_t upperIndex = last + args.length();
    2529             : 
    2530             :                 // Step 4.a.
    2531          17 :                 if (upperIndex >= uint64_t(DOUBLE_INTEGRAL_PRECISION_LIMIT)) {
    2532           0 :                     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TOO_LONG_ARRAY);
    2533           0 :                     return false;
    2534             :                 }
    2535             : 
    2536             :                 // Steps 4.b-c.
    2537          34 :                 RootedValue value(cx);
    2538          32 :                 do {
    2539          49 :                     --last; --upperIndex;
    2540          49 :                     if (!CheckForInterrupt(cx))
    2541           0 :                         return false;
    2542             :                     bool hole;
    2543          49 :                     if (!HasAndGetElement(cx, obj, last, &hole, &value))
    2544           0 :                         return false;
    2545          49 :                     if (hole) {
    2546           0 :                         if (!DeletePropertyOrThrow(cx, obj, upperIndex))
    2547           0 :                             return false;
    2548             :                     } else {
    2549          49 :                         if (!SetArrayElement(cx, obj, upperIndex, value))
    2550           0 :                             return false;
    2551             :                     }
    2552          49 :                 } while (last != 0);
    2553             :             }
    2554             : 
    2555             :             // Steps 4.d-f.
    2556             :             /* Copy from args to the bottom of the array. */
    2557          17 :             if (!SetArrayElements(cx, obj, 0, args.length(), args.array()))
    2558           0 :                 return false;
    2559             :         }
    2560             :     }
    2561             : 
    2562             :     // Step 5.
    2563          31 :     uint64_t newlength = length + args.length();
    2564          31 :     if (!SetLengthProperty(cx, obj, newlength))
    2565           0 :         return false;
    2566             : 
    2567             :     // Step 6.
    2568             :     /* Follow Perl by returning the new array length. */
    2569          31 :     args.rval().setNumber(double(newlength));
    2570          31 :     return true;
    2571             : }
    2572             : 
    2573             : enum class ArrayAccess {
    2574             :     Read, Write
    2575             : };
    2576             : 
    2577             : /*
    2578             :  * Returns true if this is a dense or unboxed array whose properties ending at
    2579             :  * |endIndex| (exclusive) may be accessed (get, set, delete) directly through
    2580             :  * its contiguous vector of elements without fear of getters, setters, etc.
    2581             :  * along the prototype chain, or of enumerators requiring notification of
    2582             :  * modifications.
    2583             :  */
    2584             : template <ArrayAccess Access>
    2585             : static bool
    2586          75 : CanOptimizeForDenseStorage(HandleObject arr, uint64_t endIndex, JSContext* cx)
    2587             : {
    2588             :     /* If the desired properties overflow dense storage, we can't optimize. */
    2589          75 :     if (endIndex > UINT32_MAX)
    2590           0 :         return false;
    2591             : 
    2592             :     if (Access == ArrayAccess::Read) {
    2593             :         /*
    2594             :          * Dense storage read access is possible for any packed array as long
    2595             :          * as we only access properties within the initialized length. In all
    2596             :          * other cases we need to ensure there are no other indexed properties
    2597             :          * on this object or on the prototype chain. Callers are required to
    2598             :          * clamp the read length, so it doesn't exceed the initialized length.
    2599             :          */
    2600          87 :         return (IsPackedArray(arr) && endIndex <= GetAnyBoxedOrUnboxedInitializedLength(arr)) ||
    2601          87 :                !ObjectMayHaveExtraIndexedProperties(arr);
    2602             :     }
    2603             : 
    2604             :     /* There's no optimizing possible if it's not an array. */
    2605          20 :     if (!IsBoxedOrUnboxedArray(arr))
    2606           0 :         return false;
    2607             : 
    2608             :     /* If the length is non-writable, always pick the slow path */
    2609          20 :     if (arr->is<ArrayObject>()) {
    2610          20 :         if (!arr->as<ArrayObject>().lengthIsWritable())
    2611           0 :             return false;
    2612             : 
    2613          20 :         MOZ_ASSERT(!arr->as<ArrayObject>().denseElementsAreFrozen(),
    2614             :                    "writable length implies elements are not frozen");
    2615             :     }
    2616             : 
    2617             :     /* Also pick the slow path if the object is being iterated over. */
    2618          20 :     if (MaybeInIteration(arr, cx))
    2619           0 :         return false;
    2620             : 
    2621             :     /* Or we attempt to write to indices outside the initialized length. */
    2622          20 :     if (endIndex > GetAnyBoxedOrUnboxedInitializedLength(arr))
    2623           0 :         return false;
    2624             : 
    2625             :     /*
    2626             :      * Now watch out for getters and setters along the prototype chain or in
    2627             :      * other indexed properties on the object. Packed arrays don't have any
    2628             :      * other indexed properties by definition.
    2629             :      */
    2630          20 :     return IsPackedArray(arr) || !ObjectMayHaveExtraIndexedProperties(arr);
    2631             : }
    2632             : 
    2633             : static JSObject*
    2634           4 : CopyDenseArrayElements(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t count)
    2635             : {
    2636           4 :     size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj);
    2637           4 :     MOZ_ASSERT(initlen <= UINT32_MAX, "initialized length shouldn't exceed UINT32_MAX");
    2638           4 :     uint32_t newlength = 0;
    2639           4 :     if (initlen > begin)
    2640           2 :         newlength = Min<uint32_t>(initlen - begin, count);
    2641             : 
    2642           4 :     JSObject* narr = NewFullyAllocatedArrayTryReuseGroup(cx, obj, newlength);
    2643           4 :     if (!narr)
    2644           0 :         return nullptr;
    2645           4 :     SetAnyBoxedOrUnboxedArrayLength(cx, narr, count);
    2646             : 
    2647           4 :     if (newlength) {
    2648             :         DebugOnly<DenseElementResult> result =
    2649           2 :             CopyAnyBoxedOrUnboxedDenseElements(cx, narr, obj, 0, begin, newlength);
    2650           1 :         MOZ_ASSERT(result.value == DenseElementResult::Success);
    2651             :     }
    2652           4 :     return narr;
    2653             : }
    2654             : 
    2655             : static bool
    2656          32 : CopyArrayElements(JSContext* cx, HandleObject obj, uint64_t begin, uint64_t count,
    2657             :                   HandleObject result)
    2658             : {
    2659          32 :     MOZ_ASSERT(IsBoxedOrUnboxedArray(result), "result is a newly allocated array object");
    2660          32 :     MOZ_ASSERT(GetAnyBoxedOrUnboxedArrayLength(result) == count);
    2661             : 
    2662          32 :     uint64_t startIndex = 0;
    2663          64 :     RootedValue value(cx);
    2664             : 
    2665             :     // Use dense storage for new indexed properties where possible.
    2666          32 :     if (result->is<ArrayObject>()) {
    2667          32 :         HandleArrayObject nresult = result.as<ArrayObject>();
    2668             : 
    2669          32 :         uint32_t index = 0;
    2670          32 :         uint32_t limit = Min<uint32_t>(count, JSID_INT_MAX);
    2671          84 :         for (; index < limit; index++) {
    2672             :             bool hole;
    2673         104 :             if (!CheckForInterrupt(cx) ||
    2674         104 :                 !HasAndGetElement(cx, obj, begin + index, &hole, &value))
    2675             :             {
    2676           0 :                 return false;
    2677             :             }
    2678             : 
    2679          26 :             if (!hole) {
    2680          26 :                 DenseElementResult edResult = nresult->ensureDenseElements(cx, index, 1);
    2681          26 :                 if (edResult != DenseElementResult::Success) {
    2682           0 :                     if (edResult == DenseElementResult::Failure)
    2683           0 :                         return false;
    2684             : 
    2685           0 :                     MOZ_ASSERT(edResult == DenseElementResult::Incomplete);
    2686           0 :                     if (!DefineElement(cx, nresult, index, value))
    2687           0 :                         return false;
    2688             : 
    2689           0 :                     break;
    2690             :                 }
    2691          26 :                 nresult->setDenseElementWithType(cx, index, value);
    2692             :             }
    2693             :         }
    2694             : 
    2695          32 :         startIndex = index + 1;
    2696             :     }
    2697             : 
    2698             :     // Copy any remaining elements.
    2699          32 :     for (uint64_t i = startIndex; i < count; i++) {
    2700             :         bool hole;
    2701           0 :         if (!CheckForInterrupt(cx) ||
    2702           0 :             !HasAndGetElement(cx, obj, begin + i, &hole, &value))
    2703             :         {
    2704           0 :             return false;
    2705             :         }
    2706             : 
    2707           0 :         if (!hole && !DefineArrayElement(cx, result, i, value))
    2708           0 :             return false;
    2709             :     }
    2710          32 :     return true;
    2711             : }
    2712             : 
    2713             : static bool
    2714          20 : array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUsed)
    2715             : {
    2716          40 :     AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.splice");
    2717          20 :     CallArgs args = CallArgsFromVp(argc, vp);
    2718             : 
    2719             :     /* Step 1. */
    2720          40 :     RootedObject obj(cx, ToObject(cx, args.thisv()));
    2721          20 :     if (!obj)
    2722           0 :         return false;
    2723             : 
    2724             :     /* Step 2. */
    2725             :     uint64_t len;
    2726          20 :     if (!GetLengthProperty(cx, obj, &len))
    2727           0 :         return false;
    2728             : 
    2729             :     /* Step 3. */
    2730             :     double relativeStart;
    2731          20 :     if (!ToInteger(cx, args.get(0), &relativeStart))
    2732           0 :         return false;
    2733             : 
    2734             :     /* Step 4. */
    2735             :     uint64_t actualStart;
    2736          20 :     if (relativeStart < 0)
    2737           1 :         actualStart = Max(len + relativeStart, 0.0);
    2738             :     else
    2739          19 :         actualStart = Min(relativeStart, double(len));
    2740             : 
    2741             :     /* Step 5. */
    2742             :     uint64_t actualDeleteCount;
    2743          20 :     if (args.length() == 0) {
    2744             :         /* Step 5.b. */
    2745           0 :         actualDeleteCount = 0;
    2746          20 :     } else if (args.length() == 1) {
    2747             :         /* Step 6.b. */
    2748           0 :         actualDeleteCount = len - actualStart;
    2749             :     } else {
    2750             :         /* Steps 7.b. */
    2751             :         double deleteCountDouble;
    2752          40 :         RootedValue cnt(cx, args[1]);
    2753          20 :         if (!ToInteger(cx, cnt, &deleteCountDouble))
    2754           0 :             return false;
    2755             : 
    2756             :         /* Step 7.c. */
    2757          20 :         actualDeleteCount = Min(Max(deleteCountDouble, 0.0), double(len - actualStart));
    2758             : 
    2759             :         /* Step 8. */
    2760          20 :         uint32_t insertCount = args.length() - 2;
    2761          20 :         if (len + insertCount - actualDeleteCount >= DOUBLE_INTEGRAL_PRECISION_LIMIT) {
    2762           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TOO_LONG_ARRAY);
    2763           0 :             return false;
    2764             :         }
    2765             :     }
    2766             : 
    2767          20 :     MOZ_ASSERT(actualStart + actualDeleteCount <= len);
    2768             : 
    2769          40 :     RootedObject arr(cx);
    2770          20 :     if (IsArraySpecies(cx, obj)) {
    2771          20 :         if (actualDeleteCount > UINT32_MAX) {
    2772           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
    2773           0 :             return false;
    2774             :         }
    2775          20 :         uint32_t count = uint32_t(actualDeleteCount);
    2776             : 
    2777          20 :         if (CanOptimizeForDenseStorage<ArrayAccess::Read>(obj, actualStart + count, cx)) {
    2778          20 :             MOZ_ASSERT(actualStart <= UINT32_MAX,
    2779             :                        "if actualStart + count <= UINT32_MAX, then actualStart <= UINT32_MAX");
    2780          20 :             if (returnValueIsUsed) {
    2781             :                 /* Steps 9-12. */
    2782           1 :                 arr = CopyDenseArrayElements(cx, obj, uint32_t(actualStart), count);
    2783           1 :                 if (!arr)
    2784           0 :                     return false;
    2785             :             }
    2786             :         } else {
    2787             :             /* Step 9. */
    2788           0 :             arr = NewFullyAllocatedArrayTryReuseGroup(cx, obj, count);
    2789           0 :             if (!arr)
    2790           0 :                 return false;
    2791             : 
    2792             :             /* Steps 10-11. */
    2793           0 :             if (!CopyArrayElements(cx, obj, actualStart, count, arr))
    2794           0 :                 return false;
    2795             : 
    2796             :             /* Step 12 (implicit). */
    2797             :         }
    2798             :     } else {
    2799             :         /* Steps 9. */
    2800           0 :         if (!ArraySpeciesCreate(cx, obj, actualDeleteCount, &arr))
    2801           0 :             return false;
    2802             : 
    2803             :         /* Steps 10, 11, 11.d. */
    2804           0 :         RootedValue fromValue(cx);
    2805           0 :         for (uint64_t k = 0; k < actualDeleteCount; k++) {
    2806             :             /* Step 11.a (implicit). */
    2807             : 
    2808           0 :             if (!CheckForInterrupt(cx))
    2809           0 :                 return false;
    2810             : 
    2811             :             /* Steps 11.b, 11.c.i. */
    2812             :             bool hole;
    2813           0 :             if (!HasAndGetElement(cx, obj, actualStart + k, &hole, &fromValue))
    2814           0 :                 return false;
    2815             : 
    2816             :             /* Step 11.c. */
    2817           0 :             if (!hole) {
    2818             :                 /* Step 11.c.ii. */
    2819           0 :                 if (!DefineArrayElement(cx, arr, k, fromValue))
    2820           0 :                     return false;
    2821             :             }
    2822             :         }
    2823             : 
    2824             :         /* Step 12. */
    2825           0 :         if (!SetLengthProperty(cx, arr, actualDeleteCount))
    2826           0 :             return false;
    2827             :     }
    2828             : 
    2829             :     /* Step 14. */
    2830          20 :     uint32_t itemCount = (args.length() >= 2) ? (args.length() - 2) : 0;
    2831          20 :     uint64_t finalLength = len - actualDeleteCount + itemCount;
    2832             : 
    2833          20 :     if (itemCount < actualDeleteCount) {
    2834             :         /* Step 15: the array is being shrunk. */
    2835           2 :         uint64_t sourceIndex = actualStart + actualDeleteCount;
    2836           2 :         uint64_t targetIndex = actualStart + itemCount;
    2837             : 
    2838           2 :         if (CanOptimizeForDenseStorage<ArrayAccess::Write>(obj, len, cx)) {
    2839           2 :             MOZ_ASSERT(sourceIndex <= len && targetIndex <= len && len <= UINT32_MAX,
    2840             :                        "sourceIndex and targetIndex are uint32 array indices");
    2841           2 :             MOZ_ASSERT(finalLength < len, "finalLength is strictly less than len");
    2842             : 
    2843             :             /* Steps 15.a-b. */
    2844           2 :             if (targetIndex != 0 ||
    2845           2 :                 !obj->is<NativeObject>() ||
    2846           0 :                 !obj->as<NativeObject>().tryShiftDenseElements(sourceIndex))
    2847             :             {
    2848             :                 DenseElementResult result =
    2849           4 :                     MoveAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(targetIndex),
    2850             :                                                        uint32_t(sourceIndex),
    2851           4 :                                                        uint32_t(len - sourceIndex));
    2852           2 :                 MOZ_ASSERT(result != DenseElementResult::Incomplete);
    2853           2 :                 if (result == DenseElementResult::Failure)
    2854           0 :                     return false;
    2855             :             }
    2856             : 
    2857             :             /* Steps 15.c-d. */
    2858           2 :             SetAnyBoxedOrUnboxedInitializedLength(cx, obj, uint32_t(finalLength));
    2859             :         } else {
    2860             :             /*
    2861             :              * This is all very slow if the length is very large. We don't yet
    2862             :              * have the ability to iterate in sorted order, so we just do the
    2863             :              * pessimistic thing and let CheckForInterrupt handle the
    2864             :              * fallout.
    2865             :              */
    2866             : 
    2867             :             /* Steps 15.a-b. */
    2868           0 :             RootedValue fromValue(cx);
    2869           0 :             for (uint64_t from = sourceIndex, to = targetIndex; from < len; from++, to++) {
    2870             :                 /* Steps 15.b.i-ii (implicit). */
    2871             : 
    2872           0 :                 if (!CheckForInterrupt(cx))
    2873           0 :                     return false;
    2874             : 
    2875             :                 /* Steps 15.b.iii, 15.b.iv.1. */
    2876             :                 bool hole;
    2877           0 :                 if (!HasAndGetElement(cx, obj, from, &hole, &fromValue))
    2878           0 :                     return false;
    2879             : 
    2880             :                 /* Steps 15.b.iv. */
    2881           0 :                 if (hole) {
    2882             :                     /* Steps 15.b.v.1. */
    2883           0 :                     if (!DeletePropertyOrThrow(cx, obj, to))
    2884           0 :                         return false;
    2885             :                 } else {
    2886             :                     /* Step 15.b.iv.2. */
    2887           0 :                     if (!SetArrayElement(cx, obj, to, fromValue))
    2888           0 :                         return false;
    2889             :                 }
    2890             :             }
    2891             : 
    2892             :             /* Steps 15.c-d. */
    2893           0 :             for (uint64_t k = len; k > finalLength; k--) {
    2894             :                 /* Steps 15.d.i-ii. */
    2895           0 :                 if (!DeletePropertyOrThrow(cx, obj, k - 1))
    2896           0 :                     return false;
    2897             :             }
    2898             :         }
    2899          18 :     } else if (itemCount > actualDeleteCount) {
    2900          18 :         MOZ_ASSERT(actualDeleteCount <= UINT32_MAX);
    2901          18 :         uint32_t deleteCount = uint32_t(actualDeleteCount);
    2902             : 
    2903             :         /* Step 16. */
    2904             : 
    2905             :         /*
    2906             :          * Optimize only if the array is already dense and we can extend it to
    2907             :          * its new length.  It would be wrong to extend the elements here for a
    2908             :          * number of reasons.
    2909             :          *
    2910             :          * First, this could cause us to fall into the fast-path below.  This
    2911             :          * would cause elements to be moved into places past the non-writable
    2912             :          * length.  And when the dense initialized length is updated, that'll
    2913             :          * cause the |in| operator to think that those elements actually exist,
    2914             :          * even though, properly, setting them must fail.
    2915             :          *
    2916             :          * Second, extending the elements here will trigger assertions inside
    2917             :          * ensureDenseElements that the elements aren't being extended past the
    2918             :          * length of a non-writable array.  This is because extending elements
    2919             :          * will extend capacity -- which might extend them past a non-writable
    2920             :          * length, violating the |capacity <= length| invariant for such
    2921             :          * arrays.  And that would make the various JITted fast-path method
    2922             :          * implementations of [].push, [].unshift, and so on wrong.
    2923             :          *
    2924             :          * If the array length is non-writable, this method *will* throw.  For
    2925             :          * simplicity, have the slow-path code do it.  (Also note that the slow
    2926             :          * path may validly *not* throw -- if all the elements being moved are
    2927             :          * holes.)
    2928             :          */
    2929          54 :         if (obj->is<ArrayObject>() &&
    2930          36 :             !ObjectMayHaveExtraIndexedProperties(obj) &&
    2931          18 :             len <= UINT32_MAX)
    2932             :         {
    2933          36 :             Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
    2934          18 :             if (arr->lengthIsWritable()) {
    2935             :                 DenseElementResult result =
    2936          18 :                     arr->ensureDenseElements(cx, uint32_t(len), itemCount - deleteCount);
    2937          18 :                 if (result == DenseElementResult::Failure)
    2938           0 :                     return false;
    2939             :             }
    2940             :         }
    2941             : 
    2942          18 :         if (CanOptimizeForDenseStorage<ArrayAccess::Write>(obj, finalLength, cx)) {
    2943          18 :             MOZ_ASSERT((actualStart + actualDeleteCount) <= len && len <= UINT32_MAX,
    2944             :                        "start and deleteCount are uint32 array indices");
    2945          18 :             MOZ_ASSERT(actualStart + itemCount <= UINT32_MAX,
    2946             :                        "can't overflow because |len - actualDeleteCount + itemCount <= UINT32_MAX| "
    2947             :                        "and |actualStart <= len - actualDeleteCount| are both true");
    2948          18 :             uint32_t start = uint32_t(actualStart);
    2949          18 :             uint32_t length = uint32_t(len);
    2950             : 
    2951             :             DenseElementResult result =
    2952          36 :                 MoveAnyBoxedOrUnboxedDenseElements(cx, obj, start + itemCount,
    2953             :                                                    start + deleteCount,
    2954          54 :                                                    length - (start + deleteCount));
    2955          18 :             MOZ_ASSERT(result != DenseElementResult::Incomplete);
    2956          18 :             if (result == DenseElementResult::Failure)
    2957           0 :                 return false;
    2958             : 
    2959             :             /* Steps 16.a-b. */
    2960          18 :             SetAnyBoxedOrUnboxedInitializedLength(cx, obj, uint32_t(finalLength));
    2961             :         } else {
    2962           0 :             RootedValue fromValue(cx);
    2963           0 :             for (uint64_t k = len - actualDeleteCount; k > actualStart; k--) {
    2964           0 :                 if (!CheckForInterrupt(cx))
    2965           0 :                     return false;
    2966             : 
    2967             :                 /* Step 16.b.i. */
    2968           0 :                 uint64_t from = k + actualDeleteCount - 1;
    2969             : 
    2970             :                 /* Step 16.b.ii. */
    2971           0 :                 uint64_t to = k + itemCount - 1;
    2972             : 
    2973             :                 /* Steps 16.b.iii, 16.b.iv.1. */
    2974             :                 bool hole;
    2975           0 :                 if (!HasAndGetElement(cx, obj, from, &hole, &fromValue))
    2976           0 :                     return false;
    2977             : 
    2978             :                 /* Steps 16.b.iv. */
    2979           0 :                 if (hole) {
    2980             :                     /* Step 16.b.v.1. */
    2981           0 :                     if (!DeletePropertyOrThrow(cx, obj, to))
    2982           0 :                         return false;
    2983             :                 } else {
    2984             :                     /* Step 16.b.iv.2. */
    2985           0 :                     if (!SetArrayElement(cx, obj, to, fromValue))
    2986           0 :                         return false;
    2987             :                 }
    2988             :             }
    2989             :         }
    2990             :     }
    2991             : 
    2992             :     /* Step 13 (reordered). */
    2993          20 :     Value* items = args.array() + 2;
    2994             : 
    2995             :     /* Steps 17-18. */
    2996          20 :     if (!SetArrayElements(cx, obj, actualStart, itemCount, items))
    2997           0 :         return false;
    2998             : 
    2999             :     /* Step 19. */
    3000          20 :     if (!SetLengthProperty(cx, obj, finalLength))
    3001           0 :         return false;
    3002             : 
    3003             :     /* Step 20. */
    3004          20 :     if (returnValueIsUsed)
    3005           1 :         args.rval().setObject(*arr);
    3006             : 
    3007          20 :     return true;
    3008             : }
    3009             : 
    3010             : /* ES 2016 draft Mar 25, 2016 22.1.3.26. */
    3011             : bool
    3012           1 : js::array_splice(JSContext* cx, unsigned argc, Value* vp)
    3013             : {
    3014           1 :     return array_splice_impl(cx, argc, vp, true);
    3015             : }
    3016             : 
    3017             : static bool
    3018          19 : array_splice_noRetVal(JSContext* cx, unsigned argc, Value* vp)
    3019             : {
    3020          19 :     return array_splice_impl(cx, argc, vp, false);
    3021             : }
    3022             : 
    3023             : struct SortComparatorIndexes
    3024             : {
    3025           0 :     bool operator()(uint32_t a, uint32_t b, bool* lessOrEqualp) {
    3026           0 :         *lessOrEqualp = (a <= b);
    3027           0 :         return true;
    3028             :     }
    3029             : };
    3030             : 
    3031             : // Returns all indexed properties in the range [begin, end) found on |obj| or
    3032             : // its proto chain. This function does not handle proxies, objects with
    3033             : // resolve/lookupProperty hooks or indexed getters, as those can introduce
    3034             : // new properties. In those cases, *success is set to |false|.
    3035             : static bool
    3036           0 : GetIndexedPropertiesInRange(JSContext* cx, HandleObject obj, uint64_t begin, uint64_t end,
    3037             :                             Vector<uint32_t>& indexes, bool* success)
    3038             : {
    3039           0 :     *success = false;
    3040             : 
    3041             :     // TODO: Add IdIsIndex with support for large indices.
    3042           0 :     if (end > UINT32_MAX)
    3043           0 :         return true;
    3044           0 :     MOZ_ASSERT(begin <= UINT32_MAX);
    3045             : 
    3046             :     // First, look for proxies or class hooks that can introduce extra
    3047             :     // properties.
    3048           0 :     JSObject* pobj = obj;
    3049           0 :     do {
    3050           0 :         if (!pobj->isNative() || pobj->getClass()->getResolve() || pobj->getOpsLookupProperty())
    3051           0 :             return true;
    3052             :     } while ((pobj = pobj->staticPrototype()));
    3053             : 
    3054             :     // Collect indexed property names.
    3055           0 :     pobj = obj;
    3056           0 :     do {
    3057             :         // Append dense elements.
    3058           0 :         NativeObject* nativeObj = &pobj->as<NativeObject>();
    3059           0 :         uint32_t initLen = nativeObj->getDenseInitializedLength();
    3060           0 :         for (uint32_t i = begin; i < initLen && i < end; i++) {
    3061           0 :             if (nativeObj->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE))
    3062           0 :                 continue;
    3063           0 :             if (!indexes.append(i))
    3064           0 :                 return false;
    3065             :         }
    3066             : 
    3067             :         // Append typed array elements.
    3068           0 :         if (pobj->is<TypedArrayObject>()) {
    3069           0 :             uint32_t len = pobj->as<TypedArrayObject>().length();
    3070           0 :             for (uint32_t i = begin; i < len && i < end; i++) {
    3071           0 :                 if (!indexes.append(i))
    3072           0 :                     return false;
    3073             :             }
    3074             :         }
    3075             : 
    3076             :         // Append sparse elements.
    3077           0 :         if (pobj->isIndexed()) {
    3078           0 :             Shape::Range<NoGC> r(pobj->as<NativeObject>().lastProperty());
    3079           0 :             for (; !r.empty(); r.popFront()) {
    3080           0 :                 Shape& shape = r.front();
    3081           0 :                 jsid id = shape.propid();
    3082             :                 uint32_t i;
    3083           0 :                 if (!IdIsIndex(id, &i))
    3084           0 :                     continue;
    3085             : 
    3086           0 :                 if (!(begin <= i && i < end))
    3087           0 :                     continue;
    3088             : 
    3089             :                 // Watch out for getters, they can add new properties.
    3090           0 :                 if (!shape.hasDefaultGetter())
    3091           0 :                     return true;
    3092             : 
    3093           0 :                 if (!indexes.append(i))
    3094           0 :                     return false;
    3095             :             }
    3096             :         }
    3097             :     } while ((pobj = pobj->staticPrototype()));
    3098             : 
    3099             :     // Sort the indexes.
    3100           0 :     Vector<uint32_t> tmp(cx);
    3101           0 :     size_t n = indexes.length();
    3102           0 :     if (!tmp.resize(n))
    3103           0 :         return false;
    3104           0 :     if (!MergeSort(indexes.begin(), n, tmp.begin(), SortComparatorIndexes()))
    3105           0 :         return false;
    3106             : 
    3107             :     // Remove duplicates.
    3108           0 :     if (!indexes.empty()) {
    3109           0 :         uint32_t last = 0;
    3110           0 :         for (size_t i = 1, len = indexes.length(); i < len; i++) {
    3111           0 :             uint32_t elem = indexes[i];
    3112           0 :             if (indexes[last] != elem) {
    3113           0 :                 last++;
    3114           0 :                 indexes[last] = elem;
    3115             :             }
    3116             :         }
    3117           0 :         if (!indexes.resize(last + 1))
    3118           0 :             return false;
    3119             :     }
    3120             : 
    3121           0 :     *success = true;
    3122           0 :     return true;
    3123             : }
    3124             : 
    3125             : static bool
    3126           0 : SliceSparse(JSContext* cx, HandleObject obj, uint64_t begin, uint64_t end, HandleObject result)
    3127             : {
    3128           0 :     MOZ_ASSERT(begin <= end);
    3129             : 
    3130           0 :     Vector<uint32_t> indexes(cx);
    3131             :     bool success;
    3132           0 :     if (!GetIndexedPropertiesInRange(cx, obj, begin, end, indexes, &success))
    3133           0 :         return false;
    3134             : 
    3135           0 :     if (!success)
    3136           0 :         return CopyArrayElements(cx, obj, begin, end - begin, result);
    3137             : 
    3138           0 :     MOZ_ASSERT(end <= UINT32_MAX,
    3139             :                "indices larger than UINT32_MAX should be rejected by GetIndexedPropertiesInRange");
    3140             : 
    3141           0 :     RootedValue value(cx);
    3142           0 :     for (uint32_t index : indexes) {
    3143           0 :         MOZ_ASSERT(begin <= index && index < end);
    3144             : 
    3145             :         bool hole;
    3146           0 :         if (!HasAndGetElement(cx, obj, index, &hole, &value))
    3147           0 :             return false;
    3148             : 
    3149           0 :         if (!hole && !DefineElement(cx, result, index - uint32_t(begin), value))
    3150           0 :             return false;
    3151             :     }
    3152             : 
    3153           0 :     return true;
    3154             : }
    3155             : 
    3156             : template <typename T, typename ArrayLength>
    3157             : static inline ArrayLength
    3158          43 : NormalizeSliceTerm(T value, ArrayLength length)
    3159             : {
    3160          43 :     if (value < 0) {
    3161           1 :         value += length;
    3162           1 :         if (value < 0)
    3163           0 :             return 0;
    3164          42 :     } else if (double(value) > double(length)) {
    3165           1 :         return length;
    3166             :     }
    3167          42 :     return ArrayLength(value);
    3168             : }
    3169             : 
    3170             : static bool
    3171          35 : ArraySliceOrdinary(JSContext* cx, HandleObject obj, uint64_t begin, uint64_t end,
    3172             :                    MutableHandleValue rval)
    3173             : {
    3174          35 :     if (begin > end)
    3175           0 :         begin = end;
    3176             : 
    3177          35 :     if ((end - begin) > UINT32_MAX) {
    3178           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
    3179           0 :         return false;
    3180             :     }
    3181          35 :     uint32_t count = uint32_t(end - begin);
    3182             : 
    3183          35 :     if (CanOptimizeForDenseStorage<ArrayAccess::Read>(obj, end, cx)) {
    3184           3 :         MOZ_ASSERT(begin <= UINT32_MAX, "if end <= UINT32_MAX, then begin <= UINT32_MAX");
    3185           3 :         JSObject* narr = CopyDenseArrayElements(cx, obj, uint32_t(begin), count);
    3186           3 :         if (!narr)
    3187           0 :             return false;
    3188             : 
    3189           3 :         rval.setObject(*narr);
    3190           3 :         return true;
    3191             :     }
    3192             : 
    3193          64 :     RootedObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, count));
    3194          32 :     if (!narr)
    3195           0 :         return false;
    3196             : 
    3197          32 :     if (end <= UINT32_MAX) {
    3198          32 :         if (js::GetElementsOp op = obj->getOpsGetElements()) {
    3199           0 :             ElementAdder adder(cx, narr, count, ElementAdder::CheckHasElemPreserveHoles);
    3200           0 :             if (!op(cx, obj, uint32_t(begin), uint32_t(end), &adder))
    3201           0 :                 return false;
    3202             : 
    3203           0 :             rval.setObject(*narr);
    3204           0 :             return true;
    3205             :         }
    3206             :     }
    3207             : 
    3208          32 :     if (obj->isNative() && obj->isIndexed() && count > 1000) {
    3209           0 :         if (!SliceSparse(cx, obj, begin, end, narr))
    3210           0 :             return false;
    3211             :     } else {
    3212          32 :         if (!CopyArrayElements(cx, obj, begin, count, narr))
    3213           0 :             return false;
    3214             :     }
    3215             : 
    3216          32 :     rval.setObject(*narr);
    3217          32 :     return true;
    3218             : }
    3219             : 
    3220             : /* ES 2016 draft Mar 25, 2016 22.1.3.23. */
    3221             : bool
    3222          37 : js::array_slice(JSContext* cx, unsigned argc, Value* vp)
    3223             : {
    3224          74 :     AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.slice");
    3225          37 :     CallArgs args = CallArgsFromVp(argc, vp);
    3226             : 
    3227             :     /* Step 1. */
    3228          74 :     RootedObject obj(cx, ToObject(cx, args.thisv()));
    3229          37 :     if (!obj)
    3230           0 :         return false;
    3231             : 
    3232             :     /* Step 2. */
    3233             :     uint64_t length;
    3234          37 :     if (!GetLengthProperty(cx, obj, &length))
    3235           0 :         return false;
    3236             : 
    3237          37 :     uint64_t k = 0;
    3238          37 :     uint64_t final = length;
    3239          37 :     if (args.length() > 0) {
    3240             :         double d;
    3241             :         /* Step 3. */
    3242          37 :         if (!ToInteger(cx, args[0], &d))
    3243           0 :             return false;
    3244             : 
    3245             :         /* Step 4. */
    3246          37 :         k = NormalizeSliceTerm(d, length);
    3247             : 
    3248          37 :         if (args.hasDefined(1)) {
    3249             :             /* Step 5. */
    3250           6 :             if (!ToInteger(cx, args[1], &d))
    3251           0 :                 return false;
    3252             : 
    3253             :             /* Step 6. */
    3254           6 :             final = NormalizeSliceTerm(d, length);
    3255             :         }
    3256             :     }
    3257             : 
    3258          37 :     if (IsArraySpecies(cx, obj)) {
    3259             :         /* Steps 7-12: Optimized for ordinary array. */
    3260          35 :         return ArraySliceOrdinary(cx, obj, k, final, args.rval());
    3261             :     }
    3262             : 
    3263             :     /* Step 7. */
    3264           2 :     uint64_t count = final > k ? final - k : 0;
    3265             : 
    3266             :     /* Step 8. */
    3267           4 :     RootedObject arr(cx);
    3268           2 :     if (!ArraySpeciesCreate(cx, obj, count, &arr))
    3269           0 :         return false;
    3270             : 
    3271             :     /* Step 9. */
    3272           2 :     uint64_t n = 0;
    3273             : 
    3274             :     /* Step 10. */
    3275           4 :     RootedValue kValue(cx);
    3276           2 :     while (k < final) {
    3277           0 :         if (!CheckForInterrupt(cx))
    3278           0 :             return false;
    3279             : 
    3280             :         /* Steps 10.a-b, and 10.c.i. */
    3281             :         bool kNotPresent;
    3282           0 :         if (!HasAndGetElement(cx, obj, k, &kNotPresent, &kValue))
    3283           0 :             return false;
    3284             : 
    3285             :         /* Step 10.c. */
    3286           0 :         if (!kNotPresent) {
    3287             :             /* Steps 10.c.ii. */
    3288           0 :             if (!DefineArrayElement(cx, arr, n, kValue))
    3289           0 :                 return false;
    3290             :         }
    3291             :         /* Step 10.d. */
    3292           0 :         k++;
    3293             : 
    3294             :         /* Step 10.e. */
    3295           0 :         n++;
    3296             :     }
    3297             : 
    3298             :     /* Step 11. */
    3299           2 :     if (!SetLengthProperty(cx, arr, n))
    3300           0 :         return false;
    3301             : 
    3302             :     /* Step 12. */
    3303           2 :     args.rval().setObject(*arr);
    3304           2 :     return true;
    3305             : }
    3306             : 
    3307             : template <JSValueType Type>
    3308             : DenseElementResult
    3309           0 : ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t endArg, JSObject* result)
    3310             : {
    3311           0 :     uint32_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
    3312             : 
    3313           0 :     uint32_t begin = NormalizeSliceTerm(beginArg, length);
    3314           0 :     uint32_t end = NormalizeSliceTerm(endArg, length);
    3315             : 
    3316           0 :     if (begin > end)
    3317           0 :         begin = end;
    3318             : 
    3319           0 :     uint32_t count = end - begin;
    3320           0 :     size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
    3321           0 :     if (initlen > begin) {
    3322           0 :         uint32_t newlength = Min<uint32_t>(initlen - begin, count);
    3323           0 :         if (newlength) {
    3324           0 :             DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements<Type>(cx, result, newlength);
    3325           0 :             if (rv != DenseElementResult::Success)
    3326           0 :                 return rv;
    3327           0 :             CopyBoxedOrUnboxedDenseElements<Type, Type>(cx, result, obj, 0, begin, newlength);
    3328             :         }
    3329             :     }
    3330             : 
    3331           0 :     SetAnyBoxedOrUnboxedArrayLength(cx, result, count);
    3332           0 :     return DenseElementResult::Success;
    3333             : }
    3334             : 
    3335           0 : DefineBoxedOrUnboxedFunctor5(ArraySliceDenseKernel,
    3336             :                              JSContext*, JSObject*, int32_t, int32_t, JSObject*);
    3337             : 
    3338             : JSObject*
    3339           0 : js::array_slice_dense(JSContext* cx, HandleObject obj, int32_t begin, int32_t end,
    3340             :                       HandleObject result)
    3341             : {
    3342           0 :     if (result && IsArraySpecies(cx, obj)) {
    3343           0 :         ArraySliceDenseKernelFunctor functor(cx, obj, begin, end, result);
    3344           0 :         DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, result);
    3345           0 :         MOZ_ASSERT(rv != DenseElementResult::Incomplete);
    3346           0 :         return rv == DenseElementResult::Success ? result : nullptr;
    3347             :     }
    3348             : 
    3349             :     // Slower path if the JIT wasn't able to allocate an object inline.
    3350           0 :     JS::AutoValueArray<4> argv(cx);
    3351           0 :     argv[0].setUndefined();
    3352           0 :     argv[1].setObject(*obj);
    3353           0 :     argv[2].setInt32(begin);
    3354           0 :     argv[3].setInt32(end);
    3355           0 :     if (!array_slice(cx, 2, argv.begin()))
    3356           0 :         return nullptr;
    3357           0 :     return &argv[0].toObject();
    3358             : }
    3359             : 
    3360             : static bool
    3361         336 : array_isArray(JSContext* cx, unsigned argc, Value* vp)
    3362             : {
    3363         336 :     CallArgs args = CallArgsFromVp(argc, vp);
    3364         336 :     bool isArray = false;
    3365         336 :     if (args.get(0).isObject()) {
    3366          76 :         RootedObject obj(cx, &args[0].toObject());
    3367          38 :         if (!IsArray(cx, obj, &isArray))
    3368           0 :             return false;
    3369             :     }
    3370         336 :     args.rval().setBoolean(isArray);
    3371         336 :     return true;
    3372             : }
    3373             : 
    3374             : static bool
    3375          24 : ArrayFromCallArgs(JSContext* cx, CallArgs& args, HandleObject proto = nullptr)
    3376             : {
    3377          24 :     JSObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length(), proto);
    3378          24 :     if (!obj)
    3379           0 :         return false;
    3380             : 
    3381          24 :     args.rval().setObject(*obj);
    3382          24 :     return true;
    3383             : }
    3384             : 
    3385             : static bool
    3386           0 : array_of(JSContext* cx, unsigned argc, Value* vp)
    3387             : {
    3388           0 :     CallArgs args = CallArgsFromVp(argc, vp);
    3389             : 
    3390           0 :     if (IsArrayConstructor(args.thisv()) || !IsConstructor(args.thisv())) {
    3391             :         // IsArrayConstructor(this) will usually be true in practice. This is
    3392             :         // the most common path.
    3393           0 :         return ArrayFromCallArgs(cx, args);
    3394             :     }
    3395             : 
    3396             :     // Step 4.
    3397           0 :     RootedObject obj(cx);
    3398             :     {
    3399           0 :         FixedConstructArgs<1> cargs(cx);
    3400             : 
    3401           0 :         cargs[0].setNumber(args.length());
    3402             : 
    3403           0 :         if (!Construct(cx, args.thisv(), cargs, args.thisv(), &obj))
    3404           0 :             return false;
    3405             :     }
    3406             : 
    3407             :     // Step 8.
    3408           0 :     for (unsigned k = 0; k < args.length(); k++) {
    3409           0 :         if (!DefineElement(cx, obj, k, args[k]))
    3410           0 :             return false;
    3411             :     }
    3412             : 
    3413             :     // Steps 9-10.
    3414           0 :     if (!SetLengthProperty(cx, obj, args.length()))
    3415           0 :         return false;
    3416             : 
    3417             :     // Step 11.
    3418           0 :     args.rval().setObject(*obj);
    3419           0 :     return true;
    3420             : }
    3421             : 
    3422             : const JSJitInfo js::array_splice_info = {
    3423             :   { (JSJitGetterOp)array_splice_noRetVal },
    3424             :   { 0 }, /* unused */
    3425             :   { 0 }, /* unused */
    3426             :   JSJitInfo::IgnoresReturnValueNative,
    3427             :   JSJitInfo::AliasEverything,
    3428             :   JSVAL_TYPE_UNDEFINED,
    3429             : };
    3430             : 
    3431             : static const JSFunctionSpec array_methods[] = {
    3432             : #if JS_HAS_TOSOURCE
    3433             :     JS_FN(js_toSource_str,      array_toSource,     0,0),
    3434             : #endif
    3435             :     JS_SELF_HOSTED_FN(js_toString_str, "ArrayToString",      0,0),
    3436             :     JS_FN(js_toLocaleString_str,       array_toLocaleString, 0,0),
    3437             : 
    3438             :     /* Perl-ish methods. */
    3439             :     JS_INLINABLE_FN("join",     array_join,         1,0, ArrayJoin),
    3440             :     JS_FN("reverse",            array_reverse,      0,0),
    3441             :     JS_FN("sort",               array_sort,         1,0),
    3442             :     JS_INLINABLE_FN("push",     array_push,         1,0, ArrayPush),
    3443             :     JS_INLINABLE_FN("pop",      array_pop,          0,0, ArrayPop),
    3444             :     JS_INLINABLE_FN("shift",    array_shift,        0,0, ArrayShift),
    3445             :     JS_FN("unshift",            array_unshift,      1,0),
    3446             :     JS_FNINFO("splice",         array_splice,       &array_splice_info, 2,0),
    3447             : 
    3448             :     /* Pythonic sequence methods. */
    3449             :     JS_SELF_HOSTED_FN("concat",      "ArrayConcat",      1,0),
    3450             :     JS_INLINABLE_FN("slice",    array_slice,        2,0, ArraySlice),
    3451             : 
    3452             :     JS_SELF_HOSTED_FN("lastIndexOf", "ArrayLastIndexOf", 1,0),
    3453             :     JS_SELF_HOSTED_FN("indexOf",     "ArrayIndexOf",     1,0),
    3454             :     JS_SELF_HOSTED_FN("forEach",     "ArrayForEach",     1,0),
    3455             :     JS_SELF_HOSTED_FN("map",         "ArrayMap",         1,0),
    3456             :     JS_SELF_HOSTED_FN("filter",      "ArrayFilter",      1,0),
    3457             :     JS_SELF_HOSTED_FN("reduce",      "ArrayReduce",      1,0),
    3458             :     JS_SELF_HOSTED_FN("reduceRight", "ArrayReduceRight", 1,0),
    3459             :     JS_SELF_HOSTED_FN("some",        "ArraySome",        1,0),
    3460             :     JS_SELF_HOSTED_FN("every",       "ArrayEvery",       1,0),
    3461             : 
    3462             :     /* ES6 additions */
    3463             :     JS_SELF_HOSTED_FN("find",        "ArrayFind",        1,0),
    3464             :     JS_SELF_HOSTED_FN("findIndex",   "ArrayFindIndex",   1,0),
    3465             :     JS_SELF_HOSTED_FN("copyWithin",  "ArrayCopyWithin",  3,0),
    3466             : 
    3467             :     JS_SELF_HOSTED_FN("fill",        "ArrayFill",        3,0),
    3468             : 
    3469             :     JS_SELF_HOSTED_SYM_FN(iterator,  "ArrayValues",      0,0),
    3470             :     JS_SELF_HOSTED_FN("entries",     "ArrayEntries",     0,0),
    3471             :     JS_SELF_HOSTED_FN("keys",        "ArrayKeys",        0,0),
    3472             : #ifdef NIGHTLY_BUILD
    3473             :     JS_SELF_HOSTED_FN("values",      "ArrayValues",      0,0),
    3474             : #endif
    3475             : 
    3476             :     /* ES7 additions */
    3477             :     JS_SELF_HOSTED_FN("includes",    "ArrayIncludes",    2,0),
    3478             :     JS_FS_END
    3479             : };
    3480             : 
    3481             : static const JSFunctionSpec array_static_methods[] = {
    3482             :     JS_INLINABLE_FN("isArray",       array_isArray,        1,0, ArrayIsArray),
    3483             :     JS_SELF_HOSTED_FN("concat",      "ArrayStaticConcat", 2,0),
    3484             :     JS_SELF_HOSTED_FN("lastIndexOf", "ArrayStaticLastIndexOf", 2,0),
    3485             :     JS_SELF_HOSTED_FN("indexOf",     "ArrayStaticIndexOf", 2,0),
    3486             :     JS_SELF_HOSTED_FN("forEach",     "ArrayStaticForEach", 2,0),
    3487             :     JS_SELF_HOSTED_FN("map",         "ArrayStaticMap",   2,0),
    3488             :     JS_SELF_HOSTED_FN("filter",      "ArrayStaticFilter", 2,0),
    3489             :     JS_SELF_HOSTED_FN("every",       "ArrayStaticEvery", 2,0),
    3490             :     JS_SELF_HOSTED_FN("some",        "ArrayStaticSome",  2,0),
    3491             :     JS_SELF_HOSTED_FN("reduce",      "ArrayStaticReduce", 2,0),
    3492             :     JS_SELF_HOSTED_FN("reduceRight", "ArrayStaticReduceRight", 2,0),
    3493             :     JS_SELF_HOSTED_FN("join",        "ArrayStaticJoin", 2,0),
    3494             :     JS_SELF_HOSTED_FN("reverse",     "ArrayStaticReverse", 1,0),
    3495             :     JS_SELF_HOSTED_FN("sort",        "ArrayStaticSort", 2,0),
    3496             :     JS_SELF_HOSTED_FN("push",        "ArrayStaticPush", 2,0),
    3497             :     JS_SELF_HOSTED_FN("pop",         "ArrayStaticPop", 1,0),
    3498             :     JS_SELF_HOSTED_FN("shift",       "ArrayStaticShift", 1,0),
    3499             :     JS_SELF_HOSTED_FN("unshift",     "ArrayStaticUnshift", 2,0),
    3500             :     JS_SELF_HOSTED_FN("splice",      "ArrayStaticSplice", 3,0),
    3501             :     JS_SELF_HOSTED_FN("slice",       "ArrayStaticSlice", 3,0),
    3502             :     JS_SELF_HOSTED_FN("from",        "ArrayFrom", 3,0),
    3503             :     JS_FN("of",                 array_of,           0,0),
    3504             : 
    3505             :     JS_FS_END
    3506             : };
    3507             : 
    3508             : const JSPropertySpec array_static_props[] = {
    3509             :     JS_SELF_HOSTED_SYM_GET(species, "ArraySpecies", 0),
    3510             :     JS_PS_END
    3511             : };
    3512             : 
    3513             : static inline bool
    3514         181 : ArrayConstructorImpl(JSContext* cx, CallArgs& args, bool isConstructor)
    3515             : {
    3516         362 :     RootedObject proto(cx);
    3517         181 :     if (isConstructor) {
    3518          24 :         if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
    3519           0 :             return false;
    3520             :     } else {
    3521             :         // We're emulating |new Array(n)| with |std_Array(n)| in self-hosted JS,
    3522             :         // and the proto should be %ArrayPrototype% regardless of the callee.
    3523         157 :         proto = GlobalObject::getOrCreateArrayPrototype(cx, cx->global());
    3524         157 :         if (!proto)
    3525           0 :             return false;
    3526             :     }
    3527             : 
    3528         181 :     if (args.length() != 1 || !args[0].isNumber())
    3529          24 :         return ArrayFromCallArgs(cx, args, proto);
    3530             : 
    3531             :     uint32_t length;
    3532         157 :     if (args[0].isInt32()) {
    3533         157 :         int32_t i = args[0].toInt32();
    3534         157 :         if (i < 0) {
    3535           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
    3536           0 :             return false;
    3537             :         }
    3538         157 :         length = uint32_t(i);
    3539             :     } else {
    3540           0 :         double d = args[0].toDouble();
    3541           0 :         length = ToUint32(d);
    3542           0 :         if (d != double(length)) {
    3543           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
    3544           0 :             return false;
    3545             :         }
    3546             :     }
    3547             : 
    3548         157 :     JSObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length, proto);
    3549         157 :     if (!obj)
    3550           0 :         return false;
    3551             : 
    3552         157 :     args.rval().setObject(*obj);
    3553         157 :     return true;
    3554             : }
    3555             : 
    3556             : /* ES5 15.4.2 */
    3557             : bool
    3558          24 : js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp)
    3559             : {
    3560          24 :     CallArgs args = CallArgsFromVp(argc, vp);
    3561          24 :     return ArrayConstructorImpl(cx, args, /* isConstructor = */ true);
    3562             : }
    3563             : 
    3564             : bool
    3565         157 : js::array_construct(JSContext* cx, unsigned argc, Value* vp)
    3566             : {
    3567         157 :     CallArgs args = CallArgsFromVp(argc, vp);
    3568         157 :     MOZ_ASSERT(!args.isConstructing());
    3569         157 :     MOZ_ASSERT(args.length() == 1);
    3570         157 :     MOZ_ASSERT(args[0].isNumber());
    3571         157 :     return ArrayConstructorImpl(cx, args, /* isConstructor = */ false);
    3572             : }
    3573             : 
    3574             : JSObject*
    3575           0 : js::ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt)
    3576             : {
    3577           0 :     if (lengthInt < 0) {
    3578           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
    3579           0 :         return nullptr;
    3580             :     }
    3581             : 
    3582           0 :     uint32_t length = uint32_t(lengthInt);
    3583           0 :     return NewPartlyAllocatedArrayTryUseGroup(cx, group, length);
    3584             : }
    3585             : 
    3586             : static JSObject*
    3587         291 : CreateArrayPrototype(JSContext* cx, JSProtoKey key)
    3588             : {
    3589         291 :     MOZ_ASSERT(key == JSProto_Array);
    3590         582 :     RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, cx->global()));
    3591         291 :     if (!proto)
    3592           0 :         return nullptr;
    3593             : 
    3594         582 :     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &ArrayObject::class_,
    3595        1164 :                                                              TaggedProto(proto)));
    3596         291 :     if (!group)
    3597           0 :         return nullptr;
    3598             : 
    3599         582 :     RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayObject::class_, TaggedProto(proto),
    3600         582 :                                                       gc::AllocKind::OBJECT0));
    3601         291 :     if (!shape)
    3602           0 :         return nullptr;
    3603             : 
    3604         582 :     AutoSetNewObjectMetadata metadata(cx);
    3605         582 :     RootedArrayObject arrayProto(cx, ArrayObject::createArray(cx, gc::AllocKind::OBJECT4,
    3606             :                                                               gc::TenuredHeap, shape, group, 0,
    3607         582 :                                                               metadata));
    3608        1455 :     if (!arrayProto ||
    3609        1746 :         !JSObject::setSingleton(cx, arrayProto) ||
    3610        2037 :         !JSObject::setDelegate(cx, arrayProto) ||
    3611         873 :         !AddLengthProperty(cx, arrayProto))
    3612             :     {
    3613           0 :         return nullptr;
    3614             :     }
    3615             : 
    3616             :     /*
    3617             :      * The default 'new' group of Array.prototype is required by type inference
    3618             :      * to have unknown properties, to simplify handling of e.g. heterogenous
    3619             :      * arrays in JSON and script literals and allows setDenseArrayElement to
    3620             :      * be used without updating the indexed type set for such default arrays.
    3621             :      */
    3622         291 :     if (!JSObject::setNewGroupUnknown(cx, &ArrayObject::class_, arrayProto))
    3623           0 :         return nullptr;
    3624             : 
    3625         291 :     return arrayProto;
    3626             : }
    3627             : 
    3628             : static bool
    3629         288 : array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto)
    3630             : {
    3631             :     // Add Array.prototype[@@unscopables]. ECMA-262 draft (2016 Mar 19) 22.1.3.32.
    3632         576 :     RootedObject unscopables(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr, TenuredObject));
    3633         288 :     if (!unscopables)
    3634           0 :         return false;
    3635             : 
    3636         576 :     RootedValue value(cx, BooleanValue(true));
    3637        2304 :     if (!DefineProperty(cx, unscopables, cx->names().copyWithin, value) ||
    3638        2016 :         !DefineProperty(cx, unscopables, cx->names().entries, value) ||
    3639        2016 :         !DefineProperty(cx, unscopables, cx->names().fill, value) ||
    3640        2016 :         !DefineProperty(cx, unscopables, cx->names().find, value) ||
    3641        2016 :         !DefineProperty(cx, unscopables, cx->names().findIndex, value) ||
    3642        2016 :         !DefineProperty(cx, unscopables, cx->names().includes, value) ||
    3643        3168 :         !DefineProperty(cx, unscopables, cx->names().keys, value) ||
    3644        1152 :         !DefineProperty(cx, unscopables, cx->names().values, value))
    3645             :     {
    3646           0 :         return false;
    3647             :     }
    3648             : 
    3649         576 :     RootedId id(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().get(JS::SymbolCode::unscopables)));
    3650         288 :     value.setObject(*unscopables);
    3651         288 :     return DefineProperty(cx, proto, id, value, nullptr, nullptr, JSPROP_READONLY);
    3652             : }
    3653             : 
    3654             : static const ClassOps ArrayObjectClassOps = {
    3655             :     array_addProperty,
    3656             :     nullptr, /* delProperty */
    3657             :     nullptr, /* getProperty */
    3658             :     nullptr, /* setProperty */
    3659             :     nullptr, /* enumerate */
    3660             :     nullptr, /* resolve */
    3661             :     nullptr, /* mayResolve */
    3662             :     nullptr, /* finalize */
    3663             :     nullptr, /* call */
    3664             :     nullptr, /* hasInstance */
    3665             :     nullptr, /* construct */
    3666             :     nullptr, /* trace */
    3667             : };
    3668             : 
    3669             : static const ClassSpec ArrayObjectClassSpec = {
    3670             :     GenericCreateConstructor<ArrayConstructor, 1, AllocKind::FUNCTION, &jit::JitInfo_Array>,
    3671             :     CreateArrayPrototype,
    3672             :     array_static_methods,
    3673             :     array_static_props,
    3674             :     array_methods,
    3675             :     nullptr,
    3676             :     array_proto_finish
    3677             : };
    3678             : 
    3679             : const Class ArrayObject::class_ = {
    3680             :     "Array",
    3681             :     JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_DELAY_METADATA_BUILDER,
    3682             :     &ArrayObjectClassOps,
    3683             :     &ArrayObjectClassSpec
    3684             : };
    3685             : 
    3686             : /*
    3687             :  * Array allocation functions.
    3688             :  */
    3689             : 
    3690             : static inline bool
    3691        5791 : EnsureNewArrayElements(JSContext* cx, ArrayObject* obj, uint32_t length)
    3692             : {
    3693             :     /*
    3694             :      * If ensureElements creates dynamically allocated slots, then having
    3695             :      * fixedSlots is a waste.
    3696             :      */
    3697       11582 :     DebugOnly<uint32_t> cap = obj->getDenseCapacity();
    3698             : 
    3699        5791 :     if (!obj->ensureElements(cx, length))
    3700           0 :         return false;
    3701             : 
    3702        5791 :     MOZ_ASSERT_IF(cap, !obj->hasDynamicElements());
    3703             : 
    3704        5791 :     return true;
    3705             : }
    3706             : 
    3707             : template <uint32_t maxLength>
    3708             : static MOZ_ALWAYS_INLINE ArrayObject*
    3709        5757 : NewArray(JSContext* cx, uint32_t length,
    3710             :          HandleObject protoArg, NewObjectKind newKind = GenericObject)
    3711             : {
    3712        5757 :     gc::AllocKind allocKind = GuessArrayGCKind(length);
    3713        5757 :     MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_));
    3714        5757 :     allocKind = GetBackgroundAllocKind(allocKind);
    3715             : 
    3716       11514 :     RootedObject proto(cx, protoArg);
    3717        5757 :     if (!proto && !GetBuiltinPrototype(cx, JSProto_Array, &proto))
    3718           0 :         return nullptr;
    3719             : 
    3720       11514 :     Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto));
    3721        5757 :     bool isCachable = NewObjectWithTaggedProtoIsCachable(cx, taggedProto, newKind, &ArrayObject::class_);
    3722        5757 :     if (isCachable) {
    3723        4166 :         NewObjectCache& cache = cx->caches().newObjectCache;
    3724        4166 :         NewObjectCache::EntryIndex entry = -1;
    3725        4166 :         if (cache.lookupProto(&ArrayObject::class_, proto, allocKind, &entry)) {
    3726        3277 :             gc::InitialHeap heap = GetInitialHeap(newKind, &ArrayObject::class_);
    3727        3277 :             AutoSetNewObjectMetadata metadata(cx);
    3728        3277 :             JSObject* obj = cache.newObjectFromHit(cx, entry, heap);
    3729        3277 :             if (obj) {
    3730             :                 /* Fixup the elements pointer and length, which may be incorrect. */
    3731        3277 :                 ArrayObject* arr = &obj->as<ArrayObject>();
    3732        3277 :                 arr->setFixedElements();
    3733        3277 :                 arr->setLength(cx, length);
    3734        6544 :                 if (maxLength > 0 &&
    3735        6534 :                     !EnsureNewArrayElements(cx, arr, std::min(maxLength, length)))
    3736             :                 {
    3737           0 :                     return nullptr;
    3738             :                 }
    3739        3277 :                 return arr;
    3740             :             }
    3741             :         }
    3742             :     }
    3743             : 
    3744        4960 :     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &ArrayObject::class_,
    3745        7440 :                                                              TaggedProto(proto)));
    3746        2480 :     if (!group)
    3747           0 :         return nullptr;
    3748             : 
    3749             :     /*
    3750             :      * Get a shape with zero fixed slots, regardless of the size class.
    3751             :      * See JSObject::createArray.
    3752             :      */
    3753        4960 :     RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayObject::class_,
    3754        2480 :                                                       TaggedProto(proto),
    3755        4960 :                                                       gc::AllocKind::OBJECT0));
    3756        2480 :     if (!shape)
    3757           0 :         return nullptr;
    3758             : 
    3759        4960 :     AutoSetNewObjectMetadata metadata(cx);
    3760        4960 :     RootedArrayObject arr(cx, ArrayObject::createArray(cx, allocKind,
    3761             :                                                        GetInitialHeap(newKind, &ArrayObject::class_),
    3762        4960 :                                                        shape, group, length, metadata));
    3763        2480 :     if (!arr)
    3764           0 :         return nullptr;
    3765             : 
    3766        2480 :     if (shape->isEmptyShape()) {
    3767          25 :         if (!AddLengthProperty(cx, arr))
    3768           0 :             return nullptr;
    3769          25 :         shape = arr->lastProperty();
    3770          25 :         EmptyShape::insertInitialShape(cx, shape, proto);
    3771             :     }
    3772             : 
    3773        2480 :     if (newKind == SingletonObject && !JSObject::setSingleton(cx, arr))
    3774           0 :         return nullptr;
    3775             : 
    3776        2480 :     if (isCachable) {
    3777         889 :         NewObjectCache& cache = cx->caches().newObjectCache;
    3778         889 :         NewObjectCache::EntryIndex entry = -1;
    3779         889 :         cache.lookupProto(&ArrayObject::class_, proto, allocKind, &entry);
    3780         889 :         cache.fillProto(entry, &ArrayObject::class_, taggedProto, allocKind, arr);
    3781             :     }
    3782             : 
    3783        2480 :     if (maxLength > 0 && !EnsureNewArrayElements(cx, arr, std::min(maxLength, length)))
    3784           0 :         return nullptr;
    3785             : 
    3786        2480 :     probes::CreateObject(cx, arr);
    3787        2480 :     return arr;
    3788             : }
    3789             : 
    3790             : ArrayObject * JS_FASTCALL
    3791          18 : js::NewDenseEmptyArray(JSContext* cx, HandleObject proto /* = nullptr */,
    3792             :                        NewObjectKind newKind /* = GenericObject */)
    3793             : {
    3794          18 :     return NewArray<0>(cx, 0, proto, newKind);
    3795             : }
    3796             : 
    3797             : ArrayObject * JS_FASTCALL
    3798        2314 : js::NewDenseFullyAllocatedArray(JSContext* cx, uint32_t length,
    3799             :                                 HandleObject proto /* = nullptr */,
    3800             :                                 NewObjectKind newKind /* = GenericObject */)
    3801             : {
    3802        2314 :     return NewArray<UINT32_MAX>(cx, length, proto, newKind);
    3803             : }
    3804             : 
    3805             : ArrayObject * JS_FASTCALL
    3806           0 : js::NewDensePartlyAllocatedArray(JSContext* cx, uint32_t length,
    3807             :                                  HandleObject proto /* = nullptr */,
    3808             :                                  NewObjectKind newKind /* = GenericObject */)
    3809             : {
    3810           0 :     return NewArray<ArrayObject::EagerAllocationMaxLength>(cx, length, proto, newKind);
    3811             : }
    3812             : 
    3813             : ArrayObject * JS_FASTCALL
    3814           6 : js::NewDenseUnallocatedArray(JSContext* cx, uint32_t length,
    3815             :                              HandleObject proto /* = nullptr */,
    3816             :                              NewObjectKind newKind /* = GenericObject */)
    3817             : {
    3818           6 :     return NewArray<0>(cx, length, proto, newKind);
    3819             : }
    3820             : 
    3821             : // values must point at already-rooted Value objects
    3822             : ArrayObject*
    3823        2223 : js::NewDenseCopiedArray(JSContext* cx, uint32_t length, const Value* values,
    3824             :                         HandleObject proto /* = nullptr */,
    3825             :                         NewObjectKind newKind /* = GenericObject */)
    3826             : {
    3827        2223 :     ArrayObject* arr = NewArray<UINT32_MAX>(cx, length, proto, newKind);
    3828        2223 :     if (!arr)
    3829           0 :         return nullptr;
    3830             : 
    3831        2223 :     MOZ_ASSERT(arr->getDenseCapacity() >= length);
    3832             : 
    3833        2223 :     arr->setDenseInitializedLength(values ? length : 0);
    3834             : 
    3835        2223 :     if (values)
    3836        2223 :         arr->initDenseElements(0, values, length);
    3837             : 
    3838        2223 :     return arr;
    3839             : }
    3840             : 
    3841             : ArrayObject*
    3842          58 : js::NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSObject* templateObject)
    3843             : {
    3844         116 :     AutoSetNewObjectMetadata metadata(cx);
    3845          58 :     gc::AllocKind allocKind = GuessArrayGCKind(length);
    3846          58 :     MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_));
    3847          58 :     allocKind = GetBackgroundAllocKind(allocKind);
    3848             : 
    3849         116 :     RootedObjectGroup group(cx, templateObject->group());
    3850         116 :     RootedShape shape(cx, templateObject->as<ArrayObject>().lastProperty());
    3851             : 
    3852          58 :     gc::InitialHeap heap = GetInitialHeap(GenericObject, &ArrayObject::class_);
    3853         116 :     Rooted<ArrayObject*> arr(cx, ArrayObject::createArray(cx, allocKind,
    3854         116 :                                                           heap, shape, group, length, metadata));
    3855          58 :     if (!arr)
    3856           0 :         return nullptr;
    3857             : 
    3858          58 :     if (!EnsureNewArrayElements(cx, arr, length))
    3859           0 :         return nullptr;
    3860             : 
    3861          58 :     probes::CreateObject(cx, arr);
    3862             : 
    3863          58 :     return arr;
    3864             : }
    3865             : 
    3866             : JSObject*
    3867         839 : js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap)
    3868             : {
    3869         839 :     MOZ_ASSERT(!gc::IsInsideNursery(templateObject));
    3870             : 
    3871         839 :     ArrayObject* arr = ArrayObject::createCopyOnWriteArray(cx, heap, templateObject);
    3872         839 :     if (!arr)
    3873           0 :         return nullptr;
    3874             : 
    3875         839 :     probes::CreateObject(cx, arr);
    3876         839 :     return arr;
    3877             : }
    3878             : 
    3879             : // Return a new boxed or unboxed array with the specified length and allocated
    3880             : // capacity (up to maxLength), using the specified group if possible. If the
    3881             : // specified group cannot be used, ensure that the created array at least has
    3882             : // the given [[Prototype]].
    3883             : template <uint32_t maxLength>
    3884             : static inline JSObject*
    3885        1163 : NewArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length,
    3886             :                     NewObjectKind newKind = GenericObject)
    3887             : {
    3888        1163 :     MOZ_ASSERT(newKind != SingletonObject);
    3889             : 
    3890        1163 :     if (group->maybePreliminaryObjects())
    3891          48 :         group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
    3892             : 
    3893        1163 :     if (group->shouldPreTenure() || group->maybePreliminaryObjects())
    3894          48 :         newKind = TenuredObject;
    3895             : 
    3896        2326 :     RootedObject proto(cx, group->proto().toObject());
    3897        1163 :     if (group->maybeUnboxedLayout()) {
    3898           0 :         if (length > UnboxedArrayObject::MaximumCapacity)
    3899           0 :             return NewArray<maxLength>(cx, length, proto, newKind);
    3900           0 :         return UnboxedArrayObject::create(cx, group, length, newKind, maxLength);
    3901             :     }
    3902             : 
    3903        1163 :     ArrayObject* res = NewArray<maxLength>(cx, length, proto, newKind);
    3904        1163 :     if (!res)
    3905           0 :         return nullptr;
    3906             : 
    3907        1163 :     res->setGroup(group);
    3908             : 
    3909             :     // If the length calculation overflowed, make sure that is marked for the
    3910             :     // new group.
    3911        1163 :     if (res->length() > INT32_MAX)
    3912           0 :         res->setLength(cx, res->length());
    3913             : 
    3914        1163 :     if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects())
    3915          48 :         preliminaryObjects->registerNewObject(res);
    3916             : 
    3917        1163 :     return res;
    3918             : }
    3919             : 
    3920             : JSObject*
    3921         990 : js::NewFullyAllocatedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length,
    3922             :                                       NewObjectKind newKind)
    3923             : {
    3924         990 :     return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length, newKind);
    3925             : }
    3926             : 
    3927             : JSObject*
    3928           0 : js::NewPartlyAllocatedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length)
    3929             : {
    3930           0 :     return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length);
    3931             : }
    3932             : 
    3933             : // Return a new array with the default prototype and specified allocated
    3934             : // capacity and length. If possible, try to reuse the group of the input
    3935             : // object. The resulting array will either reuse the input object's group or
    3936             : // will have unknown property types. Additionally, the result will have the
    3937             : // same boxed/unboxed elements representation as the input object, unless
    3938             : // |length| is larger than the input object's initialized length (in which case
    3939             : // UnboxedArrayObject::MaximumCapacity might be exceeded).
    3940             : template <uint32_t maxLength>
    3941             : static inline JSObject*
    3942          42 : NewArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length,
    3943             :                       NewObjectKind newKind = GenericObject)
    3944             : {
    3945          42 :     if (!IsBoxedOrUnboxedArray(obj))
    3946          33 :         return NewArray<maxLength>(cx, length, nullptr, newKind);
    3947             : 
    3948           9 :     if (obj->staticPrototype() != cx->global()->maybeGetArrayPrototype())
    3949           0 :         return NewArray<maxLength>(cx, length, nullptr, newKind);
    3950             : 
    3951          18 :     RootedObjectGroup group(cx, JSObject::getGroup(cx, obj));
    3952           9 :     if (!group)
    3953           0 :         return nullptr;
    3954             : 
    3955           9 :     return NewArrayTryUseGroup<maxLength>(cx, group, length, newKind);
    3956             : }
    3957             : 
    3958             : JSObject*
    3959          10 : js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length,
    3960             :                                         NewObjectKind newKind)
    3961             : {
    3962          10 :     return NewArrayTryReuseGroup<UINT32_MAX>(cx, obj, length, newKind);
    3963             : }
    3964             : 
    3965             : JSObject*
    3966          32 : js::NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, HandleObject obj, size_t length)
    3967             : {
    3968          32 :     return NewArrayTryReuseGroup<ArrayObject::EagerAllocationMaxLength>(cx, obj, length);
    3969             : }
    3970             : 
    3971             : JSObject*
    3972           7 : js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length,
    3973             :                                                    NewObjectKind newKind)
    3974             : {
    3975          14 :     RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
    3976           7 :     if (!group)
    3977           0 :         return nullptr;
    3978           7 :     return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length, newKind);
    3979             : }
    3980             : 
    3981             : JSObject*
    3982         157 : js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto)
    3983             : {
    3984         314 :     RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto));
    3985         157 :     if (!group)
    3986           0 :         return nullptr;
    3987         157 :     return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length);
    3988             : }
    3989             : 
    3990             : bool
    3991        1859 : js::MaybeAnalyzeBeforeCreatingLargeArray(JSContext* cx, HandleObjectGroup group,
    3992             :                                          const Value* vp, size_t length)
    3993             : {
    3994             :     static const size_t EagerPreliminaryObjectAnalysisThreshold = 800;
    3995             : 
    3996             :     // Force analysis to see if an unboxed array can be used when making a
    3997             :     // sufficiently large array, to avoid excessive analysis and copying later
    3998             :     // on. If this is the first array of its group that is being created, first
    3999             :     // make a dummy array with the initial elements of the array we are about
    4000             :     // to make, so there is some basis for the unboxed array analysis.
    4001        1859 :     if (length > EagerPreliminaryObjectAnalysisThreshold) {
    4002           0 :         if (PreliminaryObjectArrayWithTemplate* objects = group->maybePreliminaryObjects()) {
    4003           0 :             if (objects->empty()) {
    4004           0 :                 size_t nlength = Min<size_t>(length, 100);
    4005           0 :                 JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, nlength);
    4006           0 :                 if (!obj)
    4007           0 :                     return false;
    4008             :                 DebugOnly<DenseElementResult> result =
    4009           0 :                     SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, nlength,
    4010           0 :                                                               ShouldUpdateTypes::Update);
    4011           0 :                 MOZ_ASSERT(result.value == DenseElementResult::Success);
    4012             :             }
    4013           0 :             objects->maybeAnalyze(cx, group, /* force = */ true);
    4014             :         }
    4015             :     }
    4016        1859 :     return true;
    4017             : }
    4018             : 
    4019             : JSObject*
    4020         990 : js::NewCopiedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group,
    4021             :                               const Value* vp, size_t length, NewObjectKind newKind,
    4022             :                               ShouldUpdateTypes updateTypes)
    4023             : {
    4024         990 :     if (!MaybeAnalyzeBeforeCreatingLargeArray(cx, group, vp, length))
    4025           0 :         return nullptr;
    4026             : 
    4027         990 :     JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length, newKind);
    4028         990 :     if (!obj)
    4029           0 :         return nullptr;
    4030             : 
    4031             :     DenseElementResult result =
    4032         990 :         SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, length, updateTypes);
    4033         990 :     if (result == DenseElementResult::Failure)
    4034           0 :         return nullptr;
    4035         990 :     if (result == DenseElementResult::Success)
    4036         990 :         return obj;
    4037             : 
    4038           0 :     MOZ_ASSERT(obj->is<UnboxedArrayObject>());
    4039           0 :     if (!UnboxedArrayObject::convertToNative(cx, obj))
    4040           0 :         return nullptr;
    4041             : 
    4042           0 :     result = SetOrExtendBoxedOrUnboxedDenseElements<JSVAL_TYPE_MAGIC>(cx, obj, 0, vp, length,
    4043           0 :                                                                       updateTypes);
    4044           0 :     MOZ_ASSERT(result != DenseElementResult::Incomplete);
    4045           0 :     if (result == DenseElementResult::Failure)
    4046           0 :         return nullptr;
    4047             : 
    4048           0 :     return obj;
    4049             : }
    4050             : 
    4051             : JSObject*
    4052          24 : js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length,
    4053             :                                            HandleObject proto /* = nullptr */)
    4054             : {
    4055          48 :     RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto));
    4056          24 :     if (!group)
    4057           0 :         return nullptr;
    4058          24 :     return NewCopiedArrayTryUseGroup(cx, group, vp, length);
    4059             : }
    4060             : 
    4061             : bool
    4062          41 : js::NewValuePair(JSContext* cx, const Value& val1, const Value& val2, MutableHandleValue rval)
    4063             : {
    4064          82 :     JS::AutoValueArray<2> vec(cx);
    4065          41 :     vec[0].set(val1);
    4066          41 :     vec[1].set(val2);
    4067             : 
    4068          41 :     JSObject* aobj = js::NewDenseCopiedArray(cx, 2, vec.begin());
    4069          41 :     if (!aobj)
    4070           0 :         return false;
    4071          41 :     rval.setObject(*aobj);
    4072          41 :     return true;
    4073             : }
    4074             : 
    4075             : #ifdef DEBUG
    4076             : bool
    4077           0 : js::ArrayInfo(JSContext* cx, unsigned argc, Value* vp)
    4078             : {
    4079           0 :     CallArgs args = CallArgsFromVp(argc, vp);
    4080             :     JSObject* obj;
    4081             : 
    4082           0 :     for (unsigned i = 0; i < args.length(); i++) {
    4083           0 :         RootedValue arg(cx, args[i]);
    4084             : 
    4085           0 :         UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, arg, nullptr);
    4086           0 :         if (!bytes)
    4087           0 :             return false;
    4088           0 :         if (arg.isPrimitive() ||
    4089           0 :             !(obj = arg.toObjectOrNull())->is<ArrayObject>()) {
    4090           0 :             fprintf(stderr, "%s: not array\n", bytes.get());
    4091           0 :             continue;
    4092             :         }
    4093           0 :         fprintf(stderr, "%s: (len %u", bytes.get(), obj->as<ArrayObject>().length());
    4094           0 :         fprintf(stderr, ", capacity %u", obj->as<ArrayObject>().getDenseCapacity());
    4095           0 :         fputs(")\n", stderr);
    4096             :     }
    4097             : 
    4098           0 :     args.rval().setUndefined();
    4099           0 :     return true;
    4100             : }
    4101             : #endif

Generated by: LCOV version 1.13