js/src/jsarray.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

     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/. */
     7 #include "jsarray.h"
     9 #include "mozilla/ArrayUtils.h"
    10 #include "mozilla/DebugOnly.h"
    11 #include "mozilla/FloatingPoint.h"
    12 #include "mozilla/MathAlgorithms.h"
    14 #include "jsapi.h"
    15 #include "jsatom.h"
    16 #include "jscntxt.h"
    17 #include "jsfriendapi.h"
    18 #include "jsfun.h"
    19 #include "jsiter.h"
    20 #include "jsnum.h"
    21 #include "jsobj.h"
    22 #include "jstypes.h"
    23 #include "jsutil.h"
    25 #include "ds/Sort.h"
    26 #include "gc/Heap.h"
    27 #include "vm/ArgumentsObject.h"
    28 #include "vm/ForkJoin.h"
    29 #include "vm/Interpreter.h"
    30 #include "vm/NumericConversions.h"
    31 #include "vm/Shape.h"
    32 #include "vm/StringBuffer.h"
    34 #include "jsatominlines.h"
    36 #include "vm/ArgumentsObject-inl.h"
    37 #include "vm/ArrayObject-inl.h"
    38 #include "vm/Interpreter-inl.h"
    39 #include "vm/Runtime-inl.h"
    41 using namespace js;
    42 using namespace js::gc;
    43 using namespace js::types;
    45 using mozilla::Abs;
    46 using mozilla::ArrayLength;
    47 using mozilla::CeilingLog2;
    48 using mozilla::DebugOnly;
    49 using mozilla::IsNaN;
    50 using mozilla::PointerRangeSize;
    52 bool
    53 js::GetLengthProperty(JSContext *cx, HandleObject obj, uint32_t *lengthp)
    54 {
    55     if (obj->is<ArrayObject>()) {
    56         *lengthp = obj->as<ArrayObject>().length();
    57         return true;
    58     }
    60     if (obj->is<ArgumentsObject>()) {
    61         ArgumentsObject &argsobj = obj->as<ArgumentsObject>();
    62         if (!argsobj.hasOverriddenLength()) {
    63             *lengthp = argsobj.initialLength();
    64             return true;
    65         }
    66     }
    68     RootedValue value(cx);
    69     if (!JSObject::getProperty(cx, obj, obj, cx->names().length, &value))
    70         return false;
    72     if (value.isInt32()) {
    73         *lengthp = uint32_t(value.toInt32()); // uint32_t cast does ToUint32
    74         return true;
    75     }
    77     return ToUint32(cx, value, lengthp);
    78 }
    80 /*
    81  * Determine if the id represents an array index.
    82  *
    83  * An id is an array index according to ECMA by (15.4):
    84  *
    85  * "Array objects give special treatment to a certain class of property names.
    86  * A property name P (in the form of a string value) is an array index if and
    87  * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
    88  * to 2^32-1."
    89  *
    90  * This means the largest allowed index is actually 2^32-2 (4294967294).
    91  *
    92  * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id)
    93  * except that by using signed 31-bit integers we miss the top half of the
    94  * valid range. This function checks the string representation itself; note
    95  * that calling a standard conversion routine might allow strings such as
    96  * "08" or "4.0" as array indices, which they are not.
    97  *
    98  */
    99 JS_FRIEND_API(bool)
   100 js::StringIsArrayIndex(JSLinearString *str, uint32_t *indexp)
   101 {
   102     const jschar *s = str->chars();
   103     uint32_t length = str->length();
   104     const jschar *end = s + length;
   106     if (length == 0 || length > (sizeof("4294967294") - 1) || !JS7_ISDEC(*s))
   107         return false;
   109     uint32_t c = 0, previous = 0;
   110     uint32_t index = JS7_UNDEC(*s++);
   112     /* Don't allow leading zeros. */
   113     if (index == 0 && s != end)
   114         return false;
   116     for (; s < end; s++) {
   117         if (!JS7_ISDEC(*s))
   118             return false;
   120         previous = index;
   121         c = JS7_UNDEC(*s);
   122         index = 10 * index + c;
   123     }
   125     /* Make sure we didn't overflow. */
   126     if (previous < (MAX_ARRAY_INDEX / 10) || (previous == (MAX_ARRAY_INDEX / 10) &&
   127         c <= (MAX_ARRAY_INDEX % 10))) {
   128         JS_ASSERT(index <= MAX_ARRAY_INDEX);
   129         *indexp = index;
   130         return true;
   131     }
   133     return false;
   134 }
   136 static bool
   137 ToId(JSContext *cx, double index, MutableHandleId id)
   138 {
   139     if (index == uint32_t(index))
   140         return IndexToId(cx, uint32_t(index), id);
   142     Value tmp = DoubleValue(index);
   143     return ValueToId<CanGC>(cx, HandleValue::fromMarkedLocation(&tmp), id);
   144 }
   146 static bool
   147 ToId(JSContext *cx, uint32_t index, MutableHandleId id)
   148 {
   149     return IndexToId(cx, index, id);
   150 }
   152 /*
   153  * If the property at the given index exists, get its value into location
   154  * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp
   155  * to JSVAL_VOID. This function assumes that the location pointed by vp is
   156  * properly rooted and can be used as GC-protected storage for temporaries.
   157  */
   158 template<typename IndexType>
   159 static inline bool
   160 DoGetElement(JSContext *cx, HandleObject obj, HandleObject receiver,
   161              IndexType index, bool *hole, MutableHandleValue vp)
   162 {
   163     RootedId id(cx);
   164     if (!ToId(cx, index, &id))
   165         return false;
   167     RootedObject obj2(cx);
   168     RootedShape prop(cx);
   169     if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &prop))
   170         return false;
   172     if (!prop) {
   173         vp.setUndefined();
   174         *hole = true;
   175     } else {
   176         if (!JSObject::getGeneric(cx, obj, receiver, id, vp))
   177             return false;
   178         *hole = false;
   179     }
   180     return true;
   181 }
   183 template<typename IndexType>
   184 static void
   185 AssertGreaterThanZero(IndexType index)
   186 {
   187     JS_ASSERT(index >= 0);
   188     JS_ASSERT(index == floor(index));
   189 }
   191 template<>
   192 void
   193 AssertGreaterThanZero(uint32_t index)
   194 {
   195 }
   197 template<typename IndexType>
   198 static bool
   199 GetElement(JSContext *cx, HandleObject obj, HandleObject receiver,
   200            IndexType index, bool *hole, MutableHandleValue vp)
   201 {
   202     AssertGreaterThanZero(index);
   203     if (obj->isNative() && index < obj->getDenseInitializedLength()) {
   204         vp.set(obj->getDenseElement(uint32_t(index)));
   205         if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
   206             *hole = false;
   207             return true;
   208         }
   209     }
   210     if (obj->is<ArgumentsObject>()) {
   211         if (obj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) {
   212             *hole = false;
   213             return true;
   214         }
   215     }
   217     return DoGetElement(cx, obj, receiver, index, hole, vp);
   218 }
   220 template<typename IndexType>
   221 static inline bool
   222 GetElement(JSContext *cx, HandleObject obj, IndexType index, bool *hole, MutableHandleValue vp)
   223 {
   224     return GetElement(cx, obj, obj, index, hole, vp);
   225 }
   227 static bool
   228 GetElementsSlow(JSContext *cx, HandleObject aobj, uint32_t length, Value *vp)
   229 {
   230     for (uint32_t i = 0; i < length; i++) {
   231         if (!JSObject::getElement(cx, aobj, aobj, i, MutableHandleValue::fromMarkedLocation(&vp[i])))
   232             return false;
   233     }
   235     return true;
   236 }
   238 bool
   239 js::GetElements(JSContext *cx, HandleObject aobj, uint32_t length, Value *vp)
   240 {
   241     if (aobj->is<ArrayObject>() && length <= aobj->getDenseInitializedLength() &&
   242         !ObjectMayHaveExtraIndexedProperties(aobj))
   243     {
   244         /* No other indexed properties so hole = undefined */
   245         const Value *srcbeg = aobj->getDenseElements();
   246         const Value *srcend = srcbeg + length;
   247         const Value *src = srcbeg;
   248         for (Value *dst = vp; src < srcend; ++dst, ++src)
   249             *dst = src->isMagic(JS_ELEMENTS_HOLE) ? UndefinedValue() : *src;
   250         return true;
   251     }
   253     if (aobj->is<ArgumentsObject>()) {
   254         ArgumentsObject &argsobj = aobj->as<ArgumentsObject>();
   255         if (!argsobj.hasOverriddenLength()) {
   256             if (argsobj.maybeGetElements(0, length, vp))
   257                 return true;
   258         }
   259     }
   261     return GetElementsSlow(cx, aobj, length, vp);
   262 }
   264 /*
   265  * Set the value of the property at the given index to v assuming v is rooted.
   266  */
   267 static bool
   268 SetArrayElement(JSContext *cx, HandleObject obj, double index, HandleValue v)
   269 {
   270     JS_ASSERT(index >= 0);
   272     if (obj->is<ArrayObject>() && !obj->isIndexed()) {
   273         Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
   274         /* Predicted/prefetched code should favor the remains-dense case. */
   275         JSObject::EnsureDenseResult result = JSObject::ED_SPARSE;
   276         do {
   277             if (index > uint32_t(-1))
   278                 break;
   279             uint32_t idx = uint32_t(index);
   280             if (idx >= arr->length() && !arr->lengthIsWritable()) {
   281                 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, nullptr,
   282                                              JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
   283                 return false;
   284             }
   285             result = arr->ensureDenseElements(cx, idx, 1);
   286             if (result != JSObject::ED_OK)
   287                 break;
   288             if (idx >= arr->length())
   289                 arr->setLengthInt32(idx + 1);
   290             arr->setDenseElementWithType(cx, idx, v);
   291             return true;
   292         } while (false);
   294         if (result == JSObject::ED_FAILED)
   295             return false;
   296         JS_ASSERT(result == JSObject::ED_SPARSE);
   297     }
   299     RootedId id(cx);
   300     if (!ToId(cx, index, &id))
   301         return false;
   303     RootedValue tmp(cx, v);
   304     return JSObject::setGeneric(cx, obj, obj, id, &tmp, true);
   305 }
   307 /*
   308  * Attempt to delete the element |index| from |obj| as if by
   309  * |obj.[[Delete]](index)|.
   310  *
   311  * If an error occurs while attempting to delete the element (that is, the call
   312  * to [[Delete]] threw), return false.
   313  *
   314  * Otherwise set *succeeded to indicate whether the deletion attempt succeeded
   315  * (that is, whether the call to [[Delete]] returned true or false).  (Deletes
   316  * generally fail only when the property is non-configurable, but proxies may
   317  * implement different semantics.)
   318  */
   319 static bool
   320 DeleteArrayElement(JSContext *cx, HandleObject obj, double index, bool *succeeded)
   321 {
   322     JS_ASSERT(index >= 0);
   323     JS_ASSERT(floor(index) == index);
   325     if (obj->is<ArrayObject>() && !obj->isIndexed()) {
   326         if (index <= UINT32_MAX) {
   327             uint32_t idx = uint32_t(index);
   328             if (idx < obj->getDenseInitializedLength()) {
   329                 obj->markDenseElementsNotPacked(cx);
   330                 obj->setDenseElement(idx, MagicValue(JS_ELEMENTS_HOLE));
   331                 if (!js_SuppressDeletedElement(cx, obj, idx))
   332                     return false;
   333             }
   334         }
   336         *succeeded = true;
   337         return true;
   338     }
   340     if (index <= UINT32_MAX)
   341         return JSObject::deleteElement(cx, obj, uint32_t(index), succeeded);
   343     return JSObject::deleteByValue(cx, obj, DoubleValue(index), succeeded);
   344 }
   346 /* ES6 20130308 draft 9.3.5 */
   347 static bool
   348 DeletePropertyOrThrow(JSContext *cx, HandleObject obj, double index)
   349 {
   350     bool succeeded;
   351     if (!DeleteArrayElement(cx, obj, index, &succeeded))
   352         return false;
   353     if (succeeded)
   354         return true;
   356     RootedId id(cx);
   357     RootedValue indexv(cx, NumberValue(index));
   358     if (!ValueToId<CanGC>(cx, indexv, &id))
   359         return false;
   360     return obj->reportNotConfigurable(cx, id, JSREPORT_ERROR);
   361 }
   363 bool
   364 js::SetLengthProperty(JSContext *cx, HandleObject obj, double length)
   365 {
   366     RootedValue v(cx, NumberValue(length));
   367     return JSObject::setProperty(cx, obj, obj, cx->names().length, &v, true);
   368 }
   370 /*
   371  * Since SpiderMonkey supports cross-class prototype-based delegation, we have
   372  * to be careful about the length getter and setter being called on an object
   373  * not of Array class. For the getter, we search obj's prototype chain for the
   374  * array that caused this getter to be invoked. In the setter case to overcome
   375  * the JSPROP_SHARED attribute, we must define a shadowing length property.
   376  */
   377 static bool
   378 array_length_getter(JSContext *cx, HandleObject obj_, HandleId id, MutableHandleValue vp)
   379 {
   380     RootedObject obj(cx, obj_);
   381     do {
   382         if (obj->is<ArrayObject>()) {
   383             vp.setNumber(obj->as<ArrayObject>().length());
   384             return true;
   385         }
   386         if (!JSObject::getProto(cx, obj, &obj))
   387             return false;
   388     } while (obj);
   389     return true;
   390 }
   392 static bool
   393 array_length_setter(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp)
   394 {
   395     if (!obj->is<ArrayObject>()) {
   396         return JSObject::defineProperty(cx, obj, cx->names().length, vp,
   397                                         nullptr, nullptr, JSPROP_ENUMERATE);
   398     }
   400     Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
   401     MOZ_ASSERT(arr->lengthIsWritable(),
   402                "setter shouldn't be called if property is non-writable");
   403     return ArraySetLength<SequentialExecution>(cx, arr, id, JSPROP_PERMANENT, vp, strict);
   404 }
   406 struct ReverseIndexComparator
   407 {
   408     bool operator()(const uint32_t& a, const uint32_t& b, bool *lessOrEqualp) {
   409         MOZ_ASSERT(a != b, "how'd we get duplicate indexes?");
   410         *lessOrEqualp = b <= a;
   411         return true;
   412     }
   413 };
   415 template <ExecutionMode mode>
   416 bool
   417 js::CanonicalizeArrayLengthValue(typename ExecutionModeTraits<mode>::ContextType cx,
   418                                  HandleValue v, uint32_t *newLen)
   419 {
   420     double d;
   422     if (mode == ParallelExecution) {
   423         if (v.isObject())
   424             return false;
   426         if (!NonObjectToUint32(cx, v, newLen))
   427             return false;
   429         if (!NonObjectToNumber(cx, v, &d))
   430             return false;
   431     } else {
   432         if (!ToUint32(cx->asJSContext(), v, newLen))
   433             return false;
   435         if (!ToNumber(cx->asJSContext(), v, &d))
   436             return false;
   437     }
   439     if (d == *newLen)
   440         return true;
   442     if (cx->isJSContext())
   443         JS_ReportErrorNumber(cx->asJSContext(), js_GetErrorMessage, nullptr,
   444                              JSMSG_BAD_ARRAY_LENGTH);
   445     return false;
   446 }
   448 template bool
   449 js::CanonicalizeArrayLengthValue<SequentialExecution>(JSContext *cx,
   450                                                       HandleValue v, uint32_t *newLen);
   451 template bool
   452 js::CanonicalizeArrayLengthValue<ParallelExecution>(ForkJoinContext *cx,
   453                                                     HandleValue v, uint32_t *newLen);
   455 /* ES6 20130308 draft 8.4.2.4 ArraySetLength */
   456 template <ExecutionMode mode>
   457 bool
   458 js::ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cxArg,
   459                    Handle<ArrayObject*> arr, HandleId id,
   460                    unsigned attrs, HandleValue value, bool setterIsStrict)
   461 {
   462     MOZ_ASSERT(cxArg->isThreadLocal(arr));
   463     MOZ_ASSERT(id == NameToId(cxArg->names().length));
   465     /* Steps 1-2 are irrelevant in our implementation. */
   467     /* Steps 3-5. */
   468     uint32_t newLen;
   469     if (!CanonicalizeArrayLengthValue<mode>(cxArg, value, &newLen))
   470         return false;
   472     // Abort if we're being asked to change enumerability or configurability.
   473     // (The length property of arrays is non-configurable, so such attempts
   474     // must fail.)  This behavior is spread throughout the ArraySetLength spec
   475     // algorithm, but we only need check it once as our array implementation
   476     // is internally so different from the spec algorithm.  (ES5 and ES6 define
   477     // behavior by delegating to the default define-own-property algorithm --
   478     // OrdinaryDefineOwnProperty in ES6, the default [[DefineOwnProperty]] in
   479     // ES5 -- but we reimplement all the conflict-detection bits ourselves here
   480     // so that we can use a customized length representation.)
   481     if (!(attrs & JSPROP_PERMANENT) || (attrs & JSPROP_ENUMERATE)) {
   482         if (!setterIsStrict)
   483             return true;
   484         // Bail for strict mode in parallel execution, as we need to go back
   485         // to sequential mode to throw the error.
   486         if (mode == ParallelExecution)
   487             return false;
   488         return Throw(cxArg->asJSContext(), id, JSMSG_CANT_REDEFINE_PROP);
   489     }
   491     /* Steps 6-7. */
   492     bool lengthIsWritable = arr->lengthIsWritable();
   493 #ifdef DEBUG
   494     {
   495         RootedShape lengthShape(cxArg, arr->nativeLookupPure(id));
   496         MOZ_ASSERT(lengthShape);
   497         MOZ_ASSERT(lengthShape->writable() == lengthIsWritable);
   498     }
   499 #endif
   501     uint32_t oldLen = arr->length();
   503     /* Steps 8-9 for arrays with non-writable length. */
   504     if (!lengthIsWritable) {
   505         if (newLen == oldLen)
   506             return true;
   508         if (!cxArg->isJSContext())
   509             return false;
   511         if (setterIsStrict) {
   512             return JS_ReportErrorFlagsAndNumber(cxArg->asJSContext(),
   513                                                 JSREPORT_ERROR, js_GetErrorMessage, nullptr,
   514                                                 JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
   515         }
   517         return JSObject::reportReadOnly(cxArg->asJSContext(), id,
   518                                         JSREPORT_STRICT | JSREPORT_WARNING);
   519     }
   521     /* Step 8. */
   522     bool succeeded = true;
   523     do {
   524         // The initialized length and capacity of an array only need updating
   525         // when non-hole elements are added or removed, which doesn't happen
   526         // when array length stays the same or increases.
   527         if (newLen >= oldLen)
   528             break;
   530         // Attempt to propagate dense-element optimization tricks, if possible,
   531         // and avoid the generic (and accordingly slow) deletion code below.
   532         // We can only do this if there are only densely-indexed elements.
   533         // Once there's a sparse indexed element, there's no good way to know,
   534         // save by enumerating all the properties to find it.  But we *have* to
   535         // know in case that sparse indexed element is non-configurable, as
   536         // that element must prevent any deletions below it.  Bug 586842 should
   537         // fix this inefficiency by moving indexed storage to be entirely
   538         // separate from non-indexed storage.
   539         if (!arr->isIndexed()) {
   540             uint32_t oldCapacity = arr->getDenseCapacity();
   541             uint32_t oldInitializedLength = arr->getDenseInitializedLength();
   542             MOZ_ASSERT(oldCapacity >= oldInitializedLength);
   543             if (oldInitializedLength > newLen)
   544                 arr->setDenseInitializedLength(newLen);
   545             if (oldCapacity > newLen)
   546                 arr->shrinkElements(cxArg, newLen);
   548             // We've done the work of deleting any dense elements needing
   549             // deletion, and there are no sparse elements.  Thus we can skip
   550             // straight to defining the length.
   551             break;
   552         }
   554         // Bail from parallel execution if need to perform step 15, which is
   555         // unsafe and isn't a common case.
   556         if (mode == ParallelExecution)
   557             return false;
   559         JSContext *cx = cxArg->asJSContext();
   561         // Step 15.
   562         //
   563         // Attempt to delete all elements above the new length, from greatest
   564         // to least.  If any of these deletions fails, we're supposed to define
   565         // the length to one greater than the index that couldn't be deleted,
   566         // *with the property attributes specified*.  This might convert the
   567         // length to be not the value specified, yet non-writable.  (You may be
   568         // forgiven for thinking these are interesting semantics.)  Example:
   569         //
   570         //   var arr =
   571         //     Object.defineProperty([0, 1, 2, 3], 1, { writable: false });
   572         //   Object.defineProperty(arr, "length",
   573         //                         { value: 0, writable: false });
   574         //
   575         // will convert |arr| to an array of non-writable length two, then
   576         // throw a TypeError.
   577         //
   578         // We implement this behavior, in the relevant lops below, by setting
   579         // |succeeded| to false.  Then we exit the loop, define the length
   580         // appropriately, and only then throw a TypeError, if necessary.
   581         uint32_t gap = oldLen - newLen;
   582         const uint32_t RemoveElementsFastLimit = 1 << 24;
   583         if (gap < RemoveElementsFastLimit) {
   584             // If we're removing a relatively small number of elements, just do
   585             // it exactly by the spec.
   586             while (newLen < oldLen) {
   587                 /* Step 15a. */
   588                 oldLen--;
   590                 /* Steps 15b-d. */
   591                 bool deleteSucceeded;
   592                 if (!JSObject::deleteElement(cx, arr, oldLen, &deleteSucceeded))
   593                     return false;
   594                 if (!deleteSucceeded) {
   595                     newLen = oldLen + 1;
   596                     succeeded = false;
   597                     break;
   598                 }
   599             }
   600         } else {
   601             // If we're removing a large number of elements from an array
   602             // that's probably sparse, try a different tack.  Get all the own
   603             // property names, sift out the indexes in the deletion range into
   604             // a vector, sort the vector greatest to least, then delete the
   605             // indexes greatest to least using that vector.  See bug 322135.
   606             //
   607             // This heuristic's kind of a huge guess -- "large number of
   608             // elements" and "probably sparse" are completely unprincipled
   609             // predictions.  In the long run, bug 586842 will support the right
   610             // fix: store sparse elements in a sorted data structure that
   611             // permits fast in-reverse-order traversal and concurrent removals.
   613             Vector<uint32_t> indexes(cx);
   614             {
   615                 AutoIdVector props(cx);
   616                 if (!GetPropertyNames(cx, arr, JSITER_OWNONLY | JSITER_HIDDEN, &props))
   617                     return false;
   619                 for (size_t i = 0; i < props.length(); i++) {
   620                     if (!CheckForInterrupt(cx))
   621                         return false;
   623                     uint32_t index;
   624                     if (!js_IdIsIndex(props[i], &index))
   625                         continue;
   627                     if (index >= newLen && index < oldLen) {
   628                         if (!indexes.append(index))
   629                             return false;
   630                     }
   631                 }
   632             }
   634             uint32_t count = indexes.length();
   635             {
   636                 // We should use radix sort to be O(n), but this is uncommon
   637                 // enough that we'll punt til someone complains.
   638                 Vector<uint32_t> scratch(cx);
   639                 if (!scratch.resize(count))
   640                     return false;
   641                 MOZ_ALWAYS_TRUE(MergeSort(indexes.begin(), count, scratch.begin(),
   642                                           ReverseIndexComparator()));
   643             }
   645             uint32_t index = UINT32_MAX;
   646             for (uint32_t i = 0; i < count; i++) {
   647                 MOZ_ASSERT(indexes[i] < index, "indexes should never repeat");
   648                 index = indexes[i];
   650                 /* Steps 15b-d. */
   651                 bool deleteSucceeded;
   652                 if (!JSObject::deleteElement(cx, arr, index, &deleteSucceeded))
   653                     return false;
   654                 if (!deleteSucceeded) {
   655                     newLen = index + 1;
   656                     succeeded = false;
   657                     break;
   658                 }
   659             }
   660         }
   661     } while (false);
   663     /* Steps 12, 16. */
   665     // Yes, we totally drop a non-stub getter/setter from a defineProperty
   666     // API call on the floor here.  Given that getter/setter will go away in
   667     // the long run, with accessors replacing them both internally and at the
   668     // API level, just run with this.
   669     RootedShape lengthShape(cxArg, mode == ParallelExecution
   670                             ? arr->nativeLookupPure(id)
   671                             : arr->nativeLookup(cxArg->asJSContext(), id));
   672     if (!JSObject::changeProperty<mode>(cxArg, arr, lengthShape, attrs,
   673                                         JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED,
   674                                         array_length_getter, array_length_setter))
   675     {
   676         return false;
   677     }
   679     if (mode == ParallelExecution) {
   680         // Overflowing int32 requires changing TI state.
   681         if (newLen > INT32_MAX)
   682             return false;
   683         arr->setLengthInt32(newLen);
   684     } else {
   685         JSContext *cx = cxArg->asJSContext();
   686         arr->setLength(cx, newLen);
   687     }
   690     // All operations past here until the |!succeeded| code must be infallible,
   691     // so that all element fields remain properly synchronized.
   693     // Trim the initialized length, if needed, to preserve the <= length
   694     // invariant.  (Capacity was already reduced during element deletion, if
   695     // necessary.)
   696     ObjectElements *header = arr->getElementsHeader();
   697     header->initializedLength = Min(header->initializedLength, newLen);
   699     if (attrs & JSPROP_READONLY) {
   700         header->setNonwritableArrayLength();
   702         // When an array's length becomes non-writable, writes to indexes
   703         // greater than or equal to the length don't change the array.  We
   704         // handle this with a check for non-writable length in most places.
   705         // But in JIT code every check counts -- so we piggyback the check on
   706         // the already-required range check for |index < capacity| by making
   707         // capacity of arrays with non-writable length never exceed the length.
   708         if (arr->getDenseCapacity() > newLen) {
   709             arr->shrinkElements(cxArg, newLen);
   710             arr->getElementsHeader()->capacity = newLen;
   711         }
   712     }
   714     if (setterIsStrict && !succeeded) {
   715         // We can't have arrived here under ParallelExecution, as we have
   716         // returned from the function before step 15 above.
   717         JSContext *cx = cxArg->asJSContext();
   718         RootedId elementId(cx);
   719         if (!IndexToId(cx, newLen - 1, &elementId))
   720             return false;
   721         return arr->reportNotConfigurable(cx, elementId);
   722     }
   724     return true;
   725 }
   727 template bool
   728 js::ArraySetLength<SequentialExecution>(JSContext *cx, Handle<ArrayObject*> arr,
   729                                         HandleId id, unsigned attrs, HandleValue value,
   730                                         bool setterIsStrict);
   731 template bool
   732 js::ArraySetLength<ParallelExecution>(ForkJoinContext *cx, Handle<ArrayObject*> arr,
   733                                       HandleId id, unsigned attrs, HandleValue value,
   734                                       bool setterIsStrict);
   736 bool
   737 js::WouldDefinePastNonwritableLength(ThreadSafeContext *cx,
   738                                      HandleObject obj, uint32_t index, bool strict,
   739                                      bool *definesPast)
   740 {
   741     if (!obj->is<ArrayObject>()) {
   742         *definesPast = false;
   743         return true;
   744     }
   746     Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
   747     uint32_t length = arr->length();
   748     if (index < length) {
   749         *definesPast = false;
   750         return true;
   751     }
   753     if (arr->lengthIsWritable()) {
   754         *definesPast = false;
   755         return true;
   756     }
   758     *definesPast = true;
   760     // Error in strict mode code or warn with strict option.
   761     unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_STRICT | JSREPORT_WARNING);
   762     if (cx->isForkJoinContext())
   763         return cx->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, flags);
   765     if (!cx->isJSContext())
   766         return true;
   768     JSContext *ncx = cx->asJSContext();
   770     if (!strict && !ncx->options().extraWarnings())
   771         return true;
   773     // XXX include the index and maybe array length in the error message
   774     return JS_ReportErrorFlagsAndNumber(ncx, flags, js_GetErrorMessage, nullptr,
   775                                         JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH);
   776 }
   778 static bool
   779 array_addProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
   780 {
   781     Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
   783     uint32_t index;
   784     if (!js_IdIsIndex(id, &index))
   785         return true;
   787     uint32_t length = arr->length();
   788     if (index >= length) {
   789         MOZ_ASSERT(arr->lengthIsWritable(),
   790                    "how'd this element get added if length is non-writable?");
   791         arr->setLength(cx, index + 1);
   792     }
   793     return true;
   794 }
   796 bool
   797 js::ObjectMayHaveExtraIndexedProperties(JSObject *obj)
   798 {
   799     /*
   800      * Whether obj may have indexed properties anywhere besides its dense
   801      * elements. This includes other indexed properties in its shape hierarchy,
   802      * and indexed properties or elements along its prototype chain.
   803      */
   805     JS_ASSERT(obj->isNative());
   807     if (obj->isIndexed())
   808         return true;
   810     /*
   811      * Walk up the prototype chain and see if this indexed element already
   812      * exists. If we hit the end of the prototype chain, it's safe to set the
   813      * element on the original object.
   814      */
   815     while ((obj = obj->getProto()) != nullptr) {
   816         /*
   817          * If the prototype is a non-native object (possibly a dense array), or
   818          * a native object (possibly a slow array) that has indexed properties,
   819          * return true.
   820          */
   821         if (!obj->isNative())
   822             return true;
   823         if (obj->isIndexed())
   824             return true;
   825         if (obj->getDenseInitializedLength() > 0)
   826             return true;
   827         if (obj->is<TypedArrayObject>())
   828             return true;
   829     }
   831     return false;
   832 }
   834 const Class ArrayObject::class_ = {
   835     "Array",
   836     JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
   837     array_addProperty,
   838     JS_DeletePropertyStub,   /* delProperty */
   839     JS_PropertyStub,         /* getProperty */
   840     JS_StrictPropertyStub,   /* setProperty */
   841     JS_EnumerateStub,
   842     JS_ResolveStub,
   843     JS_ConvertStub,
   844     nullptr,
   845     nullptr,        /* call        */
   846     nullptr,        /* hasInstance */
   847     nullptr,        /* construct   */
   848     nullptr         /* trace       */
   849 };
   851 static bool
   852 AddLengthProperty(ExclusiveContext *cx, HandleObject obj)
   853 {
   854     /*
   855      * Add the 'length' property for a newly created array,
   856      * and update the elements to be an empty array owned by the object.
   857      * The shared emptyObjectElements singleton cannot be used for slow arrays,
   858      * as accesses to 'length' will use the elements header.
   859      */
   861     RootedId lengthId(cx, NameToId(cx->names().length));
   862     JS_ASSERT(!obj->nativeLookup(cx, lengthId));
   864     return JSObject::addProperty(cx, obj, lengthId, array_length_getter, array_length_setter,
   865                                  SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0,
   866                                  /* allowDictionary = */ false);
   867 }
   869 #if JS_HAS_TOSOURCE
   870 MOZ_ALWAYS_INLINE bool
   871 IsArray(HandleValue v)
   872 {
   873     return v.isObject() && v.toObject().is<ArrayObject>();
   874 }
   876 MOZ_ALWAYS_INLINE bool
   877 array_toSource_impl(JSContext *cx, CallArgs args)
   878 {
   879     JS_ASSERT(IsArray(args.thisv()));
   881     Rooted<JSObject*> obj(cx, &args.thisv().toObject());
   882     RootedValue elt(cx);
   884     AutoCycleDetector detector(cx, obj);
   885     if (!detector.init())
   886         return false;
   888     StringBuffer sb(cx);
   890     if (detector.foundCycle()) {
   891         if (!sb.append("[]"))
   892             return false;
   893         goto make_string;
   894     }
   896     if (!sb.append('['))
   897         return false;
   899     uint32_t length;
   900     if (!GetLengthProperty(cx, obj, &length))
   901         return false;
   903     for (uint32_t index = 0; index < length; index++) {
   904         bool hole;
   905         if (!CheckForInterrupt(cx) ||
   906             !GetElement(cx, obj, index, &hole, &elt)) {
   907             return false;
   908         }
   910         /* Get element's character string. */
   911         JSString *str;
   912         if (hole) {
   913             str = cx->runtime()->emptyString;
   914         } else {
   915             str = ValueToSource(cx, elt);
   916             if (!str)
   917                 return false;
   918         }
   920         /* Append element to buffer. */
   921         if (!sb.append(str))
   922             return false;
   923         if (index + 1 != length) {
   924             if (!sb.append(", "))
   925                 return false;
   926         } else if (hole) {
   927             if (!sb.append(','))
   928                 return false;
   929         }
   930     }
   932     /* Finalize the buffer. */
   933     if (!sb.append(']'))
   934         return false;
   936   make_string:
   937     JSString *str = sb.finishString();
   938     if (!str)
   939         return false;
   941     args.rval().setString(str);
   942     return true;
   943 }
   945 static bool
   946 array_toSource(JSContext *cx, unsigned argc, Value *vp)
   947 {
   948     JS_CHECK_RECURSION(cx, return false);
   949     CallArgs args = CallArgsFromVp(argc, vp);
   950     return CallNonGenericMethod<IsArray, array_toSource_impl>(cx, args);
   951 }
   952 #endif
   954 struct EmptySeparatorOp
   955 {
   956     bool operator()(JSContext *, StringBuffer &sb) { return true; }
   957 };
   959 struct CharSeparatorOp
   960 {
   961     jschar sep;
   962     CharSeparatorOp(jschar sep) : sep(sep) {};
   963     bool operator()(JSContext *, StringBuffer &sb) { return sb.append(sep); }
   964 };
   966 struct StringSeparatorOp
   967 {
   968     const jschar *sepchars;
   969     size_t seplen;
   971     StringSeparatorOp(const jschar *sepchars, size_t seplen)
   972       : sepchars(sepchars), seplen(seplen) {};
   974     bool operator()(JSContext *cx, StringBuffer &sb) {
   975         return sb.append(sepchars, seplen);
   976     }
   977 };
   979 template <bool Locale, typename SeparatorOp>
   980 static bool
   981 ArrayJoinKernel(JSContext *cx, SeparatorOp sepOp, HandleObject obj, uint32_t length,
   982                StringBuffer &sb)
   983 {
   984     uint32_t i = 0;
   986     if (!Locale && obj->is<ArrayObject>() && !ObjectMayHaveExtraIndexedProperties(obj)) {
   987         // This loop handles all elements up to initializedLength. If
   988         // length > initLength we rely on the second loop to add the
   989         // other elements.
   990         uint32_t initLength = obj->getDenseInitializedLength();
   991         while (i < initLength) {
   992             if (!CheckForInterrupt(cx))
   993                 return false;
   995             const Value &elem = obj->getDenseElement(i);
   997             if (elem.isString()) {
   998                 if (!sb.append(elem.toString()))
   999                     return false;
  1000             } else if (elem.isNumber()) {
  1001                 if (!NumberValueToStringBuffer(cx, elem, sb))
  1002                     return false;
  1003             } else if (elem.isBoolean()) {
  1004                 if (!BooleanToStringBuffer(elem.toBoolean(), sb))
  1005                     return false;
  1006             } else if (elem.isObject()) {
  1007                 /*
  1008                  * Object stringifying could modify the initialized length or make
  1009                  * the array sparse. Delegate it to a separate loop to keep this
  1010                  * one tight.
  1011                  */
  1012                 break;
  1013             } else {
  1014                 JS_ASSERT(elem.isMagic(JS_ELEMENTS_HOLE) || elem.isNullOrUndefined());
  1017             if (++i != length && !sepOp(cx, sb))
  1018                 return false;
  1022     if (i != length) {
  1023         RootedValue v(cx);
  1024         while (i < length) {
  1025             if (!CheckForInterrupt(cx))
  1026                 return false;
  1028             bool hole;
  1029             if (!GetElement(cx, obj, i, &hole, &v))
  1030                 return false;
  1031             if (!hole && !v.isNullOrUndefined()) {
  1032                 if (Locale) {
  1033                     JSObject *robj = ToObject(cx, v);
  1034                     if (!robj)
  1035                         return false;
  1036                     RootedId id(cx, NameToId(cx->names().toLocaleString));
  1037                     if (!robj->callMethod(cx, id, 0, nullptr, &v))
  1038                         return false;
  1040                 if (!ValueToStringBuffer(cx, v, sb))
  1041                     return false;
  1044             if (++i != length && !sepOp(cx, sb))
  1045                 return false;
  1049     return true;
  1052 template <bool Locale>
  1053 static bool
  1054 ArrayJoin(JSContext *cx, CallArgs &args)
  1056     // This method is shared by Array.prototype.join and
  1057     // Array.prototype.toLocaleString. The steps in ES5 are nearly the same, so
  1058     // the annotations in this function apply to both toLocaleString and join.
  1060     // Step 1
  1061     RootedObject obj(cx, ToObject(cx, args.thisv()));
  1062     if (!obj)
  1063         return false;
  1065     AutoCycleDetector detector(cx, obj);
  1066     if (!detector.init())
  1067         return false;
  1069     if (detector.foundCycle()) {
  1070         args.rval().setString(cx->names().empty);
  1071         return true;
  1074     // Steps 2 and 3
  1075     uint32_t length;
  1076     if (!GetLengthProperty(cx, obj, &length))
  1077         return false;
  1079     // Steps 4 and 5
  1080     RootedString sepstr(cx, nullptr);
  1081     const jschar *sepchars;
  1082     size_t seplen;
  1083     if (!Locale && args.hasDefined(0)) {
  1084         sepstr = ToString<CanGC>(cx, args[0]);
  1085         if (!sepstr)
  1086             return false;
  1087         sepchars = sepstr->getChars(cx);
  1088         if (!sepchars)
  1089             return false;
  1090         seplen = sepstr->length();
  1091     } else {
  1092         HandlePropertyName comma = cx->names().comma;
  1093         sepstr = comma;
  1094         sepchars = comma->chars();
  1095         seplen = comma->length();
  1098     JS::Anchor<JSString*> anchor(sepstr);
  1100     // Step 6 is implicit in the loops below.
  1102     // An optimized version of a special case of steps 7-11: when length==1 and
  1103     // the 0th element is a string, ToString() of that element is a no-op and
  1104     // so it can be immediately returned as the result.
  1105     if (length == 1 && !Locale && obj->is<ArrayObject>() &&
  1106         obj->getDenseInitializedLength() == 1)
  1108         const Value &elem0 = obj->getDenseElement(0);
  1109         if (elem0.isString()) {
  1110             args.rval().setString(elem0.toString());
  1111             return true;
  1115     StringBuffer sb(cx);
  1117     // The separator will be added |length - 1| times, reserve space for that
  1118     // so that we don't have to unnecessarily grow the buffer.
  1119     if (length > 0 && !sb.reserve(seplen * (length - 1)))
  1120         return false;
  1122     // Various optimized versions of steps 7-10.
  1123     if (seplen == 0) {
  1124         EmptySeparatorOp op;
  1125         if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb))
  1126             return false;
  1127     } else if (seplen == 1) {
  1128         CharSeparatorOp op(sepchars[0]);
  1129         if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb))
  1130             return false;
  1131     } else {
  1132         StringSeparatorOp op(sepchars, seplen);
  1133         if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb))
  1134             return false;
  1137     // Step 11
  1138     JSString *str = sb.finishString();
  1139     if (!str)
  1140         return false;
  1141     args.rval().setString(str);
  1142     return true;
  1145 /* ES5 15.4.4.2. NB: The algorithm here differs from the one in ES3. */
  1146 static bool
  1147 array_toString(JSContext *cx, unsigned argc, Value *vp)
  1149     JS_CHECK_RECURSION(cx, return false);
  1151     CallArgs args = CallArgsFromVp(argc, vp);
  1152     RootedObject obj(cx, ToObject(cx, args.thisv()));
  1153     if (!obj)
  1154         return false;
  1156     RootedValue join(cx, args.calleev());
  1157     if (!JSObject::getProperty(cx, obj, obj, cx->names().join, &join))
  1158         return false;
  1160     if (!js_IsCallable(join)) {
  1161         JSString *str = JS_BasicObjectToString(cx, obj);
  1162         if (!str)
  1163             return false;
  1164         args.rval().setString(str);
  1165         return true;
  1168     InvokeArgs args2(cx);
  1169     if (!args2.init(0))
  1170         return false;
  1172     args2.setCallee(join);
  1173     args2.setThis(ObjectValue(*obj));
  1175     /* Do the call. */
  1176     if (!Invoke(cx, args2))
  1177         return false;
  1178     args.rval().set(args2.rval());
  1179     return true;
  1182 /* ES5 15.4.4.3 */
  1183 static bool
  1184 array_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
  1186     JS_CHECK_RECURSION(cx, return false);
  1188     CallArgs args = CallArgsFromVp(argc, vp);
  1189     return ArrayJoin<true>(cx, args);
  1192 /* ES5 15.4.4.5 */
  1193 static bool
  1194 array_join(JSContext *cx, unsigned argc, Value *vp)
  1196     JS_CHECK_RECURSION(cx, return false);
  1198     CallArgs args = CallArgsFromVp(argc, vp);
  1199     return ArrayJoin<false>(cx, args);
  1202 static inline bool
  1203 InitArrayTypes(JSContext *cx, TypeObject *type, const Value *vector, unsigned count)
  1205     if (!type->unknownProperties()) {
  1206         AutoEnterAnalysis enter(cx);
  1208         HeapTypeSet *types = type->getProperty(cx, JSID_VOID);
  1209         if (!types)
  1210             return false;
  1212         for (unsigned i = 0; i < count; i++) {
  1213             if (vector[i].isMagic(JS_ELEMENTS_HOLE))
  1214                 continue;
  1215             Type valtype = GetValueType(vector[i]);
  1216             types->addType(cx, valtype);
  1219     return true;
  1222 enum ShouldUpdateTypes
  1224     UpdateTypes = true,
  1225     DontUpdateTypes = false
  1226 };
  1228 /* vector must point to rooted memory. */
  1229 static bool
  1230 InitArrayElements(JSContext *cx, HandleObject obj, uint32_t start, uint32_t count, const Value *vector, ShouldUpdateTypes updateTypes)
  1232     JS_ASSERT(count <= MAX_ARRAY_INDEX);
  1234     if (count == 0)
  1235         return true;
  1237     types::TypeObject *type = obj->getType(cx);
  1238     if (!type)
  1239         return false;
  1240     if (updateTypes && !InitArrayTypes(cx, type, vector, count))
  1241         return false;
  1243     /*
  1244      * Optimize for dense arrays so long as adding the given set of elements
  1245      * wouldn't otherwise make the array slow or exceed a non-writable array
  1246      * length.
  1247      */
  1248     do {
  1249         if (!obj->is<ArrayObject>())
  1250             break;
  1251         if (ObjectMayHaveExtraIndexedProperties(obj))
  1252             break;
  1254         if (obj->shouldConvertDoubleElements())
  1255             break;
  1257         Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
  1259         if (!arr->lengthIsWritable() && start + count > arr->length())
  1260             break;
  1262         JSObject::EnsureDenseResult result = arr->ensureDenseElements(cx, start, count);
  1263         if (result != JSObject::ED_OK) {
  1264             if (result == JSObject::ED_FAILED)
  1265                 return false;
  1266             JS_ASSERT(result == JSObject::ED_SPARSE);
  1267             break;
  1270         uint32_t newlen = start + count;
  1271         if (newlen > arr->length())
  1272             arr->setLengthInt32(newlen);
  1274         JS_ASSERT(count < UINT32_MAX / sizeof(Value));
  1275         arr->copyDenseElements(start, vector, count);
  1276         JS_ASSERT_IF(count != 0, !arr->getDenseElement(newlen - 1).isMagic(JS_ELEMENTS_HOLE));
  1277         return true;
  1278     } while (false);
  1280     const Value* end = vector + count;
  1281     while (vector < end && start <= MAX_ARRAY_INDEX) {
  1282         if (!CheckForInterrupt(cx) ||
  1283             !SetArrayElement(cx, obj, start++, HandleValue::fromMarkedLocation(vector++))) {
  1284             return false;
  1288     if (vector == end)
  1289         return true;
  1291     JS_ASSERT(start == MAX_ARRAY_INDEX + 1);
  1292     RootedValue value(cx);
  1293     RootedId id(cx);
  1294     RootedValue indexv(cx);
  1295     double index = MAX_ARRAY_INDEX + 1;
  1296     do {
  1297         value = *vector++;
  1298         indexv = DoubleValue(index);
  1299         if (!ValueToId<CanGC>(cx, indexv, &id) ||
  1300             !JSObject::setGeneric(cx, obj, obj, id, &value, true))
  1302             return false;
  1304         index += 1;
  1305     } while (vector != end);
  1307     return true;
  1310 static bool
  1311 array_reverse(JSContext *cx, unsigned argc, Value *vp)
  1313     CallArgs args = CallArgsFromVp(argc, vp);
  1314     RootedObject obj(cx, ToObject(cx, args.thisv()));
  1315     if (!obj)
  1316         return false;
  1318     uint32_t len;
  1319     if (!GetLengthProperty(cx, obj, &len))
  1320         return false;
  1322     do {
  1323         if (!obj->is<ArrayObject>())
  1324             break;
  1325         if (ObjectMayHaveExtraIndexedProperties(obj))
  1326             break;
  1328         /* An empty array or an array with no elements is already reversed. */
  1329         if (len == 0 || obj->getDenseCapacity() == 0) {
  1330             args.rval().setObject(*obj);
  1331             return true;
  1334         /*
  1335          * It's actually surprisingly complicated to reverse an array due to the
  1336          * orthogonality of array length and array capacity while handling
  1337          * leading and trailing holes correctly.  Reversing seems less likely to
  1338          * be a common operation than other array mass-mutation methods, so for
  1339          * now just take a probably-small memory hit (in the absence of too many
  1340          * holes in the array at its start) and ensure that the capacity is
  1341          * sufficient to hold all the elements in the array if it were full.
  1342          */
  1343         JSObject::EnsureDenseResult result = obj->ensureDenseElements(cx, len, 0);
  1344         if (result != JSObject::ED_OK) {
  1345             if (result == JSObject::ED_FAILED)
  1346                 return false;
  1347             JS_ASSERT(result == JSObject::ED_SPARSE);
  1348             break;
  1351         /* Fill out the array's initialized length to its proper length. */
  1352         obj->ensureDenseInitializedLength(cx, len, 0);
  1354         RootedValue origlo(cx), orighi(cx);
  1356         uint32_t lo = 0, hi = len - 1;
  1357         for (; lo < hi; lo++, hi--) {
  1358             origlo = obj->getDenseElement(lo);
  1359             orighi = obj->getDenseElement(hi);
  1360             obj->setDenseElement(lo, orighi);
  1361             if (orighi.isMagic(JS_ELEMENTS_HOLE) &&
  1362                 !js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(lo))) {
  1363                 return false;
  1365             obj->setDenseElement(hi, origlo);
  1366             if (origlo.isMagic(JS_ELEMENTS_HOLE) &&
  1367                 !js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(hi))) {
  1368                 return false;
  1372         /*
  1373          * Per ECMA-262, don't update the length of the array, even if the new
  1374          * array has trailing holes (and thus the original array began with
  1375          * holes).
  1376          */
  1377         args.rval().setObject(*obj);
  1378         return true;
  1379     } while (false);
  1381     RootedValue lowval(cx), hival(cx);
  1382     for (uint32_t i = 0, half = len / 2; i < half; i++) {
  1383         bool hole, hole2;
  1384         if (!CheckForInterrupt(cx) ||
  1385             !GetElement(cx, obj, i, &hole, &lowval) ||
  1386             !GetElement(cx, obj, len - i - 1, &hole2, &hival))
  1388             return false;
  1391         if (!hole && !hole2) {
  1392             if (!SetArrayElement(cx, obj, i, hival))
  1393                 return false;
  1394             if (!SetArrayElement(cx, obj, len - i - 1, lowval))
  1395                 return false;
  1396         } else if (hole && !hole2) {
  1397             if (!SetArrayElement(cx, obj, i, hival))
  1398                 return false;
  1399             if (!DeletePropertyOrThrow(cx, obj, len - i - 1))
  1400                 return false;
  1401         } else if (!hole && hole2) {
  1402             if (!DeletePropertyOrThrow(cx, obj, i))
  1403                 return false;
  1404             if (!SetArrayElement(cx, obj, len - i - 1, lowval))
  1405                 return false;
  1406         } else {
  1407             // No action required.
  1410     args.rval().setObject(*obj);
  1411     return true;
  1414 namespace {
  1416 inline bool
  1417 CompareStringValues(JSContext *cx, const Value &a, const Value &b, bool *lessOrEqualp)
  1419     if (!CheckForInterrupt(cx))
  1420         return false;
  1422     JSString *astr = a.toString();
  1423     JSString *bstr = b.toString();
  1424     int32_t result;
  1425     if (!CompareStrings(cx, astr, bstr, &result))
  1426         return false;
  1428     *lessOrEqualp = (result <= 0);
  1429     return true;
  1432 static const uint64_t powersOf10[] = {
  1433     1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 1000000000000ULL
  1434 };
  1436 static inline unsigned
  1437 NumDigitsBase10(uint32_t n)
  1439     /*
  1440      * This is just floor_log10(n) + 1
  1441      * Algorithm taken from
  1442      * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
  1443      */
  1444     uint32_t log2 = CeilingLog2(n);
  1445     uint32_t t = log2 * 1233 >> 12;
  1446     return t - (n < powersOf10[t]) + 1;
  1449 static inline bool
  1450 CompareLexicographicInt32(const Value &a, const Value &b, bool *lessOrEqualp)
  1452     int32_t aint = a.toInt32();
  1453     int32_t bint = b.toInt32();
  1455     /*
  1456      * If both numbers are equal ... trivial
  1457      * If only one of both is negative --> arithmetic comparison as char code
  1458      * of '-' is always less than any other digit
  1459      * If both numbers are negative convert them to positive and continue
  1460      * handling ...
  1461      */
  1462     if (aint == bint) {
  1463         *lessOrEqualp = true;
  1464     } else if ((aint < 0) && (bint >= 0)) {
  1465         *lessOrEqualp = true;
  1466     } else if ((aint >= 0) && (bint < 0)) {
  1467         *lessOrEqualp = false;
  1468     } else {
  1469         uint32_t auint = Abs(aint);
  1470         uint32_t buint = Abs(bint);
  1472         /*
  1473          *  ... get number of digits of both integers.
  1474          * If they have the same number of digits --> arithmetic comparison.
  1475          * If digits_a > digits_b: a < b*10e(digits_a - digits_b).
  1476          * If digits_b > digits_a: a*10e(digits_b - digits_a) <= b.
  1477          */
  1478         unsigned digitsa = NumDigitsBase10(auint);
  1479         unsigned digitsb = NumDigitsBase10(buint);
  1480         if (digitsa == digitsb) {
  1481             *lessOrEqualp = (auint <= buint);
  1482         } else if (digitsa > digitsb) {
  1483             JS_ASSERT((digitsa - digitsb) < ArrayLength(powersOf10));
  1484             *lessOrEqualp = (uint64_t(auint) < uint64_t(buint) * powersOf10[digitsa - digitsb]);
  1485         } else { /* if (digitsb > digitsa) */
  1486             JS_ASSERT((digitsb - digitsa) < ArrayLength(powersOf10));
  1487             *lessOrEqualp = (uint64_t(auint) * powersOf10[digitsb - digitsa] <= uint64_t(buint));
  1491     return true;
  1494 static inline bool
  1495 CompareSubStringValues(JSContext *cx, const jschar *s1, size_t l1,
  1496                        const jschar *s2, size_t l2, bool *lessOrEqualp)
  1498     if (!CheckForInterrupt(cx))
  1499         return false;
  1501     if (!s1 || !s2)
  1502         return false;
  1504     int32_t result = CompareChars(s1, l1, s2, l2);
  1505     *lessOrEqualp = (result <= 0);
  1506     return true;
  1509 struct SortComparatorStrings
  1511     JSContext   *const cx;
  1513     SortComparatorStrings(JSContext *cx)
  1514       : cx(cx) {}
  1516     bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) {
  1517         return CompareStringValues(cx, a, b, lessOrEqualp);
  1519 };
  1521 struct SortComparatorLexicographicInt32
  1523     bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) {
  1524         return CompareLexicographicInt32(a, b, lessOrEqualp);
  1526 };
  1528 struct StringifiedElement
  1530     size_t charsBegin;
  1531     size_t charsEnd;
  1532     size_t elementIndex;
  1533 };
  1535 struct SortComparatorStringifiedElements
  1537     JSContext           *const cx;
  1538     const StringBuffer  &sb;
  1540     SortComparatorStringifiedElements(JSContext *cx, const StringBuffer &sb)
  1541       : cx(cx), sb(sb) {}
  1543     bool operator()(const StringifiedElement &a, const StringifiedElement &b, bool *lessOrEqualp) {
  1544         return CompareSubStringValues(cx, sb.begin() + a.charsBegin, a.charsEnd - a.charsBegin,
  1545                                       sb.begin() + b.charsBegin, b.charsEnd - b.charsBegin,
  1546                                       lessOrEqualp);
  1548 };
  1550 struct SortComparatorFunction
  1552     JSContext          *const cx;
  1553     const Value        &fval;
  1554     FastInvokeGuard    &fig;
  1556     SortComparatorFunction(JSContext *cx, const Value &fval, FastInvokeGuard &fig)
  1557       : cx(cx), fval(fval), fig(fig) { }
  1559     bool operator()(const Value &a, const Value &b, bool *lessOrEqualp);
  1560 };
  1562 bool
  1563 SortComparatorFunction::operator()(const Value &a, const Value &b, bool *lessOrEqualp)
  1565     /*
  1566      * array_sort deals with holes and undefs on its own and they should not
  1567      * come here.
  1568      */
  1569     JS_ASSERT(!a.isMagic() && !a.isUndefined());
  1570     JS_ASSERT(!a.isMagic() && !b.isUndefined());
  1572     if (!CheckForInterrupt(cx))
  1573         return false;
  1575     InvokeArgs &args = fig.args();
  1576     if (!args.init(2))
  1577         return false;
  1579     args.setCallee(fval);
  1580     args.setThis(UndefinedValue());
  1581     args[0].set(a);
  1582     args[1].set(b);
  1584     if (!fig.invoke(cx))
  1585         return false;
  1587     double cmp;
  1588     if (!ToNumber(cx, args.rval(), &cmp))
  1589         return false;
  1591     /*
  1592      * XXX eport some kind of error here if cmp is NaN? ECMA talks about
  1593      * 'consistent compare functions' that don't return NaN, but is silent
  1594      * about what the result should be. So we currently ignore it.
  1595      */
  1596     *lessOrEqualp = (IsNaN(cmp) || cmp <= 0);
  1597     return true;
  1600 struct NumericElement
  1602     double dv;
  1603     size_t elementIndex;
  1604 };
  1606 bool
  1607 ComparatorNumericLeftMinusRight(const NumericElement &a, const NumericElement &b,
  1608                                 bool *lessOrEqualp)
  1610     *lessOrEqualp = (a.dv <= b.dv);
  1611     return true;
  1614 bool
  1615 ComparatorNumericRightMinusLeft(const NumericElement &a, const NumericElement &b,
  1616                                 bool *lessOrEqualp)
  1618     *lessOrEqualp = (b.dv <= a.dv);
  1619     return true;
  1622 typedef bool (*ComparatorNumeric)(const NumericElement &a, const NumericElement &b,
  1623                                   bool *lessOrEqualp);
  1625 ComparatorNumeric SortComparatorNumerics[] = {
  1626     nullptr,
  1627     nullptr,
  1628     ComparatorNumericLeftMinusRight,
  1629     ComparatorNumericRightMinusLeft
  1630 };
  1632 bool
  1633 ComparatorInt32LeftMinusRight(const Value &a, const Value &b, bool *lessOrEqualp)
  1635     *lessOrEqualp = (a.toInt32() <= b.toInt32());
  1636     return true;
  1639 bool
  1640 ComparatorInt32RightMinusLeft(const Value &a, const Value &b, bool *lessOrEqualp)
  1642     *lessOrEqualp = (b.toInt32() <= a.toInt32());
  1643     return true;
  1646 typedef bool (*ComparatorInt32)(const Value &a, const Value &b, bool *lessOrEqualp);
  1648 ComparatorInt32 SortComparatorInt32s[] = {
  1649     nullptr,
  1650     nullptr,
  1651     ComparatorInt32LeftMinusRight,
  1652     ComparatorInt32RightMinusLeft
  1653 };
  1655 // Note: Values for this enum must match up with SortComparatorNumerics
  1656 // and SortComparatorInt32s.
  1657 enum ComparatorMatchResult {
  1658     Match_Failure = 0,
  1659     Match_None,
  1660     Match_LeftMinusRight,
  1661     Match_RightMinusLeft
  1662 };
  1664 /*
  1665  * Specialize behavior for comparator functions with particular common bytecode
  1666  * patterns: namely, |return x - y| and |return y - x|.
  1667  */
  1668 ComparatorMatchResult
  1669 MatchNumericComparator(JSContext *cx, const Value &v)
  1671     if (!v.isObject())
  1672         return Match_None;
  1674     JSObject &obj = v.toObject();
  1675     if (!obj.is<JSFunction>())
  1676         return Match_None;
  1678     JSFunction *fun = &obj.as<JSFunction>();
  1679     if (!fun->isInterpreted())
  1680         return Match_None;
  1682     JSScript *script = fun->getOrCreateScript(cx);
  1683     if (!script)
  1684         return Match_Failure;
  1686     jsbytecode *pc = script->code();
  1688     uint16_t arg0, arg1;
  1689     if (JSOp(*pc) != JSOP_GETARG)
  1690         return Match_None;
  1691     arg0 = GET_ARGNO(pc);
  1692     pc += JSOP_GETARG_LENGTH;
  1694     if (JSOp(*pc) != JSOP_GETARG)
  1695         return Match_None;
  1696     arg1 = GET_ARGNO(pc);
  1697     pc += JSOP_GETARG_LENGTH;
  1699     if (JSOp(*pc) != JSOP_SUB)
  1700         return Match_None;
  1701     pc += JSOP_SUB_LENGTH;
  1703     if (JSOp(*pc) != JSOP_RETURN)
  1704         return Match_None;
  1706     if (arg0 == 0 && arg1 == 1)
  1707         return Match_LeftMinusRight;
  1709     if (arg0 == 1 && arg1 == 0)
  1710         return Match_RightMinusLeft;
  1712     return Match_None;
  1715 template<typename K, typename C>
  1716 static inline bool
  1717 MergeSortByKey(K keys, size_t len, K scratch, C comparator, AutoValueVector *vec)
  1719     MOZ_ASSERT(vec->length() >= len);
  1721     /* Sort keys. */
  1722     if (!MergeSort(keys, len, scratch, comparator))
  1723         return false;
  1725     /*
  1726      * Reorder vec by keys in-place, going element by element.  When an out-of-
  1727      * place element is encountered, move that element to its proper position,
  1728      * displacing whatever element was at *that* point to its proper position,
  1729      * and so on until an element must be moved to the current position.
  1731      * At each outer iteration all elements up to |i| are sorted.  If
  1732      * necessary each inner iteration moves some number of unsorted elements
  1733      * (including |i|) directly to sorted position.  Thus on completion |*vec|
  1734      * is sorted, and out-of-position elements have moved once.  Complexity is
  1735      * Θ(len) + O(len) == O(2*len), with each element visited at most twice.
  1736      */
  1737     for (size_t i = 0; i < len; i++) {
  1738         size_t j = keys[i].elementIndex;
  1739         if (i == j)
  1740             continue; // fixed point
  1742         MOZ_ASSERT(j > i, "Everything less than |i| should be in the right place!");
  1743         Value tv = (*vec)[j];
  1744         do {
  1745             size_t k = keys[j].elementIndex;
  1746             keys[j].elementIndex = j;
  1747             (*vec)[j] = (*vec)[k];
  1748             j = k;
  1749         } while (j != i);
  1751         // We could assert the loop invariant that |i == keys[i].elementIndex|
  1752         // here if we synced |keys[i].elementIndex|.  But doing so would render
  1753         // the assertion vacuous, so don't bother, even in debug builds.
  1754         (*vec)[i] = tv;
  1757     return true;
  1760 /*
  1761  * Sort Values as strings.
  1763  * To minimize #conversions, SortLexicographically() first converts all Values
  1764  * to strings at once, then sorts the elements by these cached strings.
  1765  */
  1766 bool
  1767 SortLexicographically(JSContext *cx, AutoValueVector *vec, size_t len)
  1769     JS_ASSERT(vec->length() >= len);
  1771     StringBuffer sb(cx);
  1772     Vector<StringifiedElement, 0, TempAllocPolicy> strElements(cx);
  1774     /* MergeSort uses the upper half as scratch space. */
  1775     if (!strElements.reserve(2 * len))
  1776         return false;
  1778     /* Convert Values to strings. */
  1779     size_t cursor = 0;
  1780     for (size_t i = 0; i < len; i++) {
  1781         if (!CheckForInterrupt(cx))
  1782             return false;
  1784         if (!ValueToStringBuffer(cx, (*vec)[i], sb))
  1785             return false;
  1787         StringifiedElement el = { cursor, sb.length(), i };
  1788         strElements.infallibleAppend(el);
  1789         cursor = sb.length();
  1792     /* Resize strElements so we can perform MergeSort. */
  1793     JS_ALWAYS_TRUE(strElements.resize(2 * len));
  1795     /* Sort Values in vec alphabetically. */
  1796     return MergeSortByKey(strElements.begin(), len, strElements.begin() + len,
  1797                           SortComparatorStringifiedElements(cx, sb), vec);
  1800 /*
  1801  * Sort Values as numbers.
  1803  * To minimize #conversions, SortNumerically first converts all Values to
  1804  * numerics at once, then sorts the elements by these cached numerics.
  1805  */
  1806 bool
  1807 SortNumerically(JSContext *cx, AutoValueVector *vec, size_t len, ComparatorMatchResult comp)
  1809     JS_ASSERT(vec->length() >= len);
  1811     Vector<NumericElement, 0, TempAllocPolicy> numElements(cx);
  1813     /* MergeSort uses the upper half as scratch space. */
  1814     if (!numElements.reserve(2 * len))
  1815         return false;
  1817     /* Convert Values to numerics. */
  1818     for (size_t i = 0; i < len; i++) {
  1819         if (!CheckForInterrupt(cx))
  1820             return false;
  1822         double dv;
  1823         if (!ToNumber(cx, vec->handleAt(i), &dv))
  1824             return false;
  1826         NumericElement el = { dv, i };
  1827         numElements.infallibleAppend(el);
  1830     /* Resize strElements so we can perform MergeSort. */
  1831     JS_ALWAYS_TRUE(numElements.resize(2 * len));
  1833     /* Sort Values in vec numerically. */
  1834     return MergeSortByKey(numElements.begin(), len, numElements.begin() + len,
  1835                           SortComparatorNumerics[comp], vec);
  1838 } /* namespace anonymous */
  1840 bool
  1841 js::array_sort(JSContext *cx, unsigned argc, Value *vp)
  1843     CallArgs args = CallArgsFromVp(argc, vp);
  1845     RootedValue fvalRoot(cx);
  1846     Value &fval = fvalRoot.get();
  1848     if (args.hasDefined(0)) {
  1849         if (args[0].isPrimitive()) {
  1850             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_SORT_ARG);
  1851             return false;
  1853         fval = args[0];     /* non-default compare function */
  1854     } else {
  1855         fval.setNull();
  1858     RootedObject obj(cx, ToObject(cx, args.thisv()));
  1859     if (!obj)
  1860         return false;
  1862     uint32_t len;
  1863     if (!GetLengthProperty(cx, obj, &len))
  1864         return false;
  1865     if (len < 2) {
  1866         /* [] and [a] remain unchanged when sorted. */
  1867         args.rval().setObject(*obj);
  1868         return true;
  1871     /*
  1872      * We need a temporary array of 2 * len Value to hold the array elements
  1873      * and the scratch space for merge sort. Check that its size does not
  1874      * overflow size_t, which would allow for indexing beyond the end of the
  1875      * malloc'd vector.
  1876      */
  1877 #if JS_BITS_PER_WORD == 32
  1878     if (size_t(len) > size_t(-1) / (2 * sizeof(Value))) {
  1879         js_ReportAllocationOverflow(cx);
  1880         return false;
  1882 #endif
  1884     /*
  1885      * Initialize vec as a root. We will clear elements of vec one by
  1886      * one while increasing the rooted amount of vec when we know that the
  1887      * property at the corresponding index exists and its value must be rooted.
  1889      * In this way when sorting a huge mostly sparse array we will not
  1890      * access the tail of vec corresponding to properties that do not
  1891      * exist, allowing OS to avoiding committing RAM. See bug 330812.
  1892      */
  1893     size_t n, undefs;
  1895         AutoValueVector vec(cx);
  1896         if (!vec.reserve(2 * size_t(len)))
  1897             return false;
  1899         /*
  1900          * By ECMA 262, 15.4.4.11, a property that does not exist (which we
  1901          * call a "hole") is always greater than an existing property with
  1902          * value undefined and that is always greater than any other property.
  1903          * Thus to sort holes and undefs we simply count them, sort the rest
  1904          * of elements, append undefs after them and then make holes after
  1905          * undefs.
  1906          */
  1907         undefs = 0;
  1908         bool allStrings = true;
  1909         bool allInts = true;
  1910         RootedValue v(cx);
  1911         for (uint32_t i = 0; i < len; i++) {
  1912             if (!CheckForInterrupt(cx))
  1913                 return false;
  1915             /* Clear vec[newlen] before including it in the rooted set. */
  1916             bool hole;
  1917             if (!GetElement(cx, obj, i, &hole, &v))
  1918                 return false;
  1919             if (hole)
  1920                 continue;
  1921             if (v.isUndefined()) {
  1922                 ++undefs;
  1923                 continue;
  1925             vec.infallibleAppend(v);
  1926             allStrings = allStrings && v.isString();
  1927             allInts = allInts && v.isInt32();
  1931         /*
  1932          * If the array only contains holes, we're done.  But if it contains
  1933          * undefs, those must be sorted to the front of the array.
  1934          */
  1935         n = vec.length();
  1936         if (n == 0 && undefs == 0) {
  1937             args.rval().setObject(*obj);
  1938             return true;
  1941         /* Here len == n + undefs + number_of_holes. */
  1942         if (fval.isNull()) {
  1943             /*
  1944              * Sort using the default comparator converting all elements to
  1945              * strings.
  1946              */
  1947             if (allStrings) {
  1948                 JS_ALWAYS_TRUE(vec.resize(n * 2));
  1949                 if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorStrings(cx)))
  1950                     return false;
  1951             } else if (allInts) {
  1952                 JS_ALWAYS_TRUE(vec.resize(n * 2));
  1953                 if (!MergeSort(vec.begin(), n, vec.begin() + n,
  1954                                SortComparatorLexicographicInt32())) {
  1955                     return false;
  1957             } else {
  1958                 if (!SortLexicographically(cx, &vec, n))
  1959                     return false;
  1961         } else {
  1962             ComparatorMatchResult comp = MatchNumericComparator(cx, fval);
  1963             if (comp == Match_Failure)
  1964                 return false;
  1966             if (comp != Match_None) {
  1967                 if (allInts) {
  1968                     JS_ALWAYS_TRUE(vec.resize(n * 2));
  1969                     if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorInt32s[comp]))
  1970                         return false;
  1971                 } else {
  1972                     if (!SortNumerically(cx, &vec, n, comp))
  1973                         return false;
  1975             } else {
  1976                 FastInvokeGuard fig(cx, fval);
  1977                 MOZ_ASSERT(!InParallelSection(),
  1978                            "Array.sort() can't currently be used from parallel code");
  1979                 JS_ALWAYS_TRUE(vec.resize(n * 2));
  1980                 if (!MergeSort(vec.begin(), n, vec.begin() + n,
  1981                                SortComparatorFunction(cx, fval, fig)))
  1983                     return false;
  1988         if (!InitArrayElements(cx, obj, 0, uint32_t(n), vec.begin(), DontUpdateTypes))
  1989             return false;
  1992     /* Set undefs that sorted after the rest of elements. */
  1993     while (undefs != 0) {
  1994         --undefs;
  1995         if (!CheckForInterrupt(cx) || !SetArrayElement(cx, obj, n++, UndefinedHandleValue))
  1996             return false;
  1999     /* Re-create any holes that sorted to the end of the array. */
  2000     while (len > n) {
  2001         if (!CheckForInterrupt(cx) || !DeletePropertyOrThrow(cx, obj, --len))
  2002             return false;
  2004     args.rval().setObject(*obj);
  2005     return true;
  2008 bool
  2009 js::NewbornArrayPush(JSContext *cx, HandleObject obj, const Value &v)
  2011     Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
  2013     JS_ASSERT(!v.isMagic());
  2014     JS_ASSERT(arr->lengthIsWritable());
  2016     uint32_t length = arr->length();
  2017     JS_ASSERT(length <= arr->getDenseCapacity());
  2019     if (!arr->ensureElements(cx, length + 1))
  2020         return false;
  2022     arr->setDenseInitializedLength(length + 1);
  2023     arr->setLengthInt32(length + 1);
  2024     arr->initDenseElementWithType(cx, length, v);
  2025     return true;
  2028 /* ES5 15.4.4.7 */
  2029 bool
  2030 js::array_push(JSContext *cx, unsigned argc, Value *vp)
  2032     CallArgs args = CallArgsFromVp(argc, vp);
  2034     /* Step 1. */
  2035     RootedObject obj(cx, ToObject(cx, args.thisv()));
  2036     if (!obj)
  2037         return false;
  2039     /* Steps 2-3. */
  2040     uint32_t length;
  2041     if (!GetLengthProperty(cx, obj, &length))
  2042         return false;
  2044     /* Fast path for native objects with dense elements. */
  2045     do {
  2046         if (!obj->isNative() || obj->is<TypedArrayObject>())
  2047             break;
  2049         if (obj->is<ArrayObject>() && !obj->as<ArrayObject>().lengthIsWritable())
  2050             break;
  2052         if (ObjectMayHaveExtraIndexedProperties(obj))
  2053             break;
  2055         uint32_t argCount = args.length();
  2056         JSObject::EnsureDenseResult result = obj->ensureDenseElements(cx, length, argCount);
  2057         if (result == JSObject::ED_FAILED)
  2058             return false;
  2060         if (result == JSObject::ED_OK) {
  2061             for (uint32_t i = 0, index = length; i < argCount; index++, i++)
  2062                 obj->setDenseElementWithType(cx, index, args[i]);
  2063             uint32_t newlength = length + argCount;
  2064             args.rval().setNumber(newlength);
  2065             if (obj->is<ArrayObject>()) {
  2066                 obj->as<ArrayObject>().setLengthInt32(newlength);
  2067                 return true;
  2069             return SetLengthProperty(cx, obj, newlength);
  2072         MOZ_ASSERT(result == JSObject::ED_SPARSE);
  2073     } while (false);
  2075     /* Steps 4-5. */
  2076     if (!InitArrayElements(cx, obj, length, args.length(), args.array(), UpdateTypes))
  2077         return false;
  2079     /* Steps 6-7. */
  2080     double newlength = length + double(args.length());
  2081     args.rval().setNumber(newlength);
  2082     return SetLengthProperty(cx, obj, newlength);
  2085 /* ES6 20130308 draft 15.4.4.6. */
  2086 bool
  2087 js::array_pop(JSContext *cx, unsigned argc, Value *vp)
  2089     CallArgs args = CallArgsFromVp(argc, vp);
  2091     /* Step 1. */
  2092     RootedObject obj(cx, ToObject(cx, args.thisv()));
  2093     if (!obj)
  2094         return false;
  2096     /* Steps 2-3. */
  2097     uint32_t index;
  2098     if (!GetLengthProperty(cx, obj, &index))
  2099         return false;
  2101     /* Steps 4-5. */
  2102     if (index == 0) {
  2103         /* Step 4b. */
  2104         args.rval().setUndefined();
  2105     } else {
  2106         /* Step 5a. */
  2107         index--;
  2109         /* Step 5b, 5e. */
  2110         bool hole;
  2111         if (!GetElement(cx, obj, index, &hole, args.rval()))
  2112             return false;
  2114         /* Step 5c. */
  2115         if (!hole && !DeletePropertyOrThrow(cx, obj, index))
  2116             return false;
  2119     // If this was an array, then there are no elements above the one we just
  2120     // deleted (if we deleted an element).  Thus we can shrink the dense
  2121     // initialized length accordingly.  (This is fine even if the array length
  2122     // is non-writable: length-changing occurs after element-deletion effects.)
  2123     // Don't do anything if this isn't an array, as any deletion above has no
  2124     // effect on any elements after the "last" one indicated by the "length"
  2125     // property.
  2126     if (obj->is<ArrayObject>() && obj->getDenseInitializedLength() > index)
  2127         obj->setDenseInitializedLength(index);
  2129     /* Steps 4a, 5d. */
  2130     return SetLengthProperty(cx, obj, index);
  2133 void
  2134 js::ArrayShiftMoveElements(JSObject *obj)
  2136     JS_ASSERT(obj->is<ArrayObject>());
  2137     JS_ASSERT(obj->as<ArrayObject>().lengthIsWritable());
  2139     /*
  2140      * At this point the length and initialized length have already been
  2141      * decremented and the result fetched, so just shift the array elements
  2142      * themselves.
  2143      */
  2144     uint32_t initlen = obj->getDenseInitializedLength();
  2145     obj->moveDenseElementsNoPreBarrier(0, 1, initlen);
  2148 /* ES5 15.4.4.9 */
  2149 bool
  2150 js::array_shift(JSContext *cx, unsigned argc, Value *vp)
  2152     CallArgs args = CallArgsFromVp(argc, vp);
  2154     /* Step 1. */
  2155     RootedObject obj(cx, ToObject(cx, args.thisv()));
  2156     if (!obj)
  2157         return false;
  2159     /* Steps 2-3. */
  2160     uint32_t len;
  2161     if (!GetLengthProperty(cx, obj, &len))
  2162         return false;
  2164     /* Step 4. */
  2165     if (len == 0) {
  2166         /* Step 4a. */
  2167         if (!SetLengthProperty(cx, obj, 0))
  2168             return false;
  2170         /* Step 4b. */
  2171         args.rval().setUndefined();
  2172         return true;
  2175     uint32_t newlen = len - 1;
  2177     /* Fast paths. */
  2178     if (obj->is<ArrayObject>() &&
  2179         obj->getDenseInitializedLength() > 0 &&
  2180         newlen < obj->getDenseCapacity() &&
  2181         !ObjectMayHaveExtraIndexedProperties(obj))
  2183         args.rval().set(obj->getDenseElement(0));
  2184         if (args.rval().isMagic(JS_ELEMENTS_HOLE))
  2185             args.rval().setUndefined();
  2187         obj->moveDenseElements(0, 1, obj->getDenseInitializedLength() - 1);
  2188         obj->setDenseInitializedLength(obj->getDenseInitializedLength() - 1);
  2190         if (!SetLengthProperty(cx, obj, newlen))
  2191             return false;
  2193         return js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(newlen));
  2196     /* Steps 5, 10. */
  2197     bool hole;
  2198     if (!GetElement(cx, obj, uint32_t(0), &hole, args.rval()))
  2199         return false;
  2201     /* Steps 6-7. */
  2202     RootedValue value(cx);
  2203     for (uint32_t i = 0; i < newlen; i++) {
  2204         if (!CheckForInterrupt(cx))
  2205             return false;
  2206         if (!GetElement(cx, obj, i + 1, &hole, &value))
  2207             return false;
  2208         if (hole) {
  2209             if (!DeletePropertyOrThrow(cx, obj, i))
  2210                 return false;
  2211         } else {
  2212             if (!SetArrayElement(cx, obj, i, value))
  2213                 return false;
  2217     /* Step 8. */
  2218     if (!DeletePropertyOrThrow(cx, obj, newlen))
  2219         return false;
  2221     /* Step 9. */
  2222     return SetLengthProperty(cx, obj, newlen);
  2225 static bool
  2226 array_unshift(JSContext *cx, unsigned argc, Value *vp)
  2228     CallArgs args = CallArgsFromVp(argc, vp);
  2229     RootedObject obj(cx, ToObject(cx, args.thisv()));
  2230     if (!obj)
  2231         return false;
  2233     uint32_t length;
  2234     if (!GetLengthProperty(cx, obj, &length))
  2235         return false;
  2237     double newlen = length;
  2238     if (args.length() > 0) {
  2239         /* Slide up the array to make room for all args at the bottom. */
  2240         if (length > 0) {
  2241             bool optimized = false;
  2242             do {
  2243                 if (!obj->is<ArrayObject>())
  2244                     break;
  2245                 if (ObjectMayHaveExtraIndexedProperties(obj))
  2246                     break;
  2247                 if (!obj->as<ArrayObject>().lengthIsWritable())
  2248                     break;
  2249                 JSObject::EnsureDenseResult result = obj->ensureDenseElements(cx, length, args.length());
  2250                 if (result != JSObject::ED_OK) {
  2251                     if (result == JSObject::ED_FAILED)
  2252                         return false;
  2253                     JS_ASSERT(result == JSObject::ED_SPARSE);
  2254                     break;
  2256                 obj->moveDenseElements(args.length(), 0, length);
  2257                 for (uint32_t i = 0; i < args.length(); i++)
  2258                     obj->setDenseElement(i, MagicValue(JS_ELEMENTS_HOLE));
  2259                 optimized = true;
  2260             } while (false);
  2262             if (!optimized) {
  2263                 double last = length;
  2264                 double upperIndex = last + args.length();
  2265                 RootedValue value(cx);
  2266                 do {
  2267                     --last, --upperIndex;
  2268                     bool hole;
  2269                     if (!CheckForInterrupt(cx))
  2270                         return false;
  2271                     if (!GetElement(cx, obj, last, &hole, &value))
  2272                         return false;
  2273                     if (hole) {
  2274                         if (!DeletePropertyOrThrow(cx, obj, upperIndex))
  2275                             return false;
  2276                     } else {
  2277                         if (!SetArrayElement(cx, obj, upperIndex, value))
  2278                             return false;
  2280                 } while (last != 0);
  2284         /* Copy from args to the bottom of the array. */
  2285         if (!InitArrayElements(cx, obj, 0, args.length(), args.array(), UpdateTypes))
  2286             return false;
  2288         newlen += args.length();
  2290     if (!SetLengthProperty(cx, obj, newlen))
  2291         return false;
  2293     /* Follow Perl by returning the new array length. */
  2294     args.rval().setNumber(newlen);
  2295     return true;
  2298 static inline void
  2299 TryReuseArrayType(JSObject *obj, ArrayObject *narr)
  2301     /*
  2302      * Try to change the type of a newly created array narr to the same type
  2303      * as obj. This can only be performed if the original object is an array
  2304      * and has the same prototype.
  2305      */
  2306     JS_ASSERT(narr->getProto()->hasNewType(&ArrayObject::class_, narr->type()));
  2308     if (obj->is<ArrayObject>() && !obj->hasSingletonType() && obj->getProto() == narr->getProto())
  2309         narr->setType(obj->type());
  2312 /*
  2313  * Returns true if this is a dense array whose |count| properties starting from
  2314  * |startingIndex| may be accessed (get, set, delete) directly through its
  2315  * contiguous vector of elements without fear of getters, setters, etc. along
  2316  * the prototype chain, or of enumerators requiring notification of
  2317  * modifications.
  2318  */
  2319 static inline bool
  2320 CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t count, JSContext *cx)
  2322     /* If the desired properties overflow dense storage, we can't optimize. */
  2323     if (UINT32_MAX - startingIndex < count)
  2324         return false;
  2326     /* There's no optimizing possible if it's not an array. */
  2327     if (!arr->is<ArrayObject>())
  2328         return false;
  2330     /*
  2331      * Don't optimize if the array might be in the midst of iteration.  We
  2332      * rely on this to be able to safely move dense array elements around with
  2333      * just a memmove (see JSObject::moveDenseArrayElements), without worrying
  2334      * about updating any in-progress enumerators for properties implicitly
  2335      * deleted if a hole is moved from one location to another location not yet
  2336      * visited.  See bug 690622.
  2338      * Another potential wrinkle: what if the enumeration is happening on an
  2339      * object which merely has |arr| on its prototype chain?  It turns out this
  2340      * case can't happen, because any dense array used as the prototype of
  2341      * another object is first slowified, for type inference's sake.
  2342      */
  2343     types::TypeObject *arrType = arr->getType(cx);
  2344     if (MOZ_UNLIKELY(!arrType || arrType->hasAllFlags(OBJECT_FLAG_ITERATED)))
  2345         return false;
  2347     /*
  2348      * Now watch out for getters and setters along the prototype chain or in
  2349      * other indexed properties on the object.  (Note that non-writable length
  2350      * is subsumed by the initializedLength comparison.)
  2351      */
  2352     return !ObjectMayHaveExtraIndexedProperties(arr) &&
  2353            startingIndex + count <= arr->getDenseInitializedLength();
  2356 /* ES5 15.4.4.12. */
  2357 bool
  2358 js::array_splice(JSContext *cx, unsigned argc, Value *vp)
  2360     return array_splice_impl(cx, argc, vp, true);
  2363 bool
  2364 js::array_splice_impl(JSContext *cx, unsigned argc, Value *vp, bool returnValueIsUsed)
  2366     CallArgs args = CallArgsFromVp(argc, vp);
  2368     /* Step 1. */
  2369     RootedObject obj(cx, ToObject(cx, args.thisv()));
  2370     if (!obj)
  2371         return false;
  2373     /* Steps 3-4. */
  2374     uint32_t len;
  2375     if (!GetLengthProperty(cx, obj, &len))
  2376         return false;
  2378     /* Step 5. */
  2379     double relativeStart;
  2380     if (!ToInteger(cx, args.get(0), &relativeStart))
  2381         return false;
  2383     /* Step 6. */
  2384     uint32_t actualStart;
  2385     if (relativeStart < 0)
  2386         actualStart = Max(len + relativeStart, 0.0);
  2387     else
  2388         actualStart = Min(relativeStart, double(len));
  2390     /* Step 7. */
  2391     uint32_t actualDeleteCount;
  2392     if (args.length() != 1) {
  2393         double deleteCountDouble;
  2394         RootedValue cnt(cx, args.length() >= 2 ? args[1] : Int32Value(0));
  2395         if (!ToInteger(cx, cnt, &deleteCountDouble))
  2396             return false;
  2397         actualDeleteCount = Min(Max(deleteCountDouble, 0.0), double(len - actualStart));
  2398     } else {
  2399         /*
  2400          * Non-standard: if start was specified but deleteCount was omitted,
  2401          * delete to the end of the array.  See bug 668024 for discussion.
  2402          */
  2403         actualDeleteCount = len - actualStart;
  2406     JS_ASSERT(len - actualStart >= actualDeleteCount);
  2408     /* Steps 2, 8-9. */
  2409     Rooted<ArrayObject*> arr(cx);
  2410     if (CanOptimizeForDenseStorage(obj, actualStart, actualDeleteCount, cx)) {
  2411         if (returnValueIsUsed) {
  2412             arr = NewDenseCopiedArray(cx, actualDeleteCount, obj, actualStart);
  2413             if (!arr)
  2414                 return false;
  2415             TryReuseArrayType(obj, arr);
  2417     } else {
  2418         arr = NewDenseAllocatedArray(cx, actualDeleteCount);
  2419         if (!arr)
  2420             return false;
  2421         TryReuseArrayType(obj, arr);
  2423         RootedValue fromValue(cx);
  2424         for (uint32_t k = 0; k < actualDeleteCount; k++) {
  2425             bool hole;
  2426             if (!CheckForInterrupt(cx) ||
  2427                 !GetElement(cx, obj, actualStart + k, &hole, &fromValue) ||
  2428                 (!hole && !JSObject::defineElement(cx, arr, k, fromValue)))
  2430                 return false;
  2435     /* Step 11. */
  2436     uint32_t itemCount = (args.length() >= 2) ? (args.length() - 2) : 0;
  2438     if (itemCount < actualDeleteCount) {
  2439         /* Step 12: the array is being shrunk. */
  2440         uint32_t sourceIndex = actualStart + actualDeleteCount;
  2441         uint32_t targetIndex = actualStart + itemCount;
  2442         uint32_t finalLength = len - actualDeleteCount + itemCount;
  2444         if (CanOptimizeForDenseStorage(obj, 0, len, cx)) {
  2445             /* Steps 12(a)-(b). */
  2446             obj->moveDenseElements(targetIndex, sourceIndex, len - sourceIndex);
  2448             /*
  2449              * Update the initialized length. Do so before shrinking so that we
  2450              * can apply the write barrier to the old slots.
  2451              */
  2452             obj->setDenseInitializedLength(finalLength);
  2454             /* Steps 12(c)-(d). */
  2455             obj->shrinkElements(cx, finalLength);
  2456         } else {
  2457             /*
  2458              * This is all very slow if the length is very large. We don't yet
  2459              * have the ability to iterate in sorted order, so we just do the
  2460              * pessimistic thing and let CheckForInterrupt handle the
  2461              * fallout.
  2462              */
  2464             /* Steps 12(a)-(b). */
  2465             RootedValue fromValue(cx);
  2466             for (uint32_t from = sourceIndex, to = targetIndex; from < len; from++, to++) {
  2467                 if (!CheckForInterrupt(cx))
  2468                     return false;
  2470                 bool hole;
  2471                 if (!GetElement(cx, obj, from, &hole, &fromValue))
  2472                     return false;
  2473                 if (hole) {
  2474                     if (!DeletePropertyOrThrow(cx, obj, to))
  2475                         return false;
  2476                 } else {
  2477                     if (!SetArrayElement(cx, obj, to, fromValue))
  2478                         return false;
  2482             /* Steps 12(c)-(d). */
  2483             for (uint32_t k = len; k > finalLength; k--) {
  2484                 if (!DeletePropertyOrThrow(cx, obj, k - 1))
  2485                     return false;
  2488     } else if (itemCount > actualDeleteCount) {
  2489         /* Step 13. */
  2491         /*
  2492          * Optimize only if the array is already dense and we can extend it to
  2493          * its new length.  It would be wrong to extend the elements here for a
  2494          * number of reasons.
  2496          * First, this could cause us to fall into the fast-path below.  This
  2497          * would cause elements to be moved into places past the non-writable
  2498          * length.  And when the dense initialized length is updated, that'll
  2499          * cause the |in| operator to think that those elements actually exist,
  2500          * even though, properly, setting them must fail.
  2502          * Second, extending the elements here will trigger assertions inside
  2503          * ensureDenseElements that the elements aren't being extended past the
  2504          * length of a non-writable array.  This is because extending elements
  2505          * will extend capacity -- which might extend them past a non-writable
  2506          * length, violating the |capacity <= length| invariant for such
  2507          * arrays.  And that would make the various JITted fast-path method
  2508          * implementations of [].push, [].unshift, and so on wrong.
  2510          * If the array length is non-writable, this method *will* throw.  For
  2511          * simplicity, have the slow-path code do it.  (Also note that the slow
  2512          * path may validly *not* throw -- if all the elements being moved are
  2513          * holes.)
  2514          */
  2515         if (obj->is<ArrayObject>()) {
  2516             Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
  2517             if (arr->lengthIsWritable()) {
  2518                 JSObject::EnsureDenseResult res =
  2519                     arr->ensureDenseElements(cx, arr->length(), itemCount - actualDeleteCount);
  2520                 if (res == JSObject::ED_FAILED)
  2521                     return false;
  2525         if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) {
  2526             obj->moveDenseElements(actualStart + itemCount,
  2527                                    actualStart + actualDeleteCount,
  2528                                    len - (actualStart + actualDeleteCount));
  2529             obj->setDenseInitializedLength(len + itemCount - actualDeleteCount);
  2530         } else {
  2531             RootedValue fromValue(cx);
  2532             for (double k = len - actualDeleteCount; k > actualStart; k--) {
  2533                 if (!CheckForInterrupt(cx))
  2534                     return false;
  2536                 double from = k + actualDeleteCount - 1;
  2537                 double to = k + itemCount - 1;
  2539                 bool hole;
  2540                 if (!GetElement(cx, obj, from, &hole, &fromValue))
  2541                     return false;
  2543                 if (hole) {
  2544                     if (!DeletePropertyOrThrow(cx, obj, to))
  2545                         return false;
  2546                 } else {
  2547                     if (!SetArrayElement(cx, obj, to, fromValue))
  2548                         return false;
  2554     /* Step 10. */
  2555     Value *items = args.array() + 2;
  2557     /* Steps 14-15. */
  2558     for (uint32_t k = actualStart, i = 0; i < itemCount; i++, k++) {
  2559         if (!SetArrayElement(cx, obj, k, HandleValue::fromMarkedLocation(&items[i])))
  2560             return false;
  2563     /* Step 16. */
  2564     double finalLength = double(len) - actualDeleteCount + itemCount;
  2565     if (!SetLengthProperty(cx, obj, finalLength))
  2566         return false;
  2568     /* Step 17. */
  2569     if (returnValueIsUsed)
  2570         args.rval().setObject(*arr);
  2572     return true;
  2575 #ifdef JS_ION
  2576 bool
  2577 js::array_concat_dense(JSContext *cx, Handle<ArrayObject*> arr1, Handle<ArrayObject*> arr2,
  2578                        Handle<ArrayObject*> result)
  2580     uint32_t initlen1 = arr1->getDenseInitializedLength();
  2581     JS_ASSERT(initlen1 == arr1->length());
  2583     uint32_t initlen2 = arr2->getDenseInitializedLength();
  2584     JS_ASSERT(initlen2 == arr2->length());
  2586     /* No overflow here due to nelements limit. */
  2587     uint32_t len = initlen1 + initlen2;
  2589     if (!result->ensureElements(cx, len))
  2590         return false;
  2592     JS_ASSERT(!result->getDenseInitializedLength());
  2593     result->setDenseInitializedLength(len);
  2595     result->initDenseElements(0, arr1->getDenseElements(), initlen1);
  2596     result->initDenseElements(initlen1, arr2->getDenseElements(), initlen2);
  2597     result->setLengthInt32(len);
  2598     return true;
  2600 #endif /* JS_ION */
  2602 /*
  2603  * Python-esque sequence operations.
  2604  */
  2605 bool
  2606 js::array_concat(JSContext *cx, unsigned argc, Value *vp)
  2608     CallArgs args = CallArgsFromVp(argc, vp);
  2610     /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
  2611     Value *p = args.array() - 1;
  2613     /* Create a new Array object and root it using *vp. */
  2614     RootedObject aobj(cx, ToObject(cx, args.thisv()));
  2615     if (!aobj)
  2616         return false;
  2618     Rooted<ArrayObject*> narr(cx);
  2619     uint32_t length;
  2620     if (aobj->is<ArrayObject>() && !aobj->isIndexed()) {
  2621         length = aobj->as<ArrayObject>().length();
  2622         uint32_t initlen = aobj->getDenseInitializedLength();
  2623         narr = NewDenseCopiedArray(cx, initlen, aobj, 0);
  2624         if (!narr)
  2625             return false;
  2626         TryReuseArrayType(aobj, narr);
  2627         narr->setLength(cx, length);
  2628         args.rval().setObject(*narr);
  2629         if (argc == 0)
  2630             return true;
  2631         argc--;
  2632         p++;
  2633     } else {
  2634         narr = NewDenseEmptyArray(cx);
  2635         if (!narr)
  2636             return false;
  2637         args.rval().setObject(*narr);
  2638         length = 0;
  2641     /* Loop over [0, argc] to concat args into narr, expanding all Arrays. */
  2642     for (unsigned i = 0; i <= argc; i++) {
  2643         if (!CheckForInterrupt(cx))
  2644             return false;
  2645         HandleValue v = HandleValue::fromMarkedLocation(&p[i]);
  2646         if (v.isObject()) {
  2647             RootedObject obj(cx, &v.toObject());
  2648             if (ObjectClassIs(obj, ESClass_Array, cx)) {
  2649                 uint32_t alength;
  2650                 if (!GetLengthProperty(cx, obj, &alength))
  2651                     return false;
  2652                 RootedValue tmp(cx);
  2653                 for (uint32_t slot = 0; slot < alength; slot++) {
  2654                     bool hole;
  2655                     if (!CheckForInterrupt(cx) || !GetElement(cx, obj, slot, &hole, &tmp))
  2656                         return false;
  2658                     /*
  2659                      * Per ECMA 262, 15.4.4.4, step 9, ignore nonexistent
  2660                      * properties.
  2661                      */
  2662                     if (!hole && !SetArrayElement(cx, narr, length + slot, tmp))
  2663                         return false;
  2665                 length += alength;
  2666                 continue;
  2670         if (!SetArrayElement(cx, narr, length, v))
  2671             return false;
  2672         length++;
  2675     return SetLengthProperty(cx, narr, length);
  2678 static bool
  2679 array_slice(JSContext *cx, unsigned argc, Value *vp)
  2681     CallArgs args = CallArgsFromVp(argc, vp);
  2683     RootedObject obj(cx, ToObject(cx, args.thisv()));
  2684     if (!obj)
  2685         return false;
  2687     uint32_t length;
  2688     if (!GetLengthProperty(cx, obj, &length))
  2689         return false;
  2691     uint32_t begin = 0;
  2692     uint32_t end = length;
  2693     if (args.length() > 0) {
  2694         double d;
  2695         if (!ToInteger(cx, args[0], &d))
  2696             return false;
  2697         if (d < 0) {
  2698             d += length;
  2699             if (d < 0)
  2700                 d = 0;
  2701         } else if (d > length) {
  2702             d = length;
  2704         begin = (uint32_t)d;
  2706         if (args.hasDefined(1)) {
  2707             if (!ToInteger(cx, args[1], &d))
  2708                 return false;
  2709             if (d < 0) {
  2710                 d += length;
  2711                 if (d < 0)
  2712                     d = 0;
  2713             } else if (d > length) {
  2714                 d = length;
  2716             end = (uint32_t)d;
  2720     if (begin > end)
  2721         begin = end;
  2723     Rooted<ArrayObject*> narr(cx);
  2724     narr = NewDenseAllocatedArray(cx, end - begin);
  2725     if (!narr)
  2726         return false;
  2727     TryReuseArrayType(obj, narr);
  2729     if (obj->is<ArrayObject>() && !ObjectMayHaveExtraIndexedProperties(obj)) {
  2730         if (obj->getDenseInitializedLength() > begin) {
  2731             uint32_t numSourceElements = obj->getDenseInitializedLength() - begin;
  2732             uint32_t initLength = Min(numSourceElements, end - begin);
  2733             narr->setDenseInitializedLength(initLength);
  2734             narr->initDenseElements(0, &obj->getDenseElement(begin), initLength);
  2736         args.rval().setObject(*narr);
  2737         return true;
  2740     if (js::SliceOp op = obj->getOps()->slice) {
  2741         // Ensure that we have dense elements, so that DOM can use js::UnsafeDefineElement.
  2742         JSObject::EnsureDenseResult result = narr->ensureDenseElements(cx, 0, end - begin);
  2743         if (result == JSObject::ED_FAILED)
  2744              return false;
  2746         if (result == JSObject::ED_OK) {
  2747             if (!op(cx, obj, begin, end, narr))
  2748                 return false;
  2750             args.rval().setObject(*narr);
  2751             return true;
  2754         // Fallthrough
  2755         JS_ASSERT(result == JSObject::ED_SPARSE);
  2759     if (!SliceSlowly(cx, obj, obj, begin, end, narr))
  2760         return false;
  2762     args.rval().setObject(*narr);
  2763     return true;
  2766 JS_FRIEND_API(bool)
  2767 js::SliceSlowly(JSContext* cx, HandleObject obj, HandleObject receiver,
  2768                 uint32_t begin, uint32_t end, HandleObject result)
  2770     RootedValue value(cx);
  2771     for (uint32_t slot = begin; slot < end; slot++) {
  2772         bool hole;
  2773         if (!CheckForInterrupt(cx) ||
  2774             !GetElement(cx, obj, receiver, slot, &hole, &value))
  2776             return false;
  2778         if (!hole && !JSObject::defineElement(cx, result, slot - begin, value))
  2779             return false;
  2781     return true;
  2784 /* ES5 15.4.4.20. */
  2785 static bool
  2786 array_filter(JSContext *cx, unsigned argc, Value *vp)
  2788     CallArgs args = CallArgsFromVp(argc, vp);
  2790     /* Step 1. */
  2791     RootedObject obj(cx, ToObject(cx, args.thisv()));
  2792     if (!obj)
  2793         return false;
  2795     /* Step 2-3. */
  2796     uint32_t len;
  2797     if (!GetLengthProperty(cx, obj, &len))
  2798         return false;
  2800     /* Step 4. */
  2801     if (args.length() == 0) {
  2802         js_ReportMissingArg(cx, args.calleev(), 0);
  2803         return false;
  2805     RootedObject callable(cx, ValueToCallable(cx, args[0], args.length() - 1));
  2806     if (!callable)
  2807         return false;
  2809     /* Step 5. */
  2810     RootedValue thisv(cx, args.length() >= 2 ? args[1] : UndefinedValue());
  2812     /* Step 6. */
  2813     RootedObject arr(cx, NewDenseAllocatedArray(cx, 0));
  2814     if (!arr)
  2815         return false;
  2816     TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array);
  2817     if (!newtype)
  2818         return false;
  2819     arr->setType(newtype);
  2821     /* Step 7. */
  2822     uint32_t k = 0;
  2824     /* Step 8. */
  2825     uint32_t to = 0;
  2827     /* Step 9. */
  2828     JS_ASSERT(!InParallelSection());
  2829     FastInvokeGuard fig(cx, ObjectValue(*callable));
  2830     InvokeArgs &args2 = fig.args();
  2831     RootedValue kValue(cx);
  2832     while (k < len) {
  2833         if (!CheckForInterrupt(cx))
  2834             return false;
  2836         /* Step a, b, and c.i. */
  2837         bool kNotPresent;
  2838         if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
  2839             return false;
  2841         /* Step c.ii-iii. */
  2842         if (!kNotPresent) {
  2843             if (!args2.init(3))
  2844                 return false;
  2845             args2.setCallee(ObjectValue(*callable));
  2846             args2.setThis(thisv);
  2847             args2[0].set(kValue);
  2848             args2[1].setNumber(k);
  2849             args2[2].setObject(*obj);
  2850             if (!fig.invoke(cx))
  2851                 return false;
  2853             if (ToBoolean(args2.rval())) {
  2854                 if (!SetArrayElement(cx, arr, to, kValue))
  2855                     return false;
  2856                 to++;
  2860         /* Step d. */
  2861         k++;
  2864     /* Step 10. */
  2865     args.rval().setObject(*arr);
  2866     return true;
  2869 static bool
  2870 array_isArray(JSContext *cx, unsigned argc, Value *vp)
  2872     CallArgs args = CallArgsFromVp(argc, vp);
  2873     bool isArray = args.length() > 0 && IsObjectWithClass(args[0], ESClass_Array, cx);
  2874     args.rval().setBoolean(isArray);
  2875     return true;
  2878 static bool
  2879 IsArrayConstructor(const Value &v)
  2881     // This must only return true if v is *the* Array constructor for the
  2882     // current compartment; we rely on the fact that any other Array
  2883     // constructor would be represented as a wrapper.
  2884     return v.isObject() &&
  2885            v.toObject().is<JSFunction>() &&
  2886            v.toObject().as<JSFunction>().isNative() &&
  2887            v.toObject().as<JSFunction>().native() == js_Array;
  2890 static bool
  2891 ArrayFromCallArgs(JSContext *cx, RootedTypeObject &type, CallArgs &args)
  2893     if (!InitArrayTypes(cx, type, args.array(), args.length()))
  2894         return false;
  2895     JSObject *obj = (args.length() == 0)
  2896         ? NewDenseEmptyArray(cx)
  2897         : NewDenseCopiedArray(cx, args.length(), args.array());
  2898     if (!obj)
  2899         return false;
  2900     obj->setType(type);
  2901     args.rval().setObject(*obj);
  2902     return true;
  2905 static bool
  2906 array_of(JSContext *cx, unsigned argc, Value *vp)
  2908     CallArgs args = CallArgsFromVp(argc, vp);
  2910     if (IsArrayConstructor(args.thisv()) || !IsConstructor(args.thisv())) {
  2911         // IsArrayConstructor(this) will usually be true in practice. This is
  2912         // the most common path.
  2913         RootedTypeObject type(cx, GetTypeCallerInitObject(cx, JSProto_Array));
  2914         if (!type)
  2915             return false;
  2916         return ArrayFromCallArgs(cx, type, args);
  2919     // Step 4.
  2920     RootedObject obj(cx);
  2922         RootedValue v(cx);
  2923         Value argv[1] = {NumberValue(args.length())};
  2924         if (!InvokeConstructor(cx, args.thisv(), 1, argv, v.address()))
  2925             return false;
  2926         obj = ToObject(cx, v);
  2927         if (!obj)
  2928             return false;
  2931     // Step 8.
  2932     for (unsigned k = 0; k < args.length(); k++) {
  2933         if (!JSObject::defineElement(cx, obj, k, args[k]))
  2934             return false;
  2937     // Steps 9-10.
  2938     RootedValue v(cx, NumberValue(args.length()));
  2939     if (!JSObject::setProperty(cx, obj, obj, cx->names().length, &v, true))
  2940         return false;
  2942     // Step 11.
  2943     args.rval().setObject(*obj);
  2944     return true;
  2947 #define GENERIC JSFUN_GENERIC_NATIVE
  2949 static const JSFunctionSpec array_methods[] = {
  2950 #if JS_HAS_TOSOURCE
  2951     JS_FN(js_toSource_str,      array_toSource,     0,0),
  2952 #endif
  2953     JS_FN(js_toString_str,      array_toString,     0,0),
  2954     JS_FN(js_toLocaleString_str,array_toLocaleString,0,0),
  2956     /* Perl-ish methods. */
  2957     JS_FN("join",               array_join,         1,JSFUN_GENERIC_NATIVE),
  2958     JS_FN("reverse",            array_reverse,      0,JSFUN_GENERIC_NATIVE),
  2959     JS_FN("sort",               array_sort,         1,JSFUN_GENERIC_NATIVE),
  2960     JS_FN("push",               array_push,         1,JSFUN_GENERIC_NATIVE),
  2961     JS_FN("pop",                array_pop,          0,JSFUN_GENERIC_NATIVE),
  2962     JS_FN("shift",              array_shift,        0,JSFUN_GENERIC_NATIVE),
  2963     JS_FN("unshift",            array_unshift,      1,JSFUN_GENERIC_NATIVE),
  2964     JS_FN("splice",             array_splice,       2,JSFUN_GENERIC_NATIVE),
  2966     /* Pythonic sequence methods. */
  2967     JS_FN("concat",             array_concat,       1,JSFUN_GENERIC_NATIVE),
  2968     JS_FN("slice",              array_slice,        2,JSFUN_GENERIC_NATIVE),
  2970     JS_SELF_HOSTED_FN("lastIndexOf", "ArrayLastIndexOf", 1,0),
  2971     JS_SELF_HOSTED_FN("indexOf",     "ArrayIndexOf",     1,0),
  2972     JS_SELF_HOSTED_FN("forEach",     "ArrayForEach",     1,0),
  2973     JS_SELF_HOSTED_FN("map",         "ArrayMap",         1,0),
  2974     JS_SELF_HOSTED_FN("reduce",      "ArrayReduce",      1,0),
  2975     JS_SELF_HOSTED_FN("reduceRight", "ArrayReduceRight", 1,0),
  2976     JS_FN("filter",             array_filter,       1,JSFUN_GENERIC_NATIVE),
  2977     JS_SELF_HOSTED_FN("some",        "ArraySome",        1,0),
  2978     JS_SELF_HOSTED_FN("every",       "ArrayEvery",       1,0),
  2980 #ifdef ENABLE_PARALLEL_JS
  2981     /* Parallelizable and pure methods. */
  2982     JS_SELF_HOSTED_FN("mapPar",      "ArrayMapPar",      2,0),
  2983     JS_SELF_HOSTED_FN("reducePar",   "ArrayReducePar",   2,0),
  2984     JS_SELF_HOSTED_FN("scanPar",     "ArrayScanPar",     2,0),
  2985     JS_SELF_HOSTED_FN("scatterPar",  "ArrayScatterPar",  5,0),
  2986     JS_SELF_HOSTED_FN("filterPar",   "ArrayFilterPar",   2,0),
  2987 #endif
  2989     /* ES6 additions */
  2990     JS_SELF_HOSTED_FN("find",        "ArrayFind",        1,0),
  2991     JS_SELF_HOSTED_FN("findIndex",   "ArrayFindIndex",   1,0),
  2993     JS_SELF_HOSTED_FN("fill",        "ArrayFill",        3,0),
  2995     JS_SELF_HOSTED_FN("@@iterator",  "ArrayValues",      0,0),
  2996     JS_SELF_HOSTED_FN("entries",     "ArrayEntries",     0,0),
  2997     JS_SELF_HOSTED_FN("keys",        "ArrayKeys",        0,0),
  2998     JS_FS_END
  2999 };
  3001 static const JSFunctionSpec array_static_methods[] = {
  3002     JS_FN("isArray",            array_isArray,      1,0),
  3003     JS_SELF_HOSTED_FN("lastIndexOf", "ArrayStaticLastIndexOf", 2,0),
  3004     JS_SELF_HOSTED_FN("indexOf",     "ArrayStaticIndexOf", 2,0),
  3005     JS_SELF_HOSTED_FN("forEach",     "ArrayStaticForEach", 2,0),
  3006     JS_SELF_HOSTED_FN("map",         "ArrayStaticMap",   2,0),
  3007     JS_SELF_HOSTED_FN("every",       "ArrayStaticEvery", 2,0),
  3008     JS_SELF_HOSTED_FN("some",        "ArrayStaticSome",  2,0),
  3009     JS_SELF_HOSTED_FN("reduce",      "ArrayStaticReduce", 2,0),
  3010     JS_SELF_HOSTED_FN("reduceRight", "ArrayStaticReduceRight", 2,0),
  3011     JS_FN("of",                 array_of,           0,0),
  3013 #ifdef ENABLE_PARALLEL_JS
  3014     JS_SELF_HOSTED_FN("build",       "ArrayStaticBuild", 2,0),
  3015     /* Parallelizable and pure static methods. */
  3016     JS_SELF_HOSTED_FN("buildPar",    "ArrayStaticBuildPar", 3,0),
  3017 #endif
  3019     JS_FS_END
  3020 };
  3022 /* ES5 15.4.2 */
  3023 bool
  3024 js_Array(JSContext *cx, unsigned argc, Value *vp)
  3026     CallArgs args = CallArgsFromVp(argc, vp);
  3027     RootedTypeObject type(cx, GetTypeCallerInitObject(cx, JSProto_Array));
  3028     if (!type)
  3029         return false;
  3031     if (args.length() != 1 || !args[0].isNumber())
  3032         return ArrayFromCallArgs(cx, type, args);
  3034     uint32_t length;
  3035     if (args[0].isInt32()) {
  3036         int32_t i = args[0].toInt32();
  3037         if (i < 0) {
  3038             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
  3039             return false;
  3041         length = uint32_t(i);
  3042     } else {
  3043         double d = args[0].toDouble();
  3044         length = ToUint32(d);
  3045         if (d != double(length)) {
  3046             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
  3047             return false;
  3051     /*
  3052      * Allocate dense elements eagerly for small arrays, to avoid reallocating
  3053      * elements when filling the array.
  3054      */
  3055     RootedObject obj(cx);
  3056     obj = (length <= ArrayObject::EagerAllocationMaxLength)
  3057           ? NewDenseAllocatedArray(cx, length)
  3058           : NewDenseUnallocatedArray(cx, length);
  3059     if (!obj)
  3060         return false;
  3061     Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
  3063     arr->setType(type);
  3065     /* If the length calculation overflowed, make sure that is marked for the new type. */
  3066     if (arr->length() > INT32_MAX)
  3067         arr->setLength(cx, arr->length());
  3069     args.rval().setObject(*arr);
  3070     return true;
  3073 JSObject *
  3074 js_InitArrayClass(JSContext *cx, HandleObject obj)
  3076     JS_ASSERT(obj->isNative());
  3078     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
  3080     RootedObject proto(cx, global->getOrCreateObjectPrototype(cx));
  3081     if (!proto)
  3082         return nullptr;
  3084     RootedTypeObject type(cx, cx->getNewType(&ArrayObject::class_, proto.get()));
  3085     if (!type)
  3086         return nullptr;
  3088     JSObject *metadata = nullptr;
  3089     if (!NewObjectMetadata(cx, &metadata))
  3090         return nullptr;
  3092     RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayObject::class_, TaggedProto(proto),
  3093                                                       proto->getParent(), metadata,
  3094                                                       gc::FINALIZE_OBJECT0));
  3095     if (!shape)
  3096         return nullptr;
  3098     RootedObject arrayProto(cx, JSObject::createArray(cx, gc::FINALIZE_OBJECT4, gc::TenuredHeap, shape, type, 0));
  3099     if (!arrayProto || !JSObject::setSingletonType(cx, arrayProto) || !AddLengthProperty(cx, arrayProto))
  3100         return nullptr;
  3102     RootedFunction ctor(cx);
  3103     ctor = global->createConstructor(cx, js_Array, cx->names().Array, 1);
  3104     if (!ctor)
  3105         return nullptr;
  3107     /*
  3108      * The default 'new' type of Array.prototype is required by type inference
  3109      * to have unknown properties, to simplify handling of e.g. heterogenous
  3110      * arrays in JSON and script literals and allows setDenseArrayElement to
  3111      * be used without updating the indexed type set for such default arrays.
  3112      */
  3113     if (!JSObject::setNewTypeUnknown(cx, &ArrayObject::class_, arrayProto))
  3114         return nullptr;
  3116     if (!LinkConstructorAndPrototype(cx, ctor, arrayProto))
  3117         return nullptr;
  3119     if (!DefinePropertiesAndBrand(cx, arrayProto, nullptr, array_methods) ||
  3120         !DefinePropertiesAndBrand(cx, ctor, nullptr, array_static_methods))
  3122         return nullptr;
  3125     if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Array, ctor, arrayProto))
  3126         return nullptr;
  3128     return arrayProto;
  3131 /*
  3132  * Array allocation functions.
  3133  */
  3135 static inline bool
  3136 EnsureNewArrayElements(ExclusiveContext *cx, JSObject *obj, uint32_t length)
  3138     /*
  3139      * If ensureElements creates dynamically allocated slots, then having
  3140      * fixedSlots is a waste.
  3141      */
  3142     DebugOnly<uint32_t> cap = obj->getDenseCapacity();
  3144     if (!obj->ensureElements(cx, length))
  3145         return false;
  3147     JS_ASSERT_IF(cap, !obj->hasDynamicElements());
  3149     return true;
  3152 template<bool allocateCapacity>
  3153 static MOZ_ALWAYS_INLINE ArrayObject *
  3154 NewArray(ExclusiveContext *cxArg, uint32_t length,
  3155          JSObject *protoArg, NewObjectKind newKind = GenericObject)
  3157     gc::AllocKind allocKind = GuessArrayGCKind(length);
  3158     JS_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_));
  3159     allocKind = GetBackgroundAllocKind(allocKind);
  3161     NewObjectCache::EntryIndex entry = -1;
  3162     if (JSContext *cx = cxArg->maybeJSContext()) {
  3163         NewObjectCache &cache = cx->runtime()->newObjectCache;
  3164         if (newKind == GenericObject &&
  3165             !cx->compartment()->hasObjectMetadataCallback() &&
  3166             cache.lookupGlobal(&ArrayObject::class_, cx->global(), allocKind, &entry))
  3168             gc::InitialHeap heap = GetInitialHeap(newKind, &ArrayObject::class_);
  3169             JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, heap);
  3170             if (obj) {
  3171                 /* Fixup the elements pointer and length, which may be incorrect. */
  3172                 ArrayObject *arr = &obj->as<ArrayObject>();
  3173                 arr->setFixedElements();
  3174                 arr->setLength(cx, length);
  3175                 if (allocateCapacity && !EnsureNewArrayElements(cx, arr, length))
  3176                     return nullptr;
  3177                 return arr;
  3178             } else {
  3179                 RootedObject proto(cxArg, protoArg);
  3180                 obj = cache.newObjectFromHit<CanGC>(cx, entry, heap);
  3181                 JS_ASSERT(!obj);
  3182                 protoArg = proto;
  3187     RootedObject proto(cxArg, protoArg);
  3188     if (protoArg)
  3189         JS::PoisonPtr(&protoArg);
  3191     if (!proto && !GetBuiltinPrototype(cxArg, JSProto_Array, &proto))
  3192         return nullptr;
  3194     RootedTypeObject type(cxArg, cxArg->getNewType(&ArrayObject::class_, proto.get()));
  3195     if (!type)
  3196         return nullptr;
  3198     JSObject *metadata = nullptr;
  3199     if (!NewObjectMetadata(cxArg, &metadata))
  3200         return nullptr;
  3202     /*
  3203      * Get a shape with zero fixed slots, regardless of the size class.
  3204      * See JSObject::createArray.
  3205      */
  3206     RootedShape shape(cxArg, EmptyShape::getInitialShape(cxArg, &ArrayObject::class_,
  3207                                                          TaggedProto(proto), cxArg->global(),
  3208                                                          metadata, gc::FINALIZE_OBJECT0));
  3209     if (!shape)
  3210         return nullptr;
  3212     Rooted<ArrayObject*> arr(cxArg, JSObject::createArray(cxArg, allocKind,
  3213                                                           GetInitialHeap(newKind, &ArrayObject::class_),
  3214                                                           shape, type, length));
  3215     if (!arr)
  3216         return nullptr;
  3218     if (shape->isEmptyShape()) {
  3219         if (!AddLengthProperty(cxArg, arr))
  3220             return nullptr;
  3221         shape = arr->lastProperty();
  3222         EmptyShape::insertInitialShape(cxArg, shape, proto);
  3225     if (newKind == SingletonObject && !JSObject::setSingletonType(cxArg, arr))
  3226         return nullptr;
  3228     if (entry != -1) {
  3229         cxArg->asJSContext()->runtime()->newObjectCache.fillGlobal(entry, &ArrayObject::class_,
  3230                                                                    cxArg->global(), allocKind, arr);
  3233     if (allocateCapacity && !EnsureNewArrayElements(cxArg, arr, length))
  3234         return nullptr;
  3236     probes::CreateObject(cxArg, arr);
  3237     return arr;
  3240 ArrayObject * JS_FASTCALL
  3241 js::NewDenseEmptyArray(JSContext *cx, JSObject *proto /* = nullptr */,
  3242                        NewObjectKind newKind /* = GenericObject */)
  3244     return NewArray<false>(cx, 0, proto, newKind);
  3247 ArrayObject * JS_FASTCALL
  3248 js::NewDenseAllocatedArray(ExclusiveContext *cx, uint32_t length, JSObject *proto /* = nullptr */,
  3249                            NewObjectKind newKind /* = GenericObject */)
  3251     return NewArray<true>(cx, length, proto, newKind);
  3254 ArrayObject * JS_FASTCALL
  3255 js::NewDenseUnallocatedArray(ExclusiveContext *cx, uint32_t length, JSObject *proto /* = nullptr */,
  3256                              NewObjectKind newKind /* = GenericObject */)
  3258     return NewArray<false>(cx, length, proto, newKind);
  3261 ArrayObject *
  3262 js::NewDenseCopiedArray(JSContext *cx, uint32_t length, HandleObject src, uint32_t elementOffset,
  3263                         JSObject *proto /* = nullptr */)
  3265     JS_ASSERT(!src->isIndexed());
  3267     ArrayObject* arr = NewArray<true>(cx, length, proto);
  3268     if (!arr)
  3269         return nullptr;
  3271     JS_ASSERT(arr->getDenseCapacity() >= length);
  3273     const Value* vp = src->getDenseElements() + elementOffset;
  3274     arr->setDenseInitializedLength(vp ? length : 0);
  3276     if (vp)
  3277         arr->initDenseElements(0, vp, length);
  3279     return arr;
  3282 // values must point at already-rooted Value objects
  3283 ArrayObject *
  3284 js::NewDenseCopiedArray(JSContext *cx, uint32_t length, const Value *values,
  3285                         JSObject *proto /* = nullptr */, NewObjectKind newKind /* = GenericObject */)
  3287     ArrayObject* arr = NewArray<true>(cx, length, proto);
  3288     if (!arr)
  3289         return nullptr;
  3291     JS_ASSERT(arr->getDenseCapacity() >= length);
  3293     arr->setDenseInitializedLength(values ? length : 0);
  3295     if (values)
  3296         arr->initDenseElements(0, values, length);
  3298     return arr;
  3301 ArrayObject *
  3302 js::NewDenseAllocatedArrayWithTemplate(JSContext *cx, uint32_t length, JSObject *templateObject)
  3304     gc::AllocKind allocKind = GuessArrayGCKind(length);
  3305     JS_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_));
  3306     allocKind = GetBackgroundAllocKind(allocKind);
  3308     RootedTypeObject type(cx, templateObject->type());
  3309     if (!type)
  3310         return nullptr;
  3312     RootedShape shape(cx, templateObject->lastProperty());
  3313     if (!shape)
  3314         return nullptr;
  3316     gc::InitialHeap heap = GetInitialHeap(GenericObject, &ArrayObject::class_);
  3317     Rooted<ArrayObject *> arr(cx, JSObject::createArray(cx, allocKind, heap, shape, type, length));
  3318     if (!arr)
  3319         return nullptr;
  3321     if (!EnsureNewArrayElements(cx, arr, length))
  3322         return nullptr;
  3324     probes::CreateObject(cx, arr);
  3326     return arr;
  3329 #ifdef DEBUG
  3330 bool
  3331 js_ArrayInfo(JSContext *cx, unsigned argc, Value *vp)
  3333     CallArgs args = CallArgsFromVp(argc, vp);
  3334     JSObject *obj;
  3336     for (unsigned i = 0; i < args.length(); i++) {
  3337         RootedValue arg(cx, args[i]);
  3339         char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, arg, NullPtr());
  3340         if (!bytes)
  3341             return false;
  3342         if (arg.isPrimitive() ||
  3343             !(obj = arg.toObjectOrNull())->is<ArrayObject>()) {
  3344             fprintf(stderr, "%s: not array\n", bytes);
  3345             js_free(bytes);
  3346             continue;
  3348         fprintf(stderr, "%s: (len %u", bytes, obj->as<ArrayObject>().length());
  3349         fprintf(stderr, ", capacity %u", obj->getDenseCapacity());
  3350         fputs(")\n", stderr);
  3351         js_free(bytes);
  3354     args.rval().setUndefined();
  3355     return true;
  3357 #endif

mercurial