LCOV - app.info - js/src/jsarray.cpp

LCOV - code coverage report
| Current view: | directory - js/src - jsarray.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 1815 | 1269 | 69.9 %
| Date: | 2012-04-07 | Functions: | 144 | 115 | 79.9 %

           1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
           2                 :  * vim: set sw=4 ts=8 et tw=78:
           3                 :  *
           4                 :  * ***** BEGIN LICENSE BLOCK *****
           5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
           6                 :  *
           7                 :  * The contents of this file are subject to the Mozilla Public License Version
           8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
           9                 :  * the License. You may obtain a copy of the License at
          10                 :  * http://www.mozilla.org/MPL/
          11                 :  *
          12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
          13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
          14                 :  * for the specific language governing rights and limitations under the
          15                 :  * License.
          16                 :  *
          17                 :  * The Original Code is Mozilla Communicator client code, released
          18                 :  * March 31, 1998.
          19                 :  *
          20                 :  * The Initial Developer of the Original Code is
          21                 :  * Netscape Communications Corporation.
          22                 :  * Portions created by the Initial Developer are Copyright (C) 1998
          23                 :  * the Initial Developer. All Rights Reserved.
          24                 :  *
          25                 :  * Contributor(s):
          26                 :  *
          27                 :  * Alternatively, the contents of this file may be used under the terms of
          28                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
          29                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
          30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
          31                 :  * of those above. If you wish to allow use of your version of this file only
          32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
          33                 :  * use your version of this file under the terms of the MPL, indicate your
          34                 :  * decision by deleting the provisions above and replace them with the notice
          35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
          36                 :  * the provisions above, a recipient may use your version of this file under
          37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
          38                 :  *
          39                 :  * ***** END LICENSE BLOCK ***** */
          40                 :
          41                 : /*
          42                 :  * JS array class.
          43                 :  *
          44                 :  * Array objects begin as "dense" arrays, optimized for index-only property
          45                 :  * access over a vector of slots with high load factor.  Array methods
          46                 :  * optimize for denseness by testing that the object's class is
          47                 :  * &ArrayClass, and can then directly manipulate the slots for efficiency.
          48                 :  *
          49                 :  * We track these pieces of metadata for arrays in dense mode:
          50                 :  *  - The array's length property as a uint32, accessible with
          51                 :  *    getArrayLength(), setArrayLength().
          52                 :  *  - The number of element slots (capacity), gettable with
          53                 :  *    getDenseArrayCapacity().
          54                 :  *  - The array's initialized length, accessible with
          55                 :  *    getDenseArrayInitializedLength().
          56                 :  *
          57                 :  * In dense mode, holes in the array are represented by
          58                 :  * MagicValue(JS_ARRAY_HOLE) invalid values.
          59                 :  *
          60                 :  * NB: the capacity and length of a dense array are entirely unrelated!  The
          61                 :  * length may be greater than, less than, or equal to the capacity. The first
          62                 :  * case may occur when the user writes "new Array(100)", in which case the
          63                 :  * length is 100 while the capacity remains 0 (indices below length and above
          64                 :  * capacity must be treated as holes). See array_length_setter for another
          65                 :  * explanation of how the first case may occur.
          66                 :  *
          67                 :  * The initialized length of a dense array specifies the number of elements
          68                 :  * that have been initialized. All elements above the initialized length are
          69                 :  * holes in the array, and the memory for all elements between the initialized
          70                 :  * length and capacity is left uninitialized. When type inference is disabled,
          71                 :  * the initialized length always equals the array's capacity. When inference is
          72                 :  * enabled, the initialized length is some value less than or equal to both the
          73                 :  * array's length and the array's capacity.
          74                 :  *
          75                 :  * With inference enabled, there is flexibility in exactly the value the
          76                 :  * initialized length must hold, e.g. if an array has length 5, capacity 10,
          77                 :  * completely empty, it is valid for the initialized length to be any value
          78                 :  * between zero and 5, as long as the in memory values below the initialized
          79                 :  * length have been initialized with a hole value. However, in such cases we
          80                 :  * want to keep the initialized length as small as possible: if the array is
          81                 :  * known to have no hole values below its initialized length, then it is a
          82                 :  * "packed" array and can be accessed much faster by JIT code.
          83                 :  *
          84                 :  * Arrays are converted to use SlowArrayClass when any of these conditions
          85                 :  * are met:
          86                 :  *  - there are more than MIN_SPARSE_INDEX slots total and the load factor
          87                 :  *    (COUNT / capacity) is less than 0.25
          88                 :  *  - a property is set that is not indexed (and not "length")
          89                 :  *  - a property is defined that has non-default property attributes.
          90                 :  *
          91                 :  * Dense arrays do not track property creation order, so unlike other native
          92                 :  * objects and slow arrays, enumerating an array does not necessarily visit the
          93                 :  * properties in the order they were created.  We could instead maintain the
          94                 :  * scope to track property enumeration order, but still use the fast slot
          95                 :  * access.  That would have the same memory cost as just using a
          96                 :  * SlowArrayClass, but have the same performance characteristics as a dense
          97                 :  * array for slot accesses, at some cost in code complexity.
          98                 :  */
          99                 : #include <limits.h>
         100                 : #include <stdlib.h>
         101                 : #include <string.h>
         102                 :
         103                 : #include "mozilla/RangedPtr.h"
         104                 :
         105                 : #include "jstypes.h"
         106                 : #include "jsutil.h"
         107                 :
         108                 : #include "jsapi.h"
         109                 : #include "jsarray.h"
         110                 : #include "jsatom.h"
         111                 : #include "jsbool.h"
         112                 : #include "jscntxt.h"
         113                 : #include "jsversion.h"
         114                 : #include "jsfun.h"
         115                 : #include "jsgc.h"
         116                 : #include "jsgcmark.h"
         117                 : #include "jsinterp.h"
         118                 : #include "jsiter.h"
         119                 : #include "jslock.h"
         120                 : #include "jsnum.h"
         121                 : #include "jsobj.h"
         122                 : #include "jsscope.h"
         123                 : #include "jswrapper.h"
         124                 : #include "methodjit/MethodJIT.h"
         125                 : #include "methodjit/StubCalls.h"
         126                 : #include "methodjit/StubCalls-inl.h"
         127                 :
         128                 : #include "vm/ArgumentsObject.h"
         129                 : #include "vm/MethodGuard.h"
         130                 : #include "vm/StringBuffer.h"
         131                 :
         132                 : #include "ds/Sort.h"
         133                 :
         134                 : #include "jsarrayinlines.h"
         135                 : #include "jsatominlines.h"
         136                 : #include "jscntxtinlines.h"
         137                 : #include "jsobjinlines.h"
         138                 : #include "jsscopeinlines.h"
         139                 : #include "jsstrinlines.h"
         140                 :
         141                 : #include "vm/ArgumentsObject-inl.h"
         142                 : #include "vm/ObjectImpl-inl.h"
         143                 : #include "vm/Stack-inl.h"
         144                 :
         145                 : using namespace mozilla;
         146                 : using namespace js;
         147                 : using namespace js::gc;
         148                 : using namespace js::types;
         149                 :
         150                 : JSBool
         151          607379 : js_GetLengthProperty(JSContext *cx, JSObject *obj, uint32_t *lengthp)
         152                 : {
         153          607379 :     if (obj->isArray()) {
         154          392534 :         *lengthp = obj->getArrayLength();
         155          392534 :         return true;
         156                 :     }
         157                 :
         158          214845 :     if (obj->isArguments()) {
         159          214323 :         ArgumentsObject &argsobj = obj->asArguments();
         160          214323 :         if (!argsobj.hasOverriddenLength()) {
         161          213819 :             *lengthp = argsobj.initialLength();
         162          213819 :             return true;
         163                 :         }
         164                 :     }
         165                 :
         166            2052 :     AutoValueRooter tvr(cx);
         167            1026 :     if (!obj->getProperty(cx, cx->runtime->atomState.lengthAtom, tvr.addr()))
         168              18 :         return false;
         169                 :
         170            1008 :     if (tvr.value().isInt32()) {
         171             972 :         *lengthp = uint32_t(tvr.value().toInt32()); /* uint32_t cast does ToUint32_t */
         172             972 :         return true;
         173                 :     }
         174                 :
         175                 :
         176              36 :     return ToUint32(cx, tvr.value(), (uint32_t *)lengthp);
         177                 : }
         178                 :
         179                 : namespace js {
         180                 :
         181                 : /*
         182                 :  * Determine if the id represents an array index or an XML property index.
         183                 :  *
         184                 :  * An id is an array index according to ECMA by (15.4):
         185                 :  *
         186                 :  * "Array objects give special treatment to a certain class of property names.
         187                 :  * A property name P (in the form of a string value) is an array index if and
         188                 :  * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
         189                 :  * to 2^32-1."
         190                 :  *
         191                 :  * This means the largest allowed index is actually 2^32-2 (4294967294).
         192                 :  *
         193                 :  * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id)
         194                 :  * except that by using signed 31-bit integers we miss the top half of the
         195                 :  * valid range. This function checks the string representation itself; note
         196                 :  * that calling a standard conversion routine might allow strings such as
         197                 :  * "08" or "4.0" as array indices, which they are not.
         198                 :  *
         199                 :  */
         200                 : JS_FRIEND_API(bool)
         201        31942868 : StringIsArrayIndex(JSLinearString *str, uint32_t *indexp)
         202                 : {
         203        31942868 :     const jschar *s = str->chars();
         204        31942868 :     uint32_t length = str->length();
         205        31942868 :     const jschar *end = s + length;
         206                 :
         207        31942868 :     if (length == 0 || length > (sizeof("4294967294") - 1) || !JS7_ISDEC(*s))
         208        31827944 :         return false;
         209                 :
         210          114924 :     uint32_t c = 0, previous = 0;
         211          114924 :     uint32_t index = JS7_UNDEC(*s++);
         212                 :
         213                 :     /* Don't allow leading zeros. */
         214          114924 :     if (index == 0 && s != end)
         215              45 :         return false;
         216                 :
         217          266349 :     for (; s < end; s++) {
         218          203376 :         if (!JS7_ISDEC(*s))
         219           51906 :             return false;
         220                 :
         221          151470 :         previous = index;
         222          151470 :         c = JS7_UNDEC(*s);
         223          151470 :         index = 10 * index + c;
         224                 :     }
         225                 :
         226                 :     /* Make sure we didn't overflow. */
         227           62973 :     if (previous < (MAX_ARRAY_INDEX / 10) || (previous == (MAX_ARRAY_INDEX / 10) &&
         228                 :         c <= (MAX_ARRAY_INDEX % 10))) {
         229           62932 :         JS_ASSERT(index <= MAX_ARRAY_INDEX);
         230           62932 :         *indexp = index;
         231           62932 :         return true;
         232                 :     }
         233                 :
         234              41 :     return false;
         235                 : }
         236                 :
         237                 : }
         238                 :
         239                 : static JSBool
         240               0 : BigIndexToId(JSContext *cx, JSObject *obj, uint32_t index, JSBool createAtom,
         241                 :              jsid *idp)
         242                 : {
         243                 :
         244               0 :     JS_ASSERT(index > JSID_INT_MAX);
         245                 :
         246                 :     jschar buf[10];
         247               0 :     jschar *start = ArrayEnd(buf);
         248               0 :     do {
         249               0 :         --start;
         250               0 :         *start = (jschar)('0' + index % 10);
         251               0 :         index /= 10;
         252                 :     } while (index != 0);
         253                 :
         254                 :     /*
         255                 :      * Skip the atomization if the class is known to store atoms corresponding
         256                 :      * to big indexes together with elements. In such case we know that the
         257                 :      * array does not have an element at the given index if its atom does not
         258                 :      * exist.  Dense arrays don't use atoms for any indexes, though it would be
         259                 :      * rare to see them have a big index in any case.
         260                 :      */
         261                 :     JSAtom *atom;
         262               0 :     if (!createAtom && (obj->isSlowArray() || obj->isArguments() || obj->isObject())) {
         263               0 :         atom = js_GetExistingStringAtom(cx, start, ArrayEnd(buf) - start);
         264               0 :         if (!atom) {
         265               0 :             *idp = JSID_VOID;
         266               0 :             return JS_TRUE;
         267                 :         }
         268                 :     } else {
         269               0 :         atom = js_AtomizeChars(cx, start, ArrayEnd(buf) - start);
         270               0 :         if (!atom)
         271               0 :             return JS_FALSE;
         272                 :     }
         273                 :
         274               0 :     *idp = ATOM_TO_JSID(atom);
         275               0 :     return JS_TRUE;
         276                 : }
         277                 :
         278                 : bool
         279            4866 : JSObject::willBeSparseDenseArray(unsigned requiredCapacity, unsigned newElementsHint)
         280                 : {
         281            4866 :     JS_ASSERT(isDenseArray());
         282            4866 :     JS_ASSERT(requiredCapacity > MIN_SPARSE_INDEX);
         283                 :
         284            4866 :     unsigned cap = getDenseArrayCapacity();
         285            4866 :     JS_ASSERT(requiredCapacity >= cap);
         286                 :
         287            4866 :     if (requiredCapacity >= JSObject::NELEMENTS_LIMIT)
         288               9 :         return true;
         289                 :
         290            4857 :     unsigned minimalDenseCount = requiredCapacity / 4;
         291            4857 :     if (newElementsHint >= minimalDenseCount)
         292               0 :         return false;
         293            4857 :     minimalDenseCount -= newElementsHint;
         294                 :
         295            4857 :     if (minimalDenseCount > cap)
         296            3060 :         return true;
         297                 :
         298            1797 :     unsigned len = getDenseArrayInitializedLength();
         299            1797 :     const Value *elems = getDenseArrayElements();
         300         4132033 :     for (unsigned i = 0; i < len; i++) {
         301         4132033 :         if (!elems[i].isMagic(JS_ARRAY_HOLE) && !--minimalDenseCount)
         302            1797 :             return false;
         303                 :     }
         304               0 :     return true;
         305                 : }
         306                 :
         307                 : static bool
         308               0 : ReallyBigIndexToId(JSContext* cx, double index, jsid* idp)
         309                 : {
         310               0 :     return js_ValueToStringId(cx, DoubleValue(index), idp);
         311                 : }
         312                 :
         313                 : static bool
         314          209920 : IndexToId(JSContext* cx, JSObject* obj, double index, JSBool* hole, jsid* idp,
         315                 :           JSBool createAtom = JS_FALSE)
         316                 : {
         317          209920 :     if (index <= JSID_INT_MAX) {
         318          209920 :         *idp = INT_TO_JSID(int(index));
         319          209920 :         return JS_TRUE;
         320                 :     }
         321                 :
         322               0 :     if (index <= uint32_t(-1)) {
         323               0 :         if (!BigIndexToId(cx, obj, uint32_t(index), createAtom, idp))
         324               0 :             return JS_FALSE;
         325               0 :         if (hole && JSID_IS_VOID(*idp))
         326               0 :             *hole = JS_TRUE;
         327               0 :         return JS_TRUE;
         328                 :     }
         329                 :
         330               0 :     return ReallyBigIndexToId(cx, index, idp);
         331                 : }
         332                 :
         333                 : bool
         334             360 : JSObject::arrayGetOwnDataElement(JSContext *cx, size_t i, Value *vp)
         335                 : {
         336             360 :     JS_ASSERT(isArray());
         337                 :
         338             360 :     if (isDenseArray()) {
         339             360 :         if (i >= getArrayLength())
         340             288 :             vp->setMagic(JS_ARRAY_HOLE);
         341                 :         else
         342              72 :             *vp = getDenseArrayElement(uint32_t(i));
         343             360 :         return true;
         344                 :     }
         345                 :
         346                 :     JSBool hole;
         347                 :     jsid id;
         348               0 :     if (!IndexToId(cx, this, i, &hole, &id))
         349               0 :         return false;
         350                 :
         351               0 :     const Shape *shape = nativeLookup(cx, id);
         352               0 :     if (!shape || !shape->isDataDescriptor())
         353               0 :         vp->setMagic(JS_ARRAY_HOLE);
         354                 :     else
         355               0 :         *vp = getSlot(shape->slot());
         356               0 :     return true;
         357                 : }
         358                 :
         359                 : /*
         360                 :  * If the property at the given index exists, get its value into location
         361                 :  * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp
         362                 :  * to JSVAL_VOID. This function assumes that the location pointed by vp is
         363                 :  * properly rooted and can be used as GC-protected storage for temporaries.
         364                 :  */
         365                 : static inline JSBool
         366          205933 : DoGetElement(JSContext *cx, JSObject *obj, double index, JSBool *hole, Value *vp)
         367                 : {
         368          411866 :     AutoIdRooter idr(cx);
         369                 :
         370          205933 :     *hole = JS_FALSE;
         371          205933 :     if (!IndexToId(cx, obj, index, hole, idr.addr()))
         372               0 :         return JS_FALSE;
         373          205933 :     if (*hole) {
         374               0 :         vp->setUndefined();
         375               0 :         return JS_TRUE;
         376                 :     }
         377                 :
         378                 :     JSObject *obj2;
         379                 :     JSProperty *prop;
         380          205933 :     if (!obj->lookupGeneric(cx, idr.id(), &obj2, &prop))
         381               9 :         return JS_FALSE;
         382          205924 :     if (!prop) {
         383          205447 :         vp->setUndefined();
         384          205447 :         *hole = JS_TRUE;
         385                 :     } else {
         386             477 :         if (!obj->getGeneric(cx, idr.id(), vp))
         387               9 :             return JS_FALSE;
         388             468 :         *hole = JS_FALSE;
         389                 :     }
         390          205915 :     return JS_TRUE;
         391                 : }
         392                 :
         393                 : static inline JSBool
         394          505294 : DoGetElement(JSContext *cx, JSObject *obj, uint32_t index, JSBool *hole, Value *vp)
         395                 : {
         396                 :     bool present;
         397          505294 :     if (!obj->getElementIfPresent(cx, obj, index, vp, &present))
         398              36 :         return false;
         399                 :
         400          505258 :     *hole = !present;
         401          505258 :     if (*hole)
         402          503143 :         vp->setUndefined();
         403                 :
         404          505258 :     return true;
         405                 : }
         406                 :
         407                 : template<typename IndexType>
         408                 : static void
         409          206320 : AssertGreaterThanZero(IndexType index)
         410                 : {
         411          206320 :     JS_ASSERT(index >= 0);
         412          206320 : }
         413                 :
         414                 : template<>
         415                 : void
         416         1365978 : AssertGreaterThanZero(uint32_t index)
         417                 : {
         418         1365978 : }
         419                 :
         420                 : template<typename IndexType>
         421                 : static JSBool
         422         1572298 : GetElement(JSContext *cx, JSObject *obj, IndexType index, JSBool *hole, Value *vp)
         423                 : {
         424         1572298 :     AssertGreaterThanZero(index);
         425         1572298 :     if (obj->isDenseArray() && index < obj->getDenseArrayInitializedLength() &&
         426                 :         !(*vp = obj->getDenseArrayElement(uint32_t(index))).isMagic(JS_ARRAY_HOLE)) {
         427          859550 :         *hole = JS_FALSE;
         428          859550 :         return JS_TRUE;
         429                 :     }
         430          712748 :     if (obj->isArguments()) {
         431            1521 :         if (obj->asArguments().getElement(uint32_t(index), vp)) {
         432            1521 :             *hole = JS_FALSE;
         433            1521 :             return true;
         434                 :         }
         435                 :     }
         436                 :
         437          711227 :     return DoGetElement(cx, obj, index, hole, vp);
         438                 : }
         439                 :
         440                 : namespace js {
         441                 :
         442                 : static bool
         443            4190 : GetElementsSlow(JSContext *cx, JSObject *aobj, uint32_t length, Value *vp)
         444                 : {
         445          366310 :     for (uint32_t i = 0; i < length; i++) {
         446          362120 :         if (!aobj->getElement(cx, i, &vp[i]))
         447               0 :             return false;
         448                 :     }
         449                 :
         450            4190 :     return true;
         451                 : }
         452                 :
         453                 : bool
         454          394688 : GetElements(JSContext *cx, JSObject *aobj, uint32_t length, Value *vp)
         455                 : {
         456          572366 :     if (aobj->isDenseArray() && length <= aobj->getDenseArrayInitializedLength() &&
         457          177678 :         !js_PrototypeHasIndexedProperties(cx, aobj)) {
         458                 :         /* The prototype does not have indexed properties so hole = undefined */
         459          177588 :         const Value *srcbeg = aobj->getDenseArrayElements();
         460          177588 :         const Value *srcend = srcbeg + length;
         461          177588 :         const Value *src = srcbeg;
         462          527310 :         for (Value *dst = vp; src < srcend; ++dst, ++src)
         463          349722 :             *dst = src->isMagic(JS_ARRAY_HOLE) ? UndefinedValue() : *src;
         464          177588 :         return true;
         465                 :     }
         466                 :
         467          217100 :     if (aobj->isArguments()) {
         468          213396 :         ArgumentsObject &argsobj = aobj->asArguments();
         469          213396 :         if (!argsobj.hasOverriddenLength()) {
         470          212955 :             if (argsobj.getElements(0, length, vp))
         471          212910 :                 return true;
         472                 :         }
         473                 :     }
         474                 :
         475            4190 :     return GetElementsSlow(cx, aobj, length, vp);
         476                 : }
         477                 :
         478                 : }
         479                 :
         480                 : /*
         481                 :  * Set the value of the property at the given index to v assuming v is rooted.
         482                 :  */
         483                 : static JSBool
         484           14889 : SetArrayElement(JSContext *cx, JSObject *obj, double index, const Value &v)
         485                 : {
         486           14889 :     JS_ASSERT(index >= 0);
         487                 :
         488           14889 :     if (obj->isDenseArray()) {
         489                 :         /* Predicted/prefetched code should favor the remains-dense case. */
         490           11244 :         JSObject::EnsureDenseResult result = JSObject::ED_SPARSE;
         491                 :         do {
         492           11244 :             if (index > uint32_t(-1))
         493               0 :                 break;
         494           11244 :             uint32_t idx = uint32_t(index);
         495           11244 :             result = obj->ensureDenseArrayElements(cx, idx, 1);
         496           11244 :             if (result != JSObject::ED_OK)
         497             342 :                 break;
         498           10902 :             if (idx >= obj->getArrayLength())
         499            4134 :                 obj->setDenseArrayLength(idx + 1);
         500           10902 :             obj->setDenseArrayElementWithType(cx, idx, v);
         501           10902 :             return true;
         502                 :         } while (false);
         503                 :
         504             342 :         if (result == JSObject::ED_FAILED)
         505               0 :             return false;
         506             342 :         JS_ASSERT(result == JSObject::ED_SPARSE);
         507             342 :         if (!obj->makeDenseArraySlow(cx))
         508               0 :             return JS_FALSE;
         509                 :     }
         510                 :
         511            7974 :     AutoIdRooter idr(cx);
         512                 :
         513            3987 :     if (!IndexToId(cx, obj, index, NULL, idr.addr(), JS_TRUE))
         514               0 :         return JS_FALSE;
         515            3987 :     JS_ASSERT(!JSID_IS_VOID(idr.id()));
         516                 :
         517            3987 :     Value tmp = v;
         518            3987 :     return obj->setGeneric(cx, idr.id(), &tmp, true);
         519                 : }
         520                 :
         521                 : /*
         522                 :  * Delete the element |index| from |obj|. If |strict|, do a strict
         523                 :  * deletion: throw if the property is not configurable.
         524                 :  *
         525                 :  * - Return 1 if the deletion succeeds (that is, ES5's [[Delete]] would
         526                 :  *   return true)
         527                 :  *
         528                 :  * - Return 0 if the deletion fails because the property is not
         529                 :  *   configurable (that is, [[Delete]] would return false). Note that if
         530                 :  *   |strict| is true we will throw, not return zero.
         531                 :  *
         532                 :  * - Return -1 if an exception occurs (that is, [[Delete]] would throw).
         533                 :  */
         534                 : static int
         535          305966 : DeleteArrayElement(JSContext *cx, JSObject *obj, double index, bool strict)
         536                 : {
         537          305966 :     JS_ASSERT(index >= 0);
         538          305966 :     JS_ASSERT(floor(index) == index);
         539                 :
         540          305966 :     if (obj->isDenseArray()) {
         541          113033 :         if (index <= UINT32_MAX) {
         542          113033 :             uint32_t idx = uint32_t(index);
         543          113033 :             if (idx < obj->getDenseArrayInitializedLength()) {
         544           16854 :                 obj->markDenseArrayNotPacked(cx);
         545           16854 :                 obj->setDenseArrayElement(idx, MagicValue(JS_ARRAY_HOLE));
         546           16854 :                 if (!js_SuppressDeletedElement(cx, obj, idx))
         547               0 :                     return -1;
         548                 :             }
         549                 :         }
         550          113033 :         return 1;
         551                 :     }
         552                 :
         553                 :     Value v;
         554          192933 :     if (index <= UINT32_MAX) {
         555          192933 :         if (!obj->deleteElement(cx, uint32_t(index), &v, strict))
         556              36 :             return -1;
         557                 :     } else {
         558               0 :         if (!obj->deleteByValue(cx, DoubleValue(index), &v, strict))
         559               0 :             return -1;
         560                 :     }
         561                 :
         562          192897 :     return v.isTrue() ? 1 : 0;
         563                 : }
         564                 :
         565                 : /*
         566                 :  * When hole is true, delete the property at the given index. Otherwise set
         567                 :  * its value to v assuming v is rooted.
         568                 :  */
         569                 : static JSBool
         570          300820 : SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, double index,
         571                 :                         JSBool hole, const Value &v)
         572                 : {
         573          300820 :     if (hole) {
         574          297832 :         JS_ASSERT(v.isUndefined());
         575          297832 :         return DeleteArrayElement(cx, obj, index, true) >= 0;
         576                 :     }
         577            2988 :     return SetArrayElement(cx, obj, index, v);
         578                 : }
         579                 :
         580                 : JSBool
         581           13044 : js_SetLengthProperty(JSContext *cx, JSObject *obj, double length)
         582                 : {
         583           13044 :     Value v = NumberValue(length);
         584                 :
         585                 :     /* We don't support read-only array length yet. */
         586           13044 :     return obj->setProperty(cx, cx->runtime->atomState.lengthAtom, &v, false);
         587                 : }
         588                 :
         589                 : /*
         590                 :  * Since SpiderMonkey supports cross-class prototype-based delegation, we have
         591                 :  * to be careful about the length getter and setter being called on an object
         592                 :  * not of Array class. For the getter, we search obj's prototype chain for the
         593                 :  * array that caused this getter to be invoked. In the setter case to overcome
         594                 :  * the JSPROP_SHARED attribute, we must define a shadowing length property.
         595                 :  */
         596                 : static JSBool
         597             171 : array_length_getter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
         598                 : {
         599               0 :     do {
         600             171 :         if (obj->isArray()) {
         601             171 :             vp->setNumber(obj->getArrayLength());
         602             171 :             return JS_TRUE;
         603                 :         }
         604               0 :     } while ((obj = obj->getProto()) != NULL);
         605               0 :     return JS_TRUE;
         606                 : }
         607                 :
         608                 : static JSBool
         609           15006 : array_length_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
         610                 : {
         611           15006 :     if (!obj->isArray()) {
         612                 :         return obj->defineProperty(cx, cx->runtime->atomState.lengthAtom, *vp,
         613               0 :                                    NULL, NULL, JSPROP_ENUMERATE);
         614                 :     }
         615                 :
         616                 :     uint32_t newlen;
         617           15006 :     if (!ToUint32(cx, *vp, &newlen))
         618               0 :         return false;
         619                 :
         620                 :     double d;
         621           15006 :     if (!ToNumber(cx, *vp, &d))
         622               0 :         return false;
         623                 :
         624           15006 :     if (d != newlen) {
         625               0 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
         626               0 :         return false;
         627                 :     }
         628                 :
         629           15006 :     uint32_t oldlen = obj->getArrayLength();
         630           15006 :     if (oldlen == newlen)
         631           11910 :         return true;
         632                 :
         633            3096 :     vp->setNumber(newlen);
         634            3096 :     if (oldlen < newlen) {
         635            2871 :         obj->setArrayLength(cx, newlen);
         636            2871 :         return true;
         637                 :     }
         638                 :
         639             225 :     if (obj->isDenseArray()) {
         640                 :         /*
         641                 :          * Don't reallocate if we're not actually shrinking our slots. If we do
         642                 :          * shrink slots here, shrink the initialized length too.  This permits us
         643                 :          * us to disregard length when reading from arrays as long we are within
         644                 :          * the initialized capacity.
         645                 :          */
         646             189 :         uint32_t oldcap = obj->getDenseArrayCapacity();
         647             189 :         uint32_t oldinit = obj->getDenseArrayInitializedLength();
         648             189 :         if (oldinit > newlen)
         649             123 :             obj->setDenseArrayInitializedLength(newlen);
         650             189 :         if (oldcap > newlen)
         651             175 :             obj->shrinkElements(cx, newlen);
         652              36 :     } else if (oldlen - newlen < (1 << 24)) {
         653              36 :         do {
         654              36 :             --oldlen;
         655              36 :             if (!JS_CHECK_OPERATION_LIMIT(cx)) {
         656               0 :                 obj->setArrayLength(cx, oldlen + 1);
         657               0 :                 return false;
         658                 :             }
         659              36 :             int deletion = DeleteArrayElement(cx, obj, oldlen, strict);
         660              36 :             if (deletion <= 0) {
         661               0 :                 obj->setArrayLength(cx, oldlen + 1);
         662               0 :                 return deletion >= 0;
         663                 :             }
         664                 :         } while (oldlen != newlen);
         665                 :     } else {
         666                 :         /*
         667                 :          * We are going to remove a lot of indexes in a presumably sparse
         668                 :          * array. So instead of looping through indexes between newlen and
         669                 :          * oldlen, we iterate through all properties and remove those that
         670                 :          * correspond to indexes in the half-open range [newlen, oldlen).  See
         671                 :          * bug 322135.
         672                 :          */
         673               0 :         JSObject *iter = JS_NewPropertyIterator(cx, obj);
         674               0 :         if (!iter)
         675               0 :             return false;
         676                 :
         677               0 :         uint32_t gap = oldlen - newlen;
         678               0 :         for (;;) {
         679               0 :             if (!JS_CHECK_OPERATION_LIMIT(cx) || !JS_NextProperty(cx, iter, &id))
         680               0 :                 return false;
         681               0 :             if (JSID_IS_VOID(id))
         682               0 :                 break;
         683                 :             uint32_t index;
         684                 :             Value junk;
         685               0 :             if (js_IdIsIndex(id, &index) && index - newlen < gap &&
         686               0 :                 !obj->deleteElement(cx, index, &junk, false)) {
         687               0 :                 return false;
         688                 :             }
         689                 :         }
         690                 :     }
         691                 :
         692             225 :     obj->setArrayLength(cx, newlen);
         693             225 :     return true;
         694                 : }
         695                 :
         696                 : /* Returns true if the dense array has an own property at the index. */
         697                 : static inline bool
         698          706838 : IsDenseArrayIndex(JSObject *obj, uint32_t index)
         699                 : {
         700          706838 :     JS_ASSERT(obj->isDenseArray());
         701                 :
         702          706838 :     return index < obj->getDenseArrayInitializedLength() &&
         703          706838 :            !obj->getDenseArrayElement(index).isMagic(JS_ARRAY_HOLE);
         704                 : }
         705                 :
         706                 : /*
         707                 :  * We have only indexed properties up to initialized length, plus the
         708                 :  * length property. For all else, we delegate to the prototype.
         709                 :  */
         710                 : static inline bool
         711          709407 : IsDenseArrayId(JSContext *cx, JSObject *obj, jsid id)
         712                 : {
         713          709407 :     JS_ASSERT(obj->isDenseArray());
         714                 :
         715                 :     uint32_t i;
         716          709407 :     return JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom) ||
         717          709407 :            (js_IdIsIndex(id, &i) && IsDenseArrayIndex(obj, i));
         718                 : }
         719                 :
         720                 : static JSBool
         721          709407 : array_lookupGeneric(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
         722                 :                     JSProperty **propp)
         723                 : {
         724          709407 :     if (!obj->isDenseArray())
         725               0 :         return js_LookupProperty(cx, obj, id, objp, propp);
         726                 :
         727          709407 :     if (IsDenseArrayId(cx, obj, id)) {
         728           24545 :         *propp = (JSProperty *) 1;  /* non-null to indicate found */
         729           24545 :         *objp = obj;
         730           24545 :         return JS_TRUE;
         731                 :     }
         732                 :
         733          684862 :     JSObject *proto = obj->getProto();
         734          684862 :     if (!proto) {
         735               0 :         *objp = NULL;
         736               0 :         *propp = NULL;
         737               0 :         return JS_TRUE;
         738                 :     }
         739          684862 :     return proto->lookupGeneric(cx, id, objp, propp);
         740                 : }
         741                 :
         742                 : static JSBool
         743               0 : array_lookupProperty(JSContext *cx, JSObject *obj, PropertyName *name, JSObject **objp,
         744                 :                      JSProperty **propp)
         745                 : {
         746               0 :     return array_lookupGeneric(cx, obj, ATOM_TO_JSID(name), objp, propp);
         747                 : }
         748                 :
         749                 : static JSBool
         750               0 : array_lookupElement(JSContext *cx, JSObject *obj, uint32_t index, JSObject **objp,
         751                 :                     JSProperty **propp)
         752                 : {
         753               0 :     if (!obj->isDenseArray())
         754               0 :         return js_LookupElement(cx, obj, index, objp, propp);
         755                 :
         756               0 :     if (IsDenseArrayIndex(obj, index)) {
         757               0 :         *propp = (JSProperty *) 1;  /* non-null to indicate found */
         758               0 :         *objp = obj;
         759               0 :         return true;
         760                 :     }
         761                 :
         762               0 :     if (JSObject *proto = obj->getProto())
         763               0 :         return proto->lookupElement(cx, index, objp, propp);
         764                 :
         765               0 :     *objp = NULL;
         766               0 :     *propp = NULL;
         767               0 :     return true;
         768                 : }
         769                 :
         770                 : static JSBool
         771               0 : array_lookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid, JSObject **objp,
         772                 :                     JSProperty **propp)
         773                 : {
         774               0 :     return array_lookupGeneric(cx, obj, SPECIALID_TO_JSID(sid), objp, propp);
         775                 : }
         776                 :
         777                 : JSBool
         778               0 : js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, jsid id, Value *vp)
         779                 : {
         780               0 :     JS_ASSERT(obj->isDenseArray());
         781                 :
         782                 :     uint32_t i;
         783               0 :     if (!js_IdIsIndex(id, &i)) {
         784               0 :         JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
         785               0 :         vp->setNumber(obj->getArrayLength());
         786               0 :         return JS_TRUE;
         787                 :     }
         788               0 :     *vp = obj->getDenseArrayElement(i);
         789               0 :     return JS_TRUE;
         790                 : }
         791                 :
         792                 : static JSBool
         793         2379714 : array_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name, Value *vp)
         794                 : {
         795         2379714 :     if (name == cx->runtime->atomState.lengthAtom) {
         796             812 :         vp->setNumber(obj->getArrayLength());
         797             812 :         return true;
         798                 :     }
         799                 :
         800         2378902 :     if (name == cx->runtime->atomState.protoAtom) {
         801              72 :         vp->setObjectOrNull(obj->getProto());
         802              72 :         return true;
         803                 :     }
         804                 :
         805         2378830 :     if (!obj->isDenseArray())
         806               0 :         return js_GetProperty(cx, obj, receiver, ATOM_TO_JSID(name), vp);
         807                 :
         808         2378830 :     JSObject *proto = obj->getProto();
         809         2378830 :     if (!proto) {
         810               0 :         vp->setUndefined();
         811               0 :         return true;
         812                 :     }
         813                 :
         814         2378830 :     return proto->getProperty(cx, receiver, name, vp);
         815                 : }
         816                 :
         817                 : static JSBool
         818          695873 : array_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp)
         819                 : {
         820          695873 :     if (!obj->isDenseArray())
         821               0 :         return js_GetElement(cx, obj, receiver, index, vp);
         822                 :
         823          695873 :     if (index < obj->getDenseArrayInitializedLength()) {
         824          203288 :         *vp = obj->getDenseArrayElement(index);
         825          203288 :         if (!vp->isMagic(JS_ARRAY_HOLE)) {
         826                 :             /* Type information for dense array elements must be correct. */
         827          403552 :             JS_ASSERT_IF(!obj->hasSingletonType(),
         828          403552 :                          js::types::TypeHasProperty(cx, obj->type(), JSID_VOID, *vp));
         829                 :
         830          201776 :             return true;
         831                 :         }
         832                 :     }
         833                 :
         834          494097 :     JSObject *proto = obj->getProto();
         835          494097 :     if (!proto) {
         836               0 :         vp->setUndefined();
         837               0 :         return true;
         838                 :     }
         839                 :
         840          494097 :     return proto->getElement(cx, receiver, index, vp);
         841                 : }
         842                 :
         843                 : static JSBool
         844               0 : array_getSpecial(JSContext *cx, JSObject *obj, JSObject *receiver, SpecialId sid, Value *vp)
         845                 : {
         846               0 :     if (obj->isDenseArray() && !obj->getProto()) {
         847               0 :         vp->setUndefined();
         848               0 :         return true;
         849                 :     }
         850                 :
         851               0 :     return js_GetProperty(cx, obj, receiver, SPECIALID_TO_JSID(sid), vp);
         852                 : }
         853                 :
         854                 : static JSBool
         855         2397752 : array_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
         856                 : {
         857         2397752 :     Value idval = IdToValue(id);
         858                 :
         859                 :     uint32_t index;
         860         2397752 :     if (IsDefinitelyIndex(idval, &index))
         861           17573 :         return array_getElement(cx, obj, receiver, index, vp);
         862                 :
         863         2380179 :     SpecialId sid;
         864         2380179 :     if (ValueIsSpecial(obj, &idval, &sid, cx))
         865               0 :         return array_getSpecial(cx, obj, receiver, sid, vp);
         866                 :
         867                 :     JSAtom *atom;
         868         2380179 :     if (!js_ValueToAtom(cx, idval, &atom))
         869               0 :         return false;
         870                 :
         871         2380179 :     if (atom->isIndex(&index))
         872             465 :         return array_getElement(cx, obj, receiver, index, vp);
         873                 :
         874         2379714 :     return array_getProperty(cx, obj, receiver, atom->asPropertyName(), vp);
         875                 : }
         876                 :
         877                 : static JSBool
         878         1992603 : slowarray_addProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
         879                 : {
         880                 :     uint32_t index, length;
         881                 :
         882         1992603 :     if (!js_IdIsIndex(id, &index))
         883          867448 :         return JS_TRUE;
         884         1125155 :     length = obj->getArrayLength();
         885         1125155 :     if (index >= length)
         886         1121825 :         obj->setArrayLength(cx, index + 1);
         887         1125155 :     return JS_TRUE;
         888                 : }
         889                 :
         890                 : static JSType
         891              81 : array_typeOf(JSContext *cx, JSObject *obj)
         892                 : {
         893              81 :     return JSTYPE_OBJECT;
         894                 : }
         895                 :
         896                 : static JSBool
         897         1720816 : array_setGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
         898                 : {
         899                 :     uint32_t i;
         900                 :
         901         1720816 :     if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
         902           14241 :         return array_length_setter(cx, obj, id, strict, vp);
         903                 :
         904         1706575 :     if (!obj->isDenseArray())
         905               0 :         return js_SetPropertyHelper(cx, obj, id, 0, vp, strict);
         906                 :
         907                 :     do {
         908         1706575 :         if (!js_IdIsIndex(id, &i))
         909            1215 :             break;
         910         1705360 :         if (js_PrototypeHasIndexedProperties(cx, obj))
         911             963 :             break;
         912                 :
         913         1704397 :         JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, i, 1);
         914         1704397 :         if (result != JSObject::ED_OK) {
         915            1314 :             if (result == JSObject::ED_FAILED)
         916               0 :                 return false;
         917            1314 :             JS_ASSERT(result == JSObject::ED_SPARSE);
         918            1314 :             break;
         919                 :         }
         920                 :
         921         1703083 :         if (i >= obj->getArrayLength())
         922          522204 :             obj->setDenseArrayLength(i + 1);
         923         1703083 :         obj->setDenseArrayElementWithType(cx, i, *vp);
         924         1703083 :         return true;
         925                 :     } while (false);
         926                 :
         927            3492 :     if (!obj->makeDenseArraySlow(cx))
         928               0 :         return false;
         929            3492 :     return js_SetPropertyHelper(cx, obj, id, 0, vp, strict);
         930                 : }
         931                 :
         932                 : static JSBool
         933               0 : array_setProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict)
         934                 : {
         935               0 :     return array_setGeneric(cx, obj, ATOM_TO_JSID(name), vp, strict);
         936                 : }
         937                 :
         938                 : static JSBool
         939           22238 : array_setElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict)
         940                 : {
         941                 :     jsid id;
         942           22238 :     if (!IndexToId(cx, index, &id))
         943               0 :         return false;
         944                 :
         945           22238 :     if (!obj->isDenseArray())
         946               0 :         return js_SetPropertyHelper(cx, obj, id, 0, vp, strict);
         947                 :
         948                 :     do {
         949                 :         /*
         950                 :          * UINT32_MAX is not an array index and must not affect the length
         951                 :          * property, so specifically reject it.
         952                 :          */
         953           22238 :         if (index == UINT32_MAX)
         954               0 :             break;
         955           22238 :         if (js_PrototypeHasIndexedProperties(cx, obj))
         956               0 :             break;
         957                 :
         958           22238 :         JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, index, 1);
         959           22238 :         if (result != JSObject::ED_OK) {
         960               0 :             if (result == JSObject::ED_FAILED)
         961               0 :                 return false;
         962               0 :             JS_ASSERT(result == JSObject::ED_SPARSE);
         963               0 :             break;
         964                 :         }
         965                 :
         966           22238 :         if (index >= obj->getArrayLength())
         967            3635 :             obj->setDenseArrayLength(index + 1);
         968           22238 :         obj->setDenseArrayElementWithType(cx, index, *vp);
         969           22238 :         return true;
         970                 :     } while (false);
         971                 :
         972               0 :     if (!obj->makeDenseArraySlow(cx))
         973               0 :         return false;
         974               0 :     return js_SetPropertyHelper(cx, obj, id, 0, vp, strict);
         975                 : }
         976                 :
         977                 : static JSBool
         978               0 : array_setSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict)
         979                 : {
         980               0 :     return array_setGeneric(cx, obj, SPECIALID_TO_JSID(sid), vp, strict);
         981                 : }
         982                 :
         983                 : JSBool
         984         2004834 : js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj)
         985                 : {
         986                 :     /*
         987                 :      * Walk up the prototype chain and see if this indexed element already
         988                 :      * exists. If we hit the end of the prototype chain, it's safe to set the
         989                 :      * element on the original object.
         990                 :      */
         991         8018118 :     while ((obj = obj->getProto()) != NULL) {
         992                 :         /*
         993                 :          * If the prototype is a non-native object (possibly a dense array), or
         994                 :          * a native object (possibly a slow array) that has indexed properties,
         995                 :          * return true.
         996                 :          */
         997         4009507 :         if (!obj->isNative())
         998               0 :             return JS_TRUE;
         999         4009507 :         if (obj->isIndexed())
        1000            1057 :             return JS_TRUE;
        1001                 :     }
        1002         2003777 :     return JS_FALSE;
        1003                 : }
        1004                 :
        1005                 : static JSBool
        1006         5804034 : array_defineGeneric(JSContext *cx, JSObject *obj, jsid id, const Value *value,
        1007                 :                     JSPropertyOp getter, StrictPropertyOp setter, unsigned attrs)
        1008                 : {
        1009         5804034 :     if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
        1010               0 :         return JS_TRUE;
        1011                 :
        1012         5804034 :     if (!obj->isDenseArray())
        1013               0 :         return js_DefineProperty(cx, obj, id, value, getter, setter, attrs);
        1014                 :
        1015                 :     do {
        1016         5804034 :         uint32_t i = 0;       // init to shut GCC up
        1017         5804034 :         bool isIndex = js_IdIsIndex(id, &i);
        1018         5804034 :         if (!isIndex || attrs != JSPROP_ENUMERATE)
        1019               0 :             break;
        1020                 :
        1021         5804034 :         JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, i, 1);
        1022         5804034 :         if (result != JSObject::ED_OK) {
        1023               0 :             if (result == JSObject::ED_FAILED)
        1024               0 :                 return false;
        1025               0 :             JS_ASSERT(result == JSObject::ED_SPARSE);
        1026               0 :             break;
        1027                 :         }
        1028                 :
        1029         5804034 :         if (i >= obj->getArrayLength())
        1030             388 :             obj->setDenseArrayLength(i + 1);
        1031         5804034 :         obj->setDenseArrayElementWithType(cx, i, *value);
        1032         5804034 :         return true;
        1033                 :     } while (false);
        1034                 :
        1035               0 :     if (!obj->makeDenseArraySlow(cx))
        1036               0 :         return false;
        1037               0 :     return js_DefineProperty(cx, obj, id, value, getter, setter, attrs);
        1038                 : }
        1039                 :
        1040                 : static JSBool
        1041               0 : array_defineProperty(JSContext *cx, JSObject *obj, PropertyName *name, const Value *value,
        1042                 :                      JSPropertyOp getter, StrictPropertyOp setter, unsigned attrs)
        1043                 : {
        1044               0 :     return array_defineGeneric(cx, obj, ATOM_TO_JSID(name), value, getter, setter, attrs);
        1045                 : }
        1046                 :
        1047                 : namespace js {
        1048                 :
        1049                 : /* non-static for direct definition of array elements within the engine */
        1050                 : JSBool
        1051            2843 : array_defineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *value,
        1052                 :                     PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
        1053                 : {
        1054            2843 :     if (!obj->isDenseArray())
        1055               0 :         return js_DefineElement(cx, obj, index, value, getter, setter, attrs);
        1056                 :
        1057                 :     jsid id;
        1058            2843 :     if (!IndexToId(cx, index, &id))
        1059               0 :         return false;
        1060                 :
        1061                 :     do {
        1062                 :         /*
        1063                 :          * UINT32_MAX is not an array index and must not affect the length
        1064                 :          * property, so specifically reject it.
        1065                 :          */
        1066            2843 :         if (attrs != JSPROP_ENUMERATE || index == UINT32_MAX)
        1067               0 :             break;
        1068                 :
        1069            2843 :         JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, index, 1);
        1070            2843 :         if (result != JSObject::ED_OK) {
        1071               0 :             if (result == JSObject::ED_FAILED)
        1072               0 :                 return false;
        1073               0 :             JS_ASSERT(result == JSObject::ED_SPARSE);
        1074               0 :             break;
        1075                 :         }
        1076                 :
        1077            2843 :         if (index >= obj->getArrayLength())
        1078            2187 :             obj->setDenseArrayLength(index + 1);
        1079            2843 :         obj->setDenseArrayElementWithType(cx, index, *value);
        1080            2843 :         return true;
        1081                 :     } while (false);
        1082                 :
        1083               0 :     if (!obj->makeDenseArraySlow(cx))
        1084               0 :         return false;
        1085               0 :     return js_DefineElement(cx, obj, index, value, getter, setter, attrs);
        1086                 : }
        1087                 :
        1088                 : } // namespace js
        1089                 :
        1090                 : static JSBool
        1091               0 : array_defineSpecial(JSContext *cx, JSObject *obj, SpecialId sid, const Value *value,
        1092                 :                     PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
        1093                 : {
        1094               0 :     return array_defineGeneric(cx, obj, SPECIALID_TO_JSID(sid), value, getter, setter, attrs);
        1095                 : }
        1096                 :
        1097                 : static JSBool
        1098             135 : array_getGenericAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
        1099                 : {
        1100             135 :     *attrsp = JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)
        1101             135 :         ? JSPROP_PERMANENT : JSPROP_ENUMERATE;
        1102             135 :     return true;
        1103                 : }
        1104                 :
        1105                 : static JSBool
        1106               0 : array_getPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, unsigned *attrsp)
        1107                 : {
        1108                 :     *attrsp = (name == cx->runtime->atomState.lengthAtom)
        1109                 :               ? JSPROP_PERMANENT
        1110               0 :               : JSPROP_ENUMERATE;
        1111               0 :     return true;
        1112                 : }
        1113                 :
        1114                 : static JSBool
        1115               0 : array_getElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
        1116                 : {
        1117               0 :     *attrsp = JSPROP_ENUMERATE;
        1118               0 :     return true;
        1119                 : }
        1120                 :
        1121                 : static JSBool
        1122               0 : array_getSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, unsigned *attrsp)
        1123                 : {
        1124               0 :     *attrsp = JSPROP_ENUMERATE;
        1125               0 :     return true;
        1126                 : }
        1127                 :
        1128                 : static JSBool
        1129               0 : array_setGenericAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
        1130                 : {
        1131               0 :     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS);
        1132               0 :     return false;
        1133                 : }
        1134                 :
        1135                 : static JSBool
        1136               0 : array_setPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, unsigned *attrsp)
        1137                 : {
        1138               0 :     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS);
        1139               0 :     return false;
        1140                 : }
        1141                 :
        1142                 : static JSBool
        1143               0 : array_setElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
        1144                 : {
        1145               0 :     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS);
        1146               0 :     return false;
        1147                 : }
        1148                 :
        1149                 : static JSBool
        1150               0 : array_setSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, unsigned *attrsp)
        1151                 : {
        1152               0 :     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS);
        1153               0 :     return false;
        1154                 : }
        1155                 :
        1156                 : static JSBool
        1157              99 : array_deleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict)
        1158                 : {
        1159              99 :     if (!obj->isDenseArray())
        1160               0 :         return js_DeleteProperty(cx, obj, name, rval, strict);
        1161                 :
        1162              99 :     if (name == cx->runtime->atomState.lengthAtom) {
        1163              99 :         rval->setBoolean(false);
        1164              99 :         return true;
        1165                 :     }
        1166                 :
        1167               0 :     rval->setBoolean(true);
        1168               0 :     return true;
        1169                 : }
        1170                 :
        1171                 : namespace js {
        1172                 :
        1173                 : /* non-static for direct deletion of array elements within the engine */
        1174                 : JSBool
        1175            1530 : array_deleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict)
        1176                 : {
        1177            1530 :     if (!obj->isDenseArray())
        1178               0 :         return js_DeleteElement(cx, obj, index, rval, strict);
        1179                 :
        1180            1530 :     if (index < obj->getDenseArrayInitializedLength()) {
        1181            1530 :         obj->markDenseArrayNotPacked(cx);
        1182            1530 :         obj->setDenseArrayElement(index, MagicValue(JS_ARRAY_HOLE));
        1183                 :     }
        1184                 :
        1185            1530 :     if (!js_SuppressDeletedElement(cx, obj, index))
        1186               0 :         return false;
        1187                 :
        1188            1530 :     rval->setBoolean(true);
        1189            1530 :     return true;
        1190                 : }
        1191                 :
        1192                 : } // namespace js
        1193                 :
        1194                 : static JSBool
        1195               0 : array_deleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict)
        1196                 : {
        1197               0 :     if (!obj->isDenseArray())
        1198               0 :         return js_DeleteSpecial(cx, obj, sid, rval, strict);
        1199                 :
        1200               0 :     rval->setBoolean(true);
        1201               0 :     return true;
        1202                 : }
        1203                 :
        1204                 : static void
        1205            5528 : array_trace(JSTracer *trc, JSObject *obj)
        1206                 : {
        1207            5528 :     JS_ASSERT(obj->isDenseArray());
        1208                 :
        1209            5528 :     uint32_t initLength = obj->getDenseArrayInitializedLength();
        1210            5528 :     MarkArraySlots(trc, initLength, obj->getDenseArrayElements(), "element");
        1211            5528 : }
        1212                 :
        1213                 : static JSBool
        1214              55 : array_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props)
        1215                 : {
        1216              55 :     JS_ASSERT(obj->isDenseArray());
        1217                 :
        1218                 :     /*
        1219                 :      * We must slowify dense arrays; otherwise, we'd need to detect assignments to holes,
        1220                 :      * since that is effectively adding a new property to the array.
        1221                 :      */
        1222             110 :     if (!obj->makeDenseArraySlow(cx) ||
        1223              55 :         !GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, props))
        1224               0 :         return false;
        1225                 :
        1226              55 :     *success = true;
        1227              55 :     return true;
        1228                 : }
        1229                 :
        1230                 : Class js::ArrayClass = {
        1231                 :     "Array",
        1232                 :     Class::NON_NATIVE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_FOR_OF_ITERATION,
        1233                 :     JS_PropertyStub,         /* addProperty */
        1234                 :     JS_PropertyStub,         /* delProperty */
        1235                 :     JS_PropertyStub,         /* getProperty */
        1236                 :     JS_StrictPropertyStub,   /* setProperty */
        1237                 :     JS_EnumerateStub,
        1238                 :     JS_ResolveStub,
        1239                 :     JS_ConvertStub,
        1240                 :     NULL,
        1241                 :     NULL,           /* checkAccess */
        1242                 :     NULL,           /* call        */
        1243                 :     NULL,           /* construct   */
        1244                 :     NULL,           /* hasInstance */
        1245                 :     array_trace,    /* trace       */
        1246                 :     {
        1247                 :         NULL,       /* equality    */
        1248                 :         NULL,       /* outerObject */
        1249                 :         NULL,       /* innerObject */
        1250                 :         JS_ElementIteratorStub,
        1251                 :         NULL,       /* unused      */
        1252                 :         false,      /* isWrappedNative */
        1253                 :     },
        1254                 :     {
        1255                 :         array_lookupGeneric,
        1256                 :         array_lookupProperty,
        1257                 :         array_lookupElement,
        1258                 :         array_lookupSpecial,
        1259                 :         array_defineGeneric,
        1260                 :         array_defineProperty,
        1261                 :         array_defineElement,
        1262                 :         array_defineSpecial,
        1263                 :         array_getGeneric,
        1264                 :         array_getProperty,
        1265                 :         array_getElement,
        1266                 :         NULL, /* getElementIfPresent, because this is hard for now for
        1267                 :                  slow arrays */
        1268                 :         array_getSpecial,
        1269                 :         array_setGeneric,
        1270                 :         array_setProperty,
        1271                 :         array_setElement,
        1272                 :         array_setSpecial,
        1273                 :         array_getGenericAttributes,
        1274                 :         array_getPropertyAttributes,
        1275                 :         array_getElementAttributes,
        1276                 :         array_getSpecialAttributes,
        1277                 :         array_setGenericAttributes,
        1278                 :         array_setPropertyAttributes,
        1279                 :         array_setElementAttributes,
        1280                 :         array_setSpecialAttributes,
        1281                 :         array_deleteProperty,
        1282                 :         array_deleteElement,
        1283                 :         array_deleteSpecial,
        1284                 :         NULL,       /* enumerate      */
        1285                 :         array_typeOf,
        1286                 :         array_fix,
        1287                 :         NULL,       /* thisObject     */
        1288                 :         NULL,       /* clear          */
        1289                 :     }
        1290                 : };
        1291                 :
        1292                 : Class js::SlowArrayClass = {
        1293                 :     "Array",
        1294                 :     JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_FOR_OF_ITERATION,
        1295                 :     slowarray_addProperty,
        1296                 :     JS_PropertyStub,         /* delProperty */
        1297                 :     JS_PropertyStub,         /* getProperty */
        1298                 :     JS_StrictPropertyStub,   /* setProperty */
        1299                 :     JS_EnumerateStub,
        1300                 :     JS_ResolveStub,
        1301                 :     JS_ConvertStub,
        1302                 :     NULL,
        1303                 :     NULL,           /* checkAccess */
        1304                 :     NULL,           /* call        */
        1305                 :     NULL,           /* construct   */
        1306                 :     NULL,           /* hasInstance */
        1307                 :     NULL,           /* trace       */
        1308                 :     {
        1309                 :         NULL,       /* equality    */
        1310                 :         NULL,       /* outerObject */
        1311                 :         NULL,       /* innerObject */
        1312                 :         JS_ElementIteratorStub,
        1313                 :         NULL,       /* unused      */
        1314                 :         false,      /* isWrappedNative */
        1315                 :     }
        1316                 : };
        1317                 :
        1318                 : bool
        1319          234531 : JSObject::allocateSlowArrayElements(JSContext *cx)
        1320                 : {
        1321          234531 :     JS_ASSERT(hasClass(&js::SlowArrayClass));
        1322          234531 :     JS_ASSERT(elements == emptyObjectElements);
        1323                 :
        1324          234531 :     ObjectElements *header = cx->new_<ObjectElements>(0, 0);
        1325          234531 :     if (!header)
        1326               0 :         return false;
        1327                 :
        1328          234531 :     elements = header->elements();
        1329          234531 :     return true;
        1330                 : }
        1331                 :
        1332                 : static bool
        1333          234531 : AddLengthProperty(JSContext *cx, JSObject *obj)
        1334                 : {
        1335                 :     /*
        1336                 :      * Add the 'length' property for a newly created or converted slow array,
        1337                 :      * and update the elements to be an empty array owned by the object.
        1338                 :      * The shared emptyObjectElements singleton cannot be used for slow arrays,
        1339                 :      * as accesses to 'length' will use the elements header.
        1340                 :      */
        1341                 :
        1342          234531 :     const jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
        1343          234531 :     JS_ASSERT(!obj->nativeLookup(cx, lengthId));
        1344                 :
        1345          234531 :     if (!obj->allocateSlowArrayElements(cx))
        1346               0 :         return false;
        1347                 :
        1348                 :     return obj->addProperty(cx, lengthId, array_length_getter, array_length_setter,
        1349          234531 :                             SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_SHARED, 0, 0);
        1350                 : }
        1351                 :
        1352                 : /*
        1353                 :  * Convert an array object from fast-and-dense to slow-and-flexible.
        1354                 :  */
        1355                 : JSBool
        1356            4735 : JSObject::makeDenseArraySlow(JSContext *cx)
        1357                 : {
        1358            4735 :     JS_ASSERT(isDenseArray());
        1359                 :
        1360                 :     MarkTypeObjectFlags(cx, this,
        1361                 :                         OBJECT_FLAG_NON_PACKED_ARRAY |
        1362            4735 :                         OBJECT_FLAG_NON_DENSE_ARRAY);
        1363                 :
        1364            4735 :     uint32_t arrayCapacity = getDenseArrayCapacity();
        1365            4735 :     uint32_t arrayInitialized = getDenseArrayInitializedLength();
        1366                 :
        1367                 :     /*
        1368                 :      * Get an allocated array of the existing elements, evicting from the fixed
        1369                 :      * slots if necessary.
        1370                 :      */
        1371            4735 :     if (!hasDynamicElements()) {
        1372            4330 :         if (!growElements(cx, arrayCapacity))
        1373               0 :             return false;
        1374            4330 :         JS_ASSERT(hasDynamicElements());
        1375                 :     }
        1376                 :
        1377                 :     /*
        1378                 :      * Save old map now, before calling InitScopeForObject. We'll have to undo
        1379                 :      * on error. This is gross, but a better way is not obvious. Note: the
        1380                 :      * exact contents of the array are not preserved on error.
        1381                 :      */
        1382            4735 :     js::Shape *oldShape = lastProperty();
        1383                 :
        1384                 :     /* Create a native scope. */
        1385            4735 :     gc::AllocKind kind = getAllocKind();
        1386                 :     Shape *shape = EmptyShape::getInitialShape(cx, &SlowArrayClass, getProto(),
        1387            4735 :                                                oldShape->getObjectParent(), kind);
        1388            4735 :     if (!shape)
        1389               0 :         return false;
        1390            4735 :     this->shape_ = shape;
        1391                 :
        1392                 :     /* Take ownership of the dense elements, reset to an empty dense array. */
        1393            4735 :     HeapSlot *elems = elements;
        1394            4735 :     elements = emptyObjectElements;
        1395                 :
        1396                 :     /* Root all values in the array during conversion. */
        1397            9470 :     AutoValueArray autoArray(cx, (Value *) elems, arrayInitialized);
        1398                 :
        1399                 :     /*
        1400                 :      * Begin with the length property to share more of the property tree.
        1401                 :      * The getter/setter here will directly access the object's private value.
        1402                 :      */
        1403            4735 :     if (!AddLengthProperty(cx, this)) {
        1404               0 :         this->shape_ = oldShape;
        1405               0 :         if (elements != emptyObjectElements)
        1406               0 :             cx->free_(getElementsHeader());
        1407               0 :         elements = elems;
        1408               0 :         return false;
        1409                 :     }
        1410                 :
        1411                 :     /*
        1412                 :      * Create new properties pointing to existing elements. Pack the array to
        1413                 :      * remove holes, so that shapes use successive slots (as for other objects).
        1414                 :      */
        1415            4735 :     uint32_t next = 0;
        1416           22135 :     for (uint32_t i = 0; i < arrayInitialized; i++) {
        1417                 :         /* Dense array indexes can always fit in a jsid. */
        1418                 :         jsid id;
        1419           17400 :         JS_ALWAYS_TRUE(ValueToId(cx, Int32Value(i), &id));
        1420                 :
        1421           17400 :         if (elems[i].isMagic(JS_ARRAY_HOLE))
        1422           11962 :             continue;
        1423                 :
        1424            5438 :         if (!addDataProperty(cx, id, next, JSPROP_ENUMERATE)) {
        1425               0 :             this->shape_ = oldShape;
        1426               0 :             cx->free_(getElementsHeader());
        1427               0 :             elements = elems;
        1428               0 :             return false;
        1429                 :         }
        1430                 :
        1431            5438 :         initSlot(next, elems[i]);
        1432                 :
        1433            5438 :         next++;
        1434                 :     }
        1435                 :
        1436            4735 :     ObjectElements *oldheader = ObjectElements::fromElements(elems);
        1437                 :
        1438            4735 :     getElementsHeader()->length = oldheader->length;
        1439            4735 :     cx->free_(oldheader);
        1440                 :
        1441            4735 :     return true;
        1442                 : }
        1443                 :
        1444                 : #if JS_HAS_TOSOURCE
        1445                 : class ArraySharpDetector
        1446                 : {
        1447                 :     JSContext *cx;
        1448                 :     bool success;
        1449                 :     bool alreadySeen;
        1450                 :     bool sharp;
        1451                 :
        1452                 :   public:
        1453             144 :     ArraySharpDetector(JSContext *cx)
        1454                 :       : cx(cx),
        1455                 :         success(false),
        1456                 :         alreadySeen(false),
        1457             144 :         sharp(false)
        1458             144 :     {}
        1459                 :
        1460             144 :     bool init(JSObject *obj) {
        1461             144 :         success = js_EnterSharpObject(cx, obj, NULL, &alreadySeen, &sharp);
        1462             144 :         if (!success)
        1463               0 :             return false;
        1464             144 :         return true;
        1465                 :     }
        1466                 :
        1467             144 :     bool initiallySharp() const {
        1468             144 :         JS_ASSERT_IF(sharp, alreadySeen);
        1469             144 :         return sharp;
        1470                 :     }
        1471                 :
        1472             144 :     ~ArraySharpDetector() {
        1473             144 :         if (success && !sharp)
        1474             144 :             js_LeaveSharpObject(cx, NULL);
        1475             144 :     }
        1476                 : };
        1477                 :
        1478                 : static JSBool
        1479             207 : array_toSource(JSContext *cx, unsigned argc, Value *vp)
        1480                 : {
        1481             207 :     JS_CHECK_RECURSION(cx, return false);
        1482                 :
        1483             207 :     CallArgs args = CallArgsFromVp(argc, vp);
        1484             207 :     JSObject *obj = ToObject(cx, &args.thisv());
        1485             207 :     if (!obj)
        1486               0 :         return false;
        1487             207 :     if (!obj->isArray())
        1488              63 :         return HandleNonGenericMethodClassMismatch(cx, args, array_toSource, &ArrayClass);
        1489                 :
        1490             288 :     ArraySharpDetector detector(cx);
        1491             144 :     if (!detector.init(obj))
        1492               0 :         return false;
        1493                 :
        1494             288 :     StringBuffer sb(cx);
        1495                 :
        1496             144 :     if (detector.initiallySharp()) {
        1497               0 :         if (!sb.append("[]"))
        1498               0 :             return false;
        1499               0 :         goto make_string;
        1500                 :     }
        1501                 :
        1502             144 :     if (!sb.append('['))
        1503               0 :         return false;
        1504                 :
        1505                 :     uint32_t length;
        1506             144 :     if (!js_GetLengthProperty(cx, obj, &length))
        1507               0 :         return false;
        1508                 :
        1509             882 :     for (uint32_t index = 0; index < length; index++) {
        1510                 :         JSBool hole;
        1511                 :         Value elt;
        1512            1476 :         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
        1513             738 :             !GetElement(cx, obj, index, &hole, &elt)) {
        1514               0 :             return false;
        1515                 :         }
        1516                 :
        1517                 :         /* Get element's character string. */
        1518                 :         JSString *str;
        1519             738 :         if (hole) {
        1520               0 :             str = cx->runtime->emptyString;
        1521                 :         } else {
        1522             738 :             str = js_ValueToSource(cx, elt);
        1523             738 :             if (!str)
        1524               0 :                 return false;
        1525                 :         }
        1526                 :
        1527                 :         /* Append element to buffer. */
        1528             738 :         if (!sb.append(str))
        1529               0 :             return false;
        1530             738 :         if (index + 1 != length) {
        1531             612 :             if (!sb.append(", "))
        1532               0 :                 return false;
        1533             126 :         } else if (hole) {
        1534               0 :             if (!sb.append(','))
        1535               0 :                 return false;
        1536                 :         }
        1537                 :     }
        1538                 :
        1539                 :     /* Finalize the buffer. */
        1540             144 :     if (!sb.append(']'))
        1541               0 :         return false;
        1542                 :
        1543                 :   make_string:
        1544             144 :     JSString *str = sb.finishString();
        1545             144 :     if (!str)
        1546               0 :         return false;
        1547                 :
        1548             144 :     args.rval().setString(str);
        1549             144 :     return true;
        1550                 : }
        1551                 : #endif
        1552                 :
        1553                 : class AutoArrayCycleDetector
        1554                 : {
        1555                 :     JSContext *cx;
        1556                 :     JSObject *obj;
        1557                 :     uint32_t genBefore;
        1558                 :     BusyArraysSet::AddPtr hashPointer;
        1559                 :     bool cycle;
        1560                 :     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
        1561                 :
        1562                 :   public:
        1563          125028 :     AutoArrayCycleDetector(JSContext *cx, JSObject *obj JS_GUARD_OBJECT_NOTIFIER_PARAM)
        1564                 :       : cx(cx),
        1565                 :         obj(obj),
        1566          125028 :         cycle(true)
        1567                 :     {
        1568          125028 :         JS_GUARD_OBJECT_NOTIFIER_INIT;
        1569          125028 :     }
        1570                 :
        1571          125028 :     bool init()
        1572                 :     {
        1573          125028 :         BusyArraysSet &set = cx->busyArrays;
        1574          125028 :         hashPointer = set.lookupForAdd(obj);
        1575          125028 :         if (!hashPointer) {
        1576          125028 :             if (!set.add(hashPointer, obj))
        1577               0 :                 return false;
        1578          125028 :             cycle = false;
        1579          125028 :             genBefore = set.generation();
        1580                 :         }
        1581          125028 :         return true;
        1582                 :     }
        1583                 :
        1584          125028 :     ~AutoArrayCycleDetector()
        1585          125028 :     {
        1586          125028 :         if (!cycle) {
        1587          125028 :             if (genBefore == cx->busyArrays.generation())
        1588          124992 :                 cx->busyArrays.remove(hashPointer);
        1589                 :             else
        1590              36 :                 cx->busyArrays.remove(obj);
        1591                 :         }
        1592          125028 :     }
        1593                 :
        1594          125028 :     bool foundCycle() { return cycle; }
        1595                 :
        1596                 :   protected:
        1597                 : };
        1598                 :
        1599                 : static JSBool
        1600          125028 : array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
        1601                 :                    JSString *sepstr, CallArgs &args)
        1602                 : {
        1603                 :     static const jschar comma = ',';
        1604                 :     const jschar *sep;
        1605                 :     size_t seplen;
        1606          125028 :     if (sepstr) {
        1607           22860 :         seplen = sepstr->length();
        1608           22860 :         sep = sepstr->getChars(cx);
        1609           22860 :         if (!sep)
        1610               0 :             return false;
        1611                 :     } else {
        1612          102168 :         sep = &comma;
        1613          102168 :         seplen = 1;
        1614                 :     }
        1615                 :
        1616          250056 :     AutoArrayCycleDetector detector(cx, obj);
        1617          125028 :     if (!detector.init())
        1618               0 :         return false;
        1619                 :
        1620          125028 :     if (detector.foundCycle()) {
        1621               0 :         args.rval().setString(cx->runtime->atomState.emptyAtom);
        1622               0 :         return true;
        1623                 :     }
        1624                 :
        1625                 :     uint32_t length;
        1626          125028 :     if (!js_GetLengthProperty(cx, obj, &length))
        1627               0 :         return false;
        1628                 :
        1629          250056 :     StringBuffer sb(cx);
        1630                 :
        1631          125028 :     if (!locale && !seplen && obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj)) {
        1632           17685 :         const Value *start = obj->getDenseArrayElements();
        1633           17685 :         const Value *end = start + obj->getDenseArrayInitializedLength();
        1634                 :         const Value *elem;
        1635          626391 :         for (elem = start; elem < end; elem++) {
        1636          608715 :             if (!JS_CHECK_OPERATION_LIMIT(cx))
        1637               0 :                 return false;
        1638                 :
        1639                 :             /*
        1640                 :              * Object stringifying is slow; delegate it to a separate loop to
        1641                 :              * keep this one tight.
        1642                 :              */
        1643          608715 :             if (elem->isObject())
        1644               9 :                 break;
        1645                 :
        1646          608706 :             if (!elem->isMagic(JS_ARRAY_HOLE) && !elem->isNullOrUndefined()) {
        1647          608706 :                 if (!ValueToStringBuffer(cx, *elem, sb))
        1648               0 :                     return false;
        1649                 :             }
        1650                 :         }
        1651                 :
        1652           17775 :         for (uint32_t i = uint32_t(PointerRangeSize(start, elem)); i < length; i++) {
        1653              90 :             if (!JS_CHECK_OPERATION_LIMIT(cx))
        1654               0 :                 return false;
        1655                 :
        1656                 :             JSBool hole;
        1657                 :             Value v;
        1658              90 :             if (!GetElement(cx, obj, i, &hole, &v))
        1659               0 :                 return false;
        1660              90 :             if (!hole && !v.isNullOrUndefined()) {
        1661              90 :                 if (!ValueToStringBuffer(cx, v, sb))
        1662               0 :                     return false;
        1663                 :             }
        1664                 :         }
        1665                 :     } else {
        1666          766521 :         for (uint32_t index = 0; index < length; index++) {
        1667          659187 :             if (!JS_CHECK_OPERATION_LIMIT(cx))
        1668               0 :                 return false;
        1669                 :
        1670                 :             JSBool hole;
        1671                 :             Value elt;
        1672          659187 :             if (!GetElement(cx, obj, index, &hole, &elt))
        1673               0 :                 return false;
        1674                 :
        1675          659187 :             if (!hole && !elt.isNullOrUndefined()) {
        1676          247149 :                 if (locale) {
        1677               0 :                     JSObject *robj = ToObject(cx, &elt);
        1678               0 :                     if (!robj)
        1679               0 :                         return false;
        1680               0 :                     jsid id = ATOM_TO_JSID(cx->runtime->atomState.toLocaleStringAtom);
        1681               0 :                     if (!robj->callMethod(cx, id, 0, NULL, &elt))
        1682               0 :                         return false;
        1683                 :                 }
        1684          247149 :                 if (!ValueToStringBuffer(cx, elt, sb))
        1685               9 :                     return false;
        1686                 :             }
        1687                 :
        1688          659178 :             if (index + 1 != length) {
        1689          565002 :                 if (!sb.append(sep, seplen))
        1690               0 :                     return false;
        1691                 :             }
        1692                 :         }
        1693                 :     }
        1694                 :
        1695          125019 :     JSString *str = sb.finishString();
        1696          125019 :     if (!str)
        1697               0 :         return false;
        1698          125019 :     args.rval().setString(str);
        1699          125019 :     return true;
        1700                 : }
        1701                 :
        1702                 : /* ES5 15.4.4.2. NB: The algorithm here differs from the one in ES3. */
        1703                 : static JSBool
        1704          101997 : array_toString(JSContext *cx, unsigned argc, Value *vp)
        1705                 : {
        1706          101997 :     JS_CHECK_RECURSION(cx, return false);
        1707                 :
        1708          101997 :     CallArgs args = CallArgsFromVp(argc, vp);
        1709          101997 :     JSObject *obj = ToObject(cx, &args.thisv());
        1710          101997 :     if (!obj)
        1711               0 :         return false;
        1712                 :
        1713          101997 :     Value join = args.calleev();
        1714          101997 :     if (!obj->getProperty(cx, cx->runtime->atomState.joinAtom, &join))
        1715               0 :         return false;
        1716                 :
        1717          101997 :     if (!js_IsCallable(join)) {
        1718               0 :         JSString *str = obj_toStringHelper(cx, obj);
        1719               0 :         if (!str)
        1720               0 :             return false;
        1721               0 :         args.rval().setString(str);
        1722               0 :         return true;
        1723                 :     }
        1724                 :
        1725          203994 :     InvokeArgsGuard ag;
        1726          101997 :     if (!cx->stack.pushInvokeArgs(cx, 0, &ag))
        1727               0 :         return false;
        1728                 :
        1729          101997 :     ag.calleev() = join;
        1730          101997 :     ag.thisv().setObject(*obj);
        1731                 :
        1732                 :     /* Do the call. */
        1733          101997 :     if (!Invoke(cx, ag))
        1734               9 :         return false;
        1735          101988 :     args.rval() = ag.rval();
        1736          101988 :     return true;
        1737                 : }
        1738                 :
        1739                 : static JSBool
        1740               0 : array_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
        1741                 : {
        1742               0 :     JS_CHECK_RECURSION(cx, return false);
        1743                 :
        1744               0 :     CallArgs args = CallArgsFromVp(argc, vp);
        1745               0 :     JSObject *obj = ToObject(cx, &args.thisv());
        1746               0 :     if (!obj)
        1747               0 :         return false;
        1748                 :
        1749                 :     /*
        1750                 :      *  Passing comma here as the separator. Need a way to get a
        1751                 :      *  locale-specific version.
        1752                 :      */
        1753               0 :     return array_toString_sub(cx, obj, JS_TRUE, NULL, args);
        1754                 : }
        1755                 :
        1756                 : static inline bool
        1757           38403 : InitArrayTypes(JSContext *cx, TypeObject *type, const Value *vector, unsigned count)
        1758                 : {
        1759           38403 :     if (cx->typeInferenceEnabled() && !type->unknownProperties()) {
        1760           22086 :         AutoEnterTypeInference enter(cx);
        1761                 :
        1762           11043 :         TypeSet *types = type->getProperty(cx, JSID_VOID, true);
        1763           11043 :         if (!types)
        1764               0 :             return false;
        1765                 :
        1766           22361 :         for (unsigned i = 0; i < count; i++) {
        1767           11318 :             if (vector[i].isMagic(JS_ARRAY_HOLE))
        1768               0 :                 continue;
        1769           11318 :             Type valtype = GetValueType(cx, vector[i]);
        1770           11318 :             types->addType(cx, valtype);
        1771                 :         }
        1772                 :     }
        1773           38403 :     return true;
        1774                 : }
        1775                 :
        1776                 : enum ShouldUpdateTypes
        1777                 : {
        1778                 :     UpdateTypes = true,
        1779                 :     DontUpdateTypes = false
        1780                 : };
        1781                 :
        1782                 : static bool
        1783           43427 : InitArrayElements(JSContext *cx, JSObject *obj, uint32_t start, uint32_t count, const Value *vector, ShouldUpdateTypes updateTypes)
        1784                 : {
        1785           43427 :     JS_ASSERT(count <= MAX_ARRAY_INDEX);
        1786                 :
        1787           43427 :     if (count == 0)
        1788              18 :         return true;
        1789                 :
        1790           43409 :     if (updateTypes && !InitArrayTypes(cx, obj->getType(cx), vector, count))
        1791               0 :         return false;
        1792                 :
        1793                 :     /*
        1794                 :      * Optimize for dense arrays so long as adding the given set of elements
        1795                 :      * wouldn't otherwise make the array slow.
        1796                 :      */
        1797                 :     do {
        1798           43409 :         if (!obj->isDenseArray())
        1799             414 :             break;
        1800           42995 :         if (js_PrototypeHasIndexedProperties(cx, obj))
        1801               0 :             break;
        1802                 :
        1803           42995 :         JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, start, count);
        1804           42995 :         if (result != JSObject::ED_OK) {
        1805               0 :             if (result == JSObject::ED_FAILED)
        1806               0 :                 return false;
        1807               0 :             JS_ASSERT(result == JSObject::ED_SPARSE);
        1808               0 :             break;
        1809                 :         }
        1810           42995 :         uint32_t newlen = start + count;
        1811           42995 :         if (newlen > obj->getArrayLength())
        1812           10082 :             obj->setDenseArrayLength(newlen);
        1813                 :
        1814           42995 :         JS_ASSERT(count < UINT32_MAX / sizeof(Value));
        1815           42995 :         obj->copyDenseArrayElements(start, vector, count);
        1816           42995 :         JS_ASSERT_IF(count != 0, !obj->getDenseArrayElement(newlen - 1).isMagic(JS_ARRAY_HOLE));
        1817           42995 :         return true;
        1818                 :     } while (false);
        1819                 :
        1820             414 :     const Value* end = vector + count;
        1821            1251 :     while (vector < end && start <= MAX_ARRAY_INDEX) {
        1822             846 :         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
        1823             423 :             !SetArrayElement(cx, obj, start++, *vector++)) {
        1824               0 :             return false;
        1825                 :         }
        1826                 :     }
        1827                 :
        1828             414 :     if (vector == end)
        1829             414 :         return true;
        1830                 :
        1831                 :     /* Finish out any remaining elements past the max array index. */
        1832               0 :     if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
        1833               0 :         return false;
        1834                 :
        1835               0 :     JS_ASSERT(start == MAX_ARRAY_INDEX + 1);
        1836               0 :     AutoValueRooter tvr(cx);
        1837               0 :     AutoIdRooter idr(cx);
        1838               0 :     Value idval = DoubleValue(MAX_ARRAY_INDEX + 1);
        1839               0 :     do {
        1840               0 :         *tvr.addr() = *vector++;
        1841               0 :         if (!js_ValueToStringId(cx, idval, idr.addr()) ||
        1842               0 :             !obj->setGeneric(cx, idr.id(), tvr.addr(), true)) {
        1843               0 :             return false;
        1844                 :         }
        1845               0 :         idval.getDoubleRef() += 1;
        1846                 :     } while (vector != end);
        1847                 :
        1848               0 :     return true;
        1849                 : }
        1850                 :
        1851                 : #if 0
        1852                 : static JSBool
        1853                 : InitArrayObject(JSContext *cx, JSObject *obj, uint32_t length, const Value *vector)
        1854                 : {
        1855                 :     JS_ASSERT(obj->isArray());
        1856                 :
        1857                 :     JS_ASSERT(obj->isDenseArray());
        1858                 :     obj->setArrayLength(cx, length);
        1859                 :     if (!vector || !length)
        1860                 :         return true;
        1861                 :
        1862                 :     if (!InitArrayTypes(cx, obj->getType(cx), vector, length))
        1863                 :         return false;
        1864                 :
        1865                 :     /* Avoid ensureDenseArrayElements to skip sparse array checks there. */
        1866                 :     if (!obj->ensureElements(cx, length))
        1867                 :         return false;
        1868                 :
        1869                 :     obj->setDenseArrayInitializedLength(length);
        1870                 :
        1871                 :     bool hole = false;
        1872                 :     for (uint32_t i = 0; i < length; i++) {
        1873                 :         obj->setDenseArrayElement(i, vector[i]);
        1874                 :         hole |= vector[i].isMagic(JS_ARRAY_HOLE);
        1875                 :     }
        1876                 :     if (hole)
        1877                 :         obj->markDenseArrayNotPacked(cx);
        1878                 :
        1879                 :     return true;
        1880                 : }
        1881                 : #endif
        1882                 :
        1883                 : /*
        1884                 :  * Perl-inspired join, reverse, and sort.
        1885                 :  */
        1886                 : static JSBool
        1887          125028 : array_join(JSContext *cx, unsigned argc, Value *vp)
        1888                 : {
        1889          125028 :     JS_CHECK_RECURSION(cx, return false);
        1890                 :
        1891          125028 :     CallArgs args = CallArgsFromVp(argc, vp);
        1892                 :     JSString *str;
        1893          125028 :     if (args.hasDefined(0)) {
        1894           22860 :         str = ToString(cx, args[0]);
        1895           22860 :         if (!str)
        1896               0 :             return JS_FALSE;
        1897           22860 :         args[0].setString(str);
        1898                 :     } else {
        1899          102168 :         str = NULL;
        1900                 :     }
        1901          125028 :     JSObject *obj = ToObject(cx, &args.thisv());
        1902          125028 :     if (!obj)
        1903               0 :         return false;
        1904          125028 :     return array_toString_sub(cx, obj, JS_FALSE, str, args);
        1905                 : }
        1906                 :
        1907                 : static JSBool
        1908             468 : array_reverse(JSContext *cx, unsigned argc, Value *vp)
        1909                 : {
        1910             468 :     CallArgs args = CallArgsFromVp(argc, vp);
        1911             468 :     JSObject *obj = ToObject(cx, &args.thisv());
        1912             468 :     if (!obj)
        1913               0 :         return false;
        1914                 :
        1915                 :     uint32_t len;
        1916             468 :     if (!js_GetLengthProperty(cx, obj, &len))
        1917               0 :         return false;
        1918                 :
        1919                 :     do {
        1920             468 :         if (!obj->isDenseArray())
        1921               0 :             break;
        1922             468 :         if (js_PrototypeHasIndexedProperties(cx, obj))
        1923               0 :             break;
        1924                 :
        1925                 :         /* An empty array or an array with no elements is already reversed. */
        1926             468 :         if (len == 0 || obj->getDenseArrayCapacity() == 0) {
        1927               0 :             args.rval().setObject(*obj);
        1928               0 :             return true;
        1929                 :         }
        1930                 :
        1931                 :         /*
        1932                 :          * It's actually surprisingly complicated to reverse an array due to the
        1933                 :          * orthogonality of array length and array capacity while handling
        1934                 :          * leading and trailing holes correctly.  Reversing seems less likely to
        1935                 :          * be a common operation than other array mass-mutation methods, so for
        1936                 :          * now just take a probably-small memory hit (in the absence of too many
        1937                 :          * holes in the array at its start) and ensure that the capacity is
        1938                 :          * sufficient to hold all the elements in the array if it were full.
        1939                 :          */
        1940             468 :         JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, len, 0);
        1941             468 :         if (result != JSObject::ED_OK) {
        1942             342 :             if (result == JSObject::ED_FAILED)
        1943               0 :                 return false;
        1944             342 :             JS_ASSERT(result == JSObject::ED_SPARSE);
        1945             342 :             break;
        1946                 :         }
        1947                 :
        1948                 :         /* Fill out the array's initialized length to its proper length. */
        1949             126 :         obj->ensureDenseArrayInitializedLength(cx, len, 0);
        1950                 :
        1951             126 :         uint32_t lo = 0, hi = len - 1;
        1952           13653 :         for (; lo < hi; lo++, hi--) {
        1953           13527 :             Value origlo = obj->getDenseArrayElement(lo);
        1954           13527 :             Value orighi = obj->getDenseArrayElement(hi);
        1955           13527 :             obj->setDenseArrayElement(lo, orighi);
        1956           27027 :             if (orighi.isMagic(JS_ARRAY_HOLE) &&
        1957           13500 :                 !js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(lo))) {
        1958               0 :                 return false;
        1959                 :             }
        1960           13527 :             obj->setDenseArrayElement(hi, origlo);
        1961           26496 :             if (origlo.isMagic(JS_ARRAY_HOLE) &&
        1962           12969 :                 !js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(hi))) {
        1963               0 :                 return false;
        1964                 :             }
        1965                 :         }
        1966                 :
        1967                 :         /*
        1968                 :          * Per ECMA-262, don't update the length of the array, even if the new
        1969                 :          * array has trailing holes (and thus the original array began with
        1970                 :          * holes).
        1971                 :          */
        1972             126 :         args.rval().setObject(*obj);
        1973             126 :         return true;
        1974                 :     } while (false);
        1975                 :
        1976                 :     Value lowval, hival;
        1977           47367 :     for (uint32_t i = 0, half = len / 2; i < half; i++) {
        1978                 :         JSBool hole, hole2;
        1979          235125 :         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
        1980           47025 :             !GetElement(cx, obj, i, &hole, &lowval) ||
        1981           47025 :             !GetElement(cx, obj, len - i - 1, &hole2, &hival) ||
        1982           47025 :             !SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, lowval) ||
        1983           47025 :             !SetOrDeleteArrayElement(cx, obj, i, hole2, hival)) {
        1984               0 :             return false;
        1985                 :         }
        1986                 :     }
        1987             342 :     args.rval().setObject(*obj);
        1988             342 :     return true;
        1989                 : }
        1990                 :
        1991                 : namespace {
        1992                 :
        1993                 : inline bool
        1994           61488 : CompareStringValues(JSContext *cx, const Value &a, const Value &b, bool *lessOrEqualp)
        1995                 : {
        1996           61488 :     if (!JS_CHECK_OPERATION_LIMIT(cx))
        1997               0 :         return false;
        1998                 :
        1999           61488 :     JSString *astr = a.toString();
        2000           61488 :     JSString *bstr = b.toString();
        2001                 :     int32_t result;
        2002           61488 :     if (!CompareStrings(cx, astr, bstr, &result))
        2003               0 :         return false;
        2004                 :
        2005           61488 :     *lessOrEqualp = (result <= 0);
        2006           61488 :     return true;
        2007                 : }
        2008                 :
        2009                 : static uint32_t const powersOf10[] = {
        2010                 :     1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
        2011                 : };
        2012                 :
        2013                 : static inline unsigned
        2014            6180 : NumDigitsBase10(uint32_t n)
        2015                 : {
        2016                 :     /*
        2017                 :      * This is just floor_log10(n) + 1
        2018                 :      * Algorithm taken from
        2019                 :      * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
        2020                 :      */
        2021                 :     uint32_t log2, t;
        2022            6180 :     JS_CEILING_LOG2(log2, n);
        2023            6180 :     t = log2 * 1233 >> 12;
        2024            6180 :     return t - (n < powersOf10[t]) + 1;
        2025                 : }
        2026                 :
        2027                 : static JS_ALWAYS_INLINE uint32_t
        2028            1560 : NegateNegativeInt32(int32_t i)
        2029                 : {
        2030                 :     /*
        2031                 :      * We cannot simply return '-i' because this is undefined for INT32_MIN.
        2032                 :      * 2s complement does actually give us what we want, however.  That is,
        2033                 :      * ~0x80000000 + 1 = 0x80000000 which is correct when interpreted as a
        2034                 :      * uint32_t. To avoid undefined behavior, we write out 2s complement
        2035                 :      * explicitly and rely on the peephole optimizer to generate 'neg'.
        2036                 :      */
        2037            1560 :     return ~uint32_t(i) + 1;
        2038                 : }
        2039                 :
        2040                 : inline bool
        2041            6125 : CompareLexicographicInt32(JSContext *cx, const Value &a, const Value &b, bool *lessOrEqualp)
        2042                 : {
        2043            6125 :     int32_t aint = a.toInt32();
        2044            6125 :     int32_t bint = b.toInt32();
        2045                 :
        2046                 :     /*
        2047                 :      * If both numbers are equal ... trivial
        2048                 :      * If only one of both is negative --> arithmetic comparison as char code
        2049                 :      * of '-' is always less than any other digit
        2050                 :      * If both numbers are negative convert them to positive and continue
        2051                 :      * handling ...
        2052                 :      */
        2053            6125 :     if (aint == bint) {
        2054             175 :         *lessOrEqualp = true;
        2055            5950 :     } else if ((aint < 0) && (bint >= 0)) {
        2056            1430 :         *lessOrEqualp = true;
        2057            4520 :     } else if ((aint >= 0) && (bint < 0)) {
        2058            1430 :         *lessOrEqualp = false;
        2059                 :     } else {
        2060                 :         uint32_t auint, buint;
        2061            3090 :         if (aint >= 0) {
        2062            2310 :             auint = aint;
        2063            2310 :             buint = bint;
        2064                 :         } else {
        2065             780 :             auint = NegateNegativeInt32(aint);
        2066             780 :             buint = NegateNegativeInt32(bint);
        2067                 :         }
        2068                 :
        2069                 :         /*
        2070                 :          *  ... get number of digits of both integers.
        2071                 :          * If they have the same number of digits --> arithmetic comparison.
        2072                 :          * If digits_a > digits_b: a < b*10e(digits_a - digits_b).
        2073                 :          * If digits_b > digits_a: a*10e(digits_b - digits_a) <= b.
        2074                 :          */
        2075            3090 :         unsigned digitsa = NumDigitsBase10(auint);
        2076            3090 :         unsigned digitsb = NumDigitsBase10(buint);
        2077            3090 :         if (digitsa == digitsb)
        2078             970 :             *lessOrEqualp = (auint <= buint);
        2079            2120 :         else if (digitsa > digitsb)
        2080            1060 :             *lessOrEqualp = (uint64_t(auint) < uint64_t(buint) * powersOf10[digitsa - digitsb]);
        2081                 :         else /* if (digitsb > digitsa) */
        2082            1060 :             *lessOrEqualp = (uint64_t(auint) * powersOf10[digitsb - digitsa] <= uint64_t(buint));
        2083                 :     }
        2084                 :
        2085            6125 :     return true;
        2086                 : }
        2087                 :
        2088                 : inline bool
        2089            9769 : CompareSubStringValues(JSContext *cx, const jschar *s1, size_t l1,
        2090                 :                        const jschar *s2, size_t l2, bool *lessOrEqualp)
        2091                 : {
        2092            9769 :     if (!JS_CHECK_OPERATION_LIMIT(cx))
        2093               0 :         return false;
        2094                 :
        2095                 :     int32_t result;
        2096            9769 :     if (!s1 || !s2 || !CompareChars(s1, l1, s2, l2, &result))
        2097               0 :         return false;
        2098                 :
        2099            9769 :     *lessOrEqualp = (result <= 0);
        2100            9769 :     return true;
        2101                 : }
        2102                 :
        2103                 : struct SortComparatorStrings
        2104                 : {
        2105                 :     JSContext   *const cx;
        2106                 :
        2107             630 :     SortComparatorStrings(JSContext *cx)
        2108             630 :       : cx(cx) {}
        2109                 :
        2110           61488 :     bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) {
        2111           61488 :         return CompareStringValues(cx, a, b, lessOrEqualp);
        2112                 :     }
        2113                 : };
        2114                 :
        2115                 : struct SortComparatorLexicographicInt32
        2116                 : {
        2117                 :     JSContext   *const cx;
        2118                 :
        2119            6134 :     SortComparatorLexicographicInt32(JSContext *cx)
        2120            6134 :       : cx(cx) {}
        2121                 :
        2122            6125 :     bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) {
        2123            6125 :         return CompareLexicographicInt32(cx, a, b, lessOrEqualp);
        2124                 :     }
        2125                 : };
        2126                 :
        2127                 : struct StringifiedElement
        2128           39076 : {
        2129                 :     size_t charsBegin;
        2130                 :     size_t charsEnd;
        2131                 :     size_t elementIndex;
        2132                 : };
        2133                 :
        2134                 : struct SortComparatorStringifiedElements
        2135                 : {
        2136                 :     JSContext           *const cx;
        2137                 :     const StringBuffer  &sb;
        2138                 :
        2139            9769 :     SortComparatorStringifiedElements(JSContext *cx, const StringBuffer &sb)
        2140            9769 :       : cx(cx), sb(sb) {}
        2141                 :
        2142            9769 :     bool operator()(const StringifiedElement &a, const StringifiedElement &b, bool *lessOrEqualp) {
        2143            9769 :         return CompareSubStringValues(cx, sb.begin() + a.charsBegin, a.charsEnd - a.charsBegin,
        2144            9769 :                                       sb.begin() + b.charsBegin, b.charsEnd - b.charsBegin,
        2145           29307 :                                       lessOrEqualp);
        2146                 :     }
        2147                 : };
        2148                 :
        2149                 : struct SortComparatorFunction
        2150                 : {
        2151                 :     JSContext          *const cx;
        2152                 :     const Value        &fval;
        2153                 :     InvokeArgsGuard    &ag;
        2154                 :
        2155           15939 :     SortComparatorFunction(JSContext *cx, const Value &fval, InvokeArgsGuard &ag)
        2156           15939 :       : cx(cx), fval(fval), ag(ag) { }
        2157                 :
        2158                 :     bool operator()(const Value &a, const Value &b, bool *lessOrEqualp);
        2159                 : };
        2160                 :
        2161                 : bool
        2162          253701 : SortComparatorFunction::operator()(const Value &a, const Value &b, bool *lessOrEqualp)
        2163                 : {
        2164                 :     /*
        2165                 :      * array_sort deals with holes and undefs on its own and they should not
        2166                 :      * come here.
        2167                 :      */
        2168          253701 :     JS_ASSERT(!a.isMagic() && !a.isUndefined());
        2169          253701 :     JS_ASSERT(!a.isMagic() && !b.isUndefined());
        2170                 :
        2171          253701 :     if (!JS_CHECK_OPERATION_LIMIT(cx))
        2172               0 :         return false;
        2173                 :
        2174          253701 :     if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 2, &ag))
        2175               0 :         return false;
        2176                 :
        2177          253701 :     ag.setCallee(fval);
        2178          253701 :     ag.thisv() = UndefinedValue();
        2179          253701 :     ag[0] = a;
        2180          253701 :     ag[1] = b;
        2181                 :
        2182          253701 :     if (!Invoke(cx, ag))
        2183               9 :         return false;
        2184                 :
        2185                 :     double cmp;
        2186          253692 :     if (!ToNumber(cx, ag.rval(), &cmp))
        2187               9 :         return false;
        2188                 :
        2189                 :     /*
        2190                 :      * XXX eport some kind of error here if cmp is NaN? ECMA talks about
        2191                 :      * 'consistent compare functions' that don't return NaN, but is silent
        2192                 :      * about what the result should be. So we currently ignore it.
        2193                 :      */
        2194          253683 :     *lessOrEqualp = (JSDOUBLE_IS_NaN(cmp) || cmp <= 0);
        2195          253683 :     return true;
        2196                 : }
        2197                 :
        2198                 : } /* namespace anonymous */
        2199                 :
        2200                 : JSBool
        2201           32607 : js::array_sort(JSContext *cx, unsigned argc, Value *vp)
        2202                 : {
        2203           32607 :     CallArgs args = CallArgsFromVp(argc, vp);
        2204                 :     Value fval;
        2205           32607 :     if (args.hasDefined(0)) {
        2206           15939 :         if (args[0].isPrimitive()) {
        2207               0 :             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SORT_ARG);
        2208               0 :             return false;
        2209                 :         }
        2210           15939 :         fval = args[0];     /* non-default compare function */
        2211                 :     } else {
        2212           16668 :         fval.setNull();
        2213                 :     }
        2214                 :
        2215           32607 :     JSObject *obj = ToObject(cx, &args.thisv());
        2216           32607 :     if (!obj)
        2217               0 :         return false;
        2218                 :
        2219                 :     uint32_t len;
        2220           32607 :     if (!js_GetLengthProperty(cx, obj, &len))
        2221               0 :         return false;
        2222           32607 :     if (len == 0) {
        2223             126 :         args.rval().setObject(*obj);
        2224             126 :         return true;
        2225                 :     }
        2226                 :
        2227                 :     /*
        2228                 :      * We need a temporary array of 2 * len Value to hold the array elements
        2229                 :      * and the scratch space for merge sort. Check that its size does not
        2230                 :      * overflow size_t, which would allow for indexing beyond the end of the
        2231                 :      * malloc'd vector.
        2232                 :      */
        2233                 : #if JS_BITS_PER_WORD == 32
        2234           32481 :     if (size_t(len) > size_t(-1) / (2 * sizeof(Value))) {
        2235               0 :         js_ReportAllocationOverflow(cx);
        2236               0 :         return false;
        2237                 :     }
        2238                 : #endif
        2239                 :
        2240                 :     /*
        2241                 :      * Initialize vec as a root. We will clear elements of vec one by
        2242                 :      * one while increasing the rooted amount of vec when we know that the
        2243                 :      * property at the corresponding index exists and its value must be rooted.
        2244                 :      *
        2245                 :      * In this way when sorting a huge mostly sparse array we will not
        2246                 :      * access the tail of vec corresponding to properties that do not
        2247                 :      * exist, allowing OS to avoiding committing RAM. See bug 330812.
        2248                 :      */
        2249                 :     size_t n, undefs;
        2250                 :     {
        2251           64962 :         AutoValueVector vec(cx);
        2252           32481 :         if (!vec.reserve(2 * size_t(len)))
        2253               0 :             return false;
        2254                 :
        2255                 :         /*
        2256                 :          * By ECMA 262, 15.4.4.11, a property that does not exist (which we
        2257                 :          * call a "hole") is always greater than an existing property with
        2258                 :          * value undefined and that is always greater than any other property.
        2259                 :          * Thus to sort holes and undefs we simply count them, sort the rest
        2260                 :          * of elements, append undefs after them and then make holes after
        2261                 :          * undefs.
        2262                 :          */
        2263           32481 :         undefs = 0;
        2264           32481 :         bool allStrings = true;
        2265           32481 :         bool allInts = true;
        2266          129852 :         for (uint32_t i = 0; i < len; i++) {
        2267           97371 :             if (!JS_CHECK_OPERATION_LIMIT(cx))
        2268               0 :                 return false;
        2269                 :
        2270                 :             /* Clear vec[newlen] before including it in the rooted set. */
        2271                 :             JSBool hole;
        2272                 :             Value v;
        2273           97371 :             if (!GetElement(cx, obj, i, &hole, &v))
        2274               0 :                 return false;
        2275           97371 :             if (hole)
        2276               0 :                 continue;
        2277           97371 :             if (v.isUndefined()) {
        2278              18 :                 ++undefs;
        2279              18 :                 continue;
        2280                 :             }
        2281           97353 :             vec.infallibleAppend(v);
        2282           97353 :             allStrings = allStrings && v.isString();
        2283           97353 :             allInts = allInts && v.isInt32();
        2284                 :         }
        2285                 :
        2286           32481 :         n = vec.length();
        2287           32481 :         if (n == 0) {
        2288               9 :             args.rval().setObject(*obj);
        2289               9 :             return true; /* The array has only holes and undefs. */
        2290                 :         }
        2291                 :
        2292           32472 :         JS_ALWAYS_TRUE(vec.resize(n * 2));
        2293                 :
        2294                 :         /* Here len == n + undefs + number_of_holes. */
        2295           32472 :         Value *result = vec.begin();
        2296           32472 :         if (fval.isNull()) {
        2297                 :             /*
        2298                 :              * Sort using the default comparator converting all elements to
        2299                 :              * strings.
        2300                 :              */
        2301           16533 :             if (allStrings) {
        2302             630 :                 if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorStrings(cx)))
        2303               0 :                     return false;
        2304           15903 :             } else if (allInts) {
        2305           12268 :                 if (!MergeSort(vec.begin(), n, vec.begin() + n,
        2306           12268 :                                SortComparatorLexicographicInt32(cx))) {
        2307               0 :                     return false;
        2308                 :                 }
        2309                 :             } else {
        2310                 :                 /*
        2311                 :                  * Convert all elements to a jschar array in StringBuffer.
        2312                 :                  * Store the index and length of each stringified element with
        2313                 :                  * the corresponding index of the element in the array. Sort
        2314                 :                  * the stringified elements and with this result order the
        2315                 :                  * original array.
        2316                 :                  */
        2317           19538 :                 StringBuffer sb(cx);
        2318           19538 :                 Vector<StringifiedElement, 0, TempAllocPolicy> strElements(cx);
        2319            9769 :                 if (!strElements.reserve(2 * n))
        2320               0 :                     return false;
        2321                 :
        2322            9769 :                 int cursor = 0;
        2323           29307 :                 for (size_t i = 0; i < n; i++) {
        2324           19538 :                     if (!JS_CHECK_OPERATION_LIMIT(cx))
        2325               0 :                         return false;
        2326                 :
        2327           19538 :                     if (!ValueToStringBuffer(cx, vec[i], sb))
        2328               0 :                         return false;
        2329                 :
        2330           19538 :                     StringifiedElement el = { cursor, sb.length(), i };
        2331           19538 :                     strElements.infallibleAppend(el);
        2332           19538 :                     cursor = sb.length();
        2333                 :                 }
        2334                 :
        2335                 :                 /* Resize strElements so we can perform the sorting */
        2336            9769 :                 JS_ALWAYS_TRUE(strElements.resize(2 * n));
        2337                 :
        2338           19538 :                 if (!MergeSort(strElements.begin(), n, strElements.begin() + n,
        2339           19538 :                                SortComparatorStringifiedElements(cx, sb))) {
        2340               0 :                     return false;
        2341                 :                 }
        2342                 :
        2343                 :                 /* Order vec[n:2n-1] using strElements.index */
        2344           29307 :                 for (size_t i = 0; i < n; i ++)
        2345           19538 :                     vec[n + i] = vec[strElements[i].elementIndex];
        2346                 :
        2347           19538 :                 result = vec.begin() + n;
        2348                 :             }
        2349                 :         } else {
        2350           31878 :             InvokeArgsGuard args;
        2351           31878 :             if (!MergeSort(vec.begin(), n, vec.begin() + n,
        2352           31878 :                            SortComparatorFunction(cx, fval, args))) {
        2353              18 :                 return false;
        2354                 :             }
        2355                 :         }
        2356                 :
        2357           32454 :         if (!InitArrayElements(cx, obj, 0, uint32_t(n), result, DontUpdateTypes))
        2358               0 :             return false;
        2359                 :     }
        2360                 :
        2361                 :     /* Set undefs that sorted after the rest of elements. */
        2362           64908 :     while (undefs != 0) {
        2363               0 :         --undefs;
        2364               0 :         if (!JS_CHECK_OPERATION_LIMIT(cx) || !SetArrayElement(cx, obj, n++, UndefinedValue()))
        2365               0 :             return false;
        2366                 :     }
        2367                 :
        2368                 :     /* Re-create any holes that sorted to the end of the array. */
        2369           64908 :     while (len > n) {
        2370               0 :         if (!JS_CHECK_OPERATION_LIMIT(cx) || DeleteArrayElement(cx, obj, --len, true) < 0)
        2371               0 :             return false;
        2372                 :     }
        2373           32454 :     args.rval().setObject(*obj);
        2374           32454 :     return true;
        2375                 : }
        2376                 :
        2377                 : /*
        2378                 :  * Perl-inspired push, pop, shift, unshift, and splice methods.
        2379                 :  */
        2380                 : static bool
        2381           10487 : array_push_slowly(JSContext *cx, JSObject *obj, CallArgs &args)
        2382                 : {
        2383                 :     uint32_t length;
        2384                 :
        2385           10487 :     if (!js_GetLengthProperty(cx, obj, &length))
        2386               0 :         return false;
        2387           10487 :     if (!InitArrayElements(cx, obj, length, args.length(), args.array(), UpdateTypes))
        2388               0 :         return false;
        2389                 :
        2390                 :     /* Per ECMA-262, return the new array length. */
        2391           10487 :     double newlength = length + double(args.length());
        2392           10487 :     args.rval().setNumber(newlength);
        2393           10487 :     return js_SetLengthProperty(cx, obj, newlength);
        2394                 : }
        2395                 :
        2396                 : static bool
        2397         5076433 : array_push1_dense(JSContext* cx, JSObject* obj, CallArgs &args)
        2398                 : {
        2399         5076433 :     JS_ASSERT(args.length() == 1);
        2400                 :
        2401         5076433 :     uint32_t length = obj->getArrayLength();
        2402         5076433 :     JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, 1);
        2403         5076433 :     if (result != JSObject::ED_OK) {
        2404             351 :         if (result == JSObject::ED_FAILED)
        2405               0 :             return false;
        2406             351 :         JS_ASSERT(result == JSObject::ED_SPARSE);
        2407             351 :         if (!obj->makeDenseArraySlow(cx))
        2408               0 :             return false;
        2409             351 :         return array_push_slowly(cx, obj, args);
        2410                 :     }
        2411                 :
        2412         5076082 :     obj->setDenseArrayLength(length + 1);
        2413         5076082 :     obj->setDenseArrayElementWithType(cx, length, args[0]);
        2414         5076082 :     args.rval().setNumber(obj->getArrayLength());
        2415         5076082 :     return true;
        2416                 : }
        2417                 :
        2418                 : JS_ALWAYS_INLINE JSBool
        2419          487065 : NewbornArrayPushImpl(JSContext *cx, JSObject *obj, const Value &v)
        2420                 : {
        2421          487065 :     JS_ASSERT(!v.isMagic());
        2422                 :
        2423          487065 :     uint32_t length = obj->getArrayLength();
        2424          487065 :     if (obj->isSlowArray()) {
        2425                 :         /* This can happen in one evil case. See bug 630377. */
        2426                 :         jsid id;
        2427               0 :         return IndexToId(cx, length, &id) &&
        2428               0 :                js_DefineProperty(cx, obj, id, &v, NULL, NULL, JSPROP_ENUMERATE);
        2429                 :     }
        2430                 :
        2431          487065 :     JS_ASSERT(obj->isDenseArray());
        2432          487065 :     JS_ASSERT(length <= obj->getDenseArrayCapacity());
        2433                 :
        2434          487065 :     if (!obj->ensureElements(cx, length + 1))
        2435               0 :         return false;
        2436                 :
        2437          487065 :     obj->setDenseArrayInitializedLength(length + 1);
        2438          487065 :     obj->setDenseArrayLength(length + 1);
        2439          487065 :     obj->initDenseArrayElementWithType(cx, length, v);
        2440          487065 :     return true;
        2441                 : }
        2442                 :
        2443                 : JSBool
        2444          487065 : js_NewbornArrayPush(JSContext *cx, JSObject *obj, const Value &vp)
        2445                 : {
        2446          487065 :     return NewbornArrayPushImpl(cx, obj, vp);
        2447                 : }
        2448                 :
        2449                 : JSBool
        2450         5086569 : js::array_push(JSContext *cx, unsigned argc, Value *vp)
        2451                 : {
        2452         5086569 :     CallArgs args = CallArgsFromVp(argc, vp);
        2453         5086569 :     JSObject *obj = ToObject(cx, &args.thisv());
        2454         5086569 :     if (!obj)
        2455               0 :         return false;
        2456                 :
        2457                 :     /* Insist on one argument and obj of the expected class. */
        2458         5086569 :     if (args.length() != 1 || !obj->isDenseArray())
        2459           10136 :         return array_push_slowly(cx, obj, args);
        2460                 :
        2461         5076433 :     return array_push1_dense(cx, obj, args);
        2462                 : }
        2463                 :
        2464                 : static JSBool
        2465              45 : array_pop_slowly(JSContext *cx, JSObject* obj, CallArgs &args)
        2466                 : {
        2467                 :     uint32_t index;
        2468              45 :     if (!js_GetLengthProperty(cx, obj, &index))
        2469               0 :         return false;
        2470                 :
        2471              45 :     if (index == 0) {
        2472               9 :         args.rval().setUndefined();
        2473               9 :         return js_SetLengthProperty(cx, obj, index);
        2474                 :     }
        2475                 :
        2476              36 :     index--;
        2477                 :
        2478                 :     JSBool hole;
        2479                 :     Value elt;
        2480              36 :     if (!GetElement(cx, obj, index, &hole, &elt))
        2481               0 :         return false;
        2482                 :
        2483              36 :     if (!hole && DeleteArrayElement(cx, obj, index, true) < 0)
        2484               0 :         return false;
        2485                 :
        2486              36 :     args.rval() = elt;
        2487              36 :     return js_SetLengthProperty(cx, obj, index);
        2488                 : }
        2489                 :
        2490                 : static JSBool
        2491            8189 : array_pop_dense(JSContext *cx, JSObject* obj, CallArgs &args)
        2492                 : {
        2493            8189 :     uint32_t index = obj->getArrayLength();
        2494            8189 :     if (index == 0) {
        2495             420 :         args.rval().setUndefined();
        2496             420 :         return JS_TRUE;
        2497                 :     }
        2498                 :
        2499            7769 :     index--;
        2500                 :
        2501                 :     JSBool hole;
        2502                 :     Value elt;
        2503            7769 :     if (!GetElement(cx, obj, index, &hole, &elt))
        2504               0 :         return JS_FALSE;
        2505                 :
        2506            7769 :     if (!hole && DeleteArrayElement(cx, obj, index, true) < 0)
        2507               0 :         return JS_FALSE;
        2508            7769 :     if (obj->getDenseArrayInitializedLength() > index)
        2509            7742 :         obj->setDenseArrayInitializedLength(index);
        2510                 :
        2511            7769 :     obj->setArrayLength(cx, index);
        2512                 :
        2513            7769 :     args.rval() = elt;
        2514            7769 :     return JS_TRUE;
        2515                 : }
        2516                 :
        2517                 : JSBool
        2518            8234 : js::array_pop(JSContext *cx, unsigned argc, Value *vp)
        2519                 : {
        2520            8234 :     CallArgs args = CallArgsFromVp(argc, vp);
        2521            8234 :     JSObject *obj = ToObject(cx, &args.thisv());
        2522            8234 :     if (!obj)
        2523               0 :         return false;
        2524            8234 :     if (obj->isDenseArray())
        2525            8189 :         return array_pop_dense(cx, obj, args);
        2526              45 :     return array_pop_slowly(cx, obj, args);
        2527                 : }
        2528                 :
        2529                 : #ifdef JS_METHODJIT
        2530                 : void JS_FASTCALL
        2531              66 : mjit::stubs::ArrayShift(VMFrame &f)
        2532                 : {
        2533              66 :     JSObject *obj = &f.regs.sp[-1].toObject();
        2534              66 :     JS_ASSERT(obj->isDenseArray());
        2535                 :
        2536                 :     /*
        2537                 :      * At this point the length and initialized length have already been
        2538                 :      * decremented and the result fetched, so just shift the array elements
        2539                 :      * themselves.
        2540                 :      */
        2541              66 :     uint32_t initlen = obj->getDenseArrayInitializedLength();
        2542              66 :     obj->moveDenseArrayElementsUnbarriered(0, 1, initlen);
        2543              66 : }
        2544                 : #endif /* JS_METHODJIT */
        2545                 :
        2546                 : JSBool
        2547            1613 : js::array_shift(JSContext *cx, unsigned argc, Value *vp)
        2548                 : {
        2549            1613 :     CallArgs args = CallArgsFromVp(argc, vp);
        2550            1613 :     JSObject *obj = ToObject(cx, &args.thisv());
        2551            1613 :     if (!obj)
        2552               0 :         return JS_FALSE;
        2553                 :
        2554                 :     uint32_t length;
        2555            1613 :     if (!js_GetLengthProperty(cx, obj, &length))
        2556               0 :         return JS_FALSE;
        2557                 :
        2558            1613 :     if (length == 0) {
        2559             428 :         args.rval().setUndefined();
        2560                 :     } else {
        2561            1185 :         length--;
        2562                 :
        2563            3546 :         if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
        2564            1185 :             length < obj->getDenseArrayCapacity() &&
        2565            1176 :             0 < obj->getDenseArrayInitializedLength()) {
        2566            1176 :             args.rval() = obj->getDenseArrayElement(0);
        2567            1176 :             if (args.rval().isMagic(JS_ARRAY_HOLE))
        2568              27 :                 args.rval().setUndefined();
        2569            1176 :             obj->moveDenseArrayElements(0, 1, obj->getDenseArrayInitializedLength() - 1);
        2570            1176 :             obj->setDenseArrayInitializedLength(obj->getDenseArrayInitializedLength() - 1);
        2571            1176 :             obj->setArrayLength(cx, length);
        2572            1176 :             if (!js_SuppressDeletedProperty(cx, obj, INT_TO_JSID(length)))
        2573               0 :                 return JS_FALSE;
        2574            1176 :             return JS_TRUE;
        2575                 :         }
        2576                 :
        2577                 :         JSBool hole;
        2578               9 :         if (!GetElement(cx, obj, 0u, &hole, &args.rval()))
        2579               0 :             return JS_FALSE;
        2580                 :
        2581                 :         /* Slide down the array above the first element. */
        2582              18 :         AutoValueRooter tvr(cx);
        2583              63 :         for (uint32_t i = 0; i < length; i++) {
        2584             162 :             if (!JS_CHECK_OPERATION_LIMIT(cx) ||
        2585              54 :                 !GetElement(cx, obj, i + 1, &hole, tvr.addr()) ||
        2586              54 :                 !SetOrDeleteArrayElement(cx, obj, i, hole, tvr.value())) {
        2587               0 :                 return JS_FALSE;
        2588                 :             }
        2589                 :         }
        2590                 :
        2591                 :         /* Delete the only or last element when it exists. */
        2592               9 :         if (!hole && DeleteArrayElement(cx, obj, length, true) < 0)
        2593               0 :             return JS_FALSE;
        2594                 :     }
        2595             437 :     return js_SetLengthProperty(cx, obj, length);
        2596                 : }
        2597                 :
        2598                 : static JSBool
        2599             486 : array_unshift(JSContext *cx, unsigned argc, Value *vp)
        2600                 : {
        2601             486 :     CallArgs args = CallArgsFromVp(argc, vp);
        2602             486 :     JSObject *obj = ToObject(cx, &args.thisv());
        2603             486 :     if (!obj)
        2604               0 :         return false;
        2605                 :
        2606                 :     uint32_t length;
        2607             486 :     if (!js_GetLengthProperty(cx, obj, &length))
        2608               0 :         return JS_FALSE;
        2609                 :
        2610             486 :     double newlen = length;
        2611             486 :     if (args.length() > 0) {
        2612                 :         /* Slide up the array to make room for all args at the bottom. */
        2613             486 :         if (length > 0) {
        2614             459 :             bool optimized = false;
        2615                 :             do {
        2616             459 :                 if (!obj->isDenseArray())
        2617               0 :                     break;
        2618             459 :                 if (js_PrototypeHasIndexedProperties(cx, obj))
        2619               0 :                     break;
        2620             459 :                 JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, args.length());
        2621             459 :                 if (result != JSObject::ED_OK) {
        2622             351 :                     if (result == JSObject::ED_FAILED)
        2623               0 :                         return false;
        2624             351 :                     JS_ASSERT(result == JSObject::ED_SPARSE);
        2625             351 :                     break;
        2626                 :                 }
        2627             108 :                 obj->moveDenseArrayElements(args.length(), 0, length);
        2628             216 :                 for (uint32_t i = 0; i < args.length(); i++)
        2629             108 :                     obj->setDenseArrayElement(i, MagicValue(JS_ARRAY_HOLE));
        2630             108 :                 optimized = true;
        2631                 :             } while (false);
        2632                 :
        2633             459 :             if (!optimized) {
        2634             351 :                 double last = length;
        2635             351 :                 double upperIndex = last + args.length();
        2636             702 :                 AutoValueRooter tvr(cx);
        2637           96525 :                 do {
        2638           96525 :                     --last, --upperIndex;
        2639                 :                     JSBool hole;
        2640          289575 :                     if (!JS_CHECK_OPERATION_LIMIT(cx) ||
        2641           96525 :                         !GetElement(cx, obj, last, &hole, tvr.addr()) ||
        2642           96525 :                         !SetOrDeleteArrayElement(cx, obj, upperIndex, hole, tvr.value())) {
        2643               0 :                         return JS_FALSE;
        2644                 :                     }
        2645                 :                 } while (last != 0);
        2646                 :             }
        2647                 :         }
        2648                 :
        2649                 :         /* Copy from args to the bottom of the array. */
        2650             486 :         if (!InitArrayElements(cx, obj, 0, args.length(), args.array(), UpdateTypes))
        2651               0 :             return JS_FALSE;
        2652                 :
        2653             486 :         newlen += args.length();
        2654                 :     }
        2655             486 :     if (!js_SetLengthProperty(cx, obj, newlen))
        2656               0 :         return JS_FALSE;
        2657                 :
        2658                 :     /* Follow Perl by returning the new array length. */
        2659             486 :     args.rval().setNumber(newlen);
        2660             486 :     return JS_TRUE;
        2661                 : }
        2662                 :
        2663                 : static inline void
        2664            4377 : TryReuseArrayType(JSObject *obj, JSObject *nobj)
        2665                 : {
        2666                 :     /*
        2667                 :      * Try to change the type of a newly created array nobj to the same type
        2668                 :      * as obj. This can only be performed if the original object is an array
        2669                 :      * and has the same prototype.
        2670                 :      */
        2671            4377 :     JS_ASSERT(nobj->isDenseArray());
        2672            4377 :     JS_ASSERT(nobj->getProto()->hasNewType(nobj->type()));
        2673                 :
        2674            4377 :     if (obj->isArray() && !obj->hasSingletonType() && obj->getProto() == nobj->getProto())
        2675            3531 :         nobj->setType(obj->type());
        2676            4377 : }
        2677                 :
        2678                 : /*
        2679                 :  * Returns true if this is a dense array whose |count| properties starting from
        2680                 :  * |startingIndex| may be accessed (get, set, delete) directly through its
        2681                 :  * contiguous vector of elements without fear of getters, setters, etc. along
        2682                 :  * the prototype chain, or of enumerators requiring notification of
        2683                 :  * modifications.
        2684                 :  */
        2685                 : static inline bool
        2686            1503 : CanOptimizeForDenseStorage(JSObject *arr, uint32_t startingIndex, uint32_t count, JSContext *cx)
        2687                 : {
        2688                 :     /* If the desired properties overflow dense storage, we can't optimize. */
        2689            1503 :     if (UINT32_MAX - startingIndex < count)
        2690               0 :         return false;
        2691                 :
        2692                 :     /* There's no optimizing possible if it's not a dense array. */
        2693            1503 :     if (!arr->isDenseArray())
        2694             684 :         return false;
        2695                 :
        2696                 :     /*
        2697                 :      * Don't optimize if the array might be in the midst of iteration.  We
        2698                 :      * rely on this to be able to safely move dense array elements around with
        2699                 :      * just a memmove (see JSObject::moveDenseArrayElements), without worrying
        2700                 :      * about updating any in-progress enumerators for properties implicitly
        2701                 :      * deleted if a hole is moved from one location to another location not yet
        2702                 :      * visited.  See bug 690622.
        2703                 :      *
        2704                 :      * Another potential wrinkle: what if the enumeration is happening on an
        2705                 :      * object which merely has |arr| on its prototype chain?  It turns out this
        2706                 :      * case can't happen, because any dense array used as the prototype of
        2707                 :      * another object is first slowified, for type inference's sake.
        2708                 :      */
        2709             819 :     if (JS_UNLIKELY(arr->getType(cx)->hasAllFlags(OBJECT_FLAG_ITERATED)))
        2710             404 :         return false;
        2711                 :
        2712                 :     /* Now just watch out for getters and setters along the prototype chain. */
        2713             415 :     return !js_PrototypeHasIndexedProperties(cx, arr) &&
        2714             415 :            startingIndex + count <= arr->getDenseArrayInitializedLength();
        2715                 : }
        2716                 :
        2717                 : /* ES5 15.4.4.12. */
        2718                 : static JSBool
        2719             783 : array_splice(JSContext *cx, unsigned argc, Value *vp)
        2720                 : {
        2721             783 :     CallArgs args = CallArgsFromVp(argc, vp);
        2722                 :
        2723                 :     /* Step 1. */
        2724             783 :     JSObject *obj = ToObject(cx, &args.thisv());
        2725             783 :     if (!obj)
        2726               0 :         return false;
        2727                 :
        2728                 :     /* Steps 3-4. */
        2729                 :     uint32_t len;
        2730             783 :     if (!js_GetLengthProperty(cx, obj, &len))
        2731              18 :         return false;
        2732                 :
        2733                 :     /* Step 5. */
        2734                 :     double relativeStart;
        2735             765 :     if (!ToInteger(cx, argc >= 1 ? args[0] : UndefinedValue(), &relativeStart))
        2736               0 :         return false;
        2737                 :
        2738                 :     /* Step 6. */
        2739                 :     uint32_t actualStart;
        2740             765 :     if (relativeStart < 0)
        2741               0 :         actualStart = JS_MAX(len + relativeStart, 0);
        2742                 :     else
        2743             765 :         actualStart = JS_MIN(relativeStart, len);
        2744                 :
        2745                 :     /* Step 7. */
        2746                 :     uint32_t actualDeleteCount;
        2747             765 :     if (argc != 1) {
        2748                 :         double deleteCountDouble;
        2749             756 :         if (!ToInteger(cx, argc >= 2 ? args[1] : Int32Value(0), &deleteCountDouble))
        2750               0 :             return false;
        2751             756 :         actualDeleteCount = JS_MIN(JS_MAX(deleteCountDouble, 0), len - actualStart);
        2752                 :     } else {
        2753                 :         /*
        2754                 :          * Non-standard: if start was specified but deleteCount was omitted,
        2755                 :          * delete to the end of the array.  See bug 668024 for discussion.
        2756                 :          */
        2757               9 :         actualDeleteCount = len - actualStart;
        2758                 :     }
        2759                 :
        2760             765 :     JS_ASSERT(len - actualStart >= actualDeleteCount);
        2761                 :
        2762                 :     /* Steps 2, 8-9. */
        2763                 :     JSObject *arr;
        2764             765 :     if (CanOptimizeForDenseStorage(obj, actualStart, actualDeleteCount, cx)) {
        2765                 :         arr = NewDenseCopiedArray(cx, actualDeleteCount,
        2766             307 :                                   obj->getDenseArrayElements() + actualStart);
        2767             307 :         if (!arr)
        2768               0 :             return false;
        2769             307 :         TryReuseArrayType(obj, arr);
        2770                 :     } else {
        2771             458 :         arr = NewDenseAllocatedArray(cx, actualDeleteCount);
        2772             458 :         if (!arr)
        2773               0 :             return false;
        2774             458 :         TryReuseArrayType(obj, arr);
        2775                 :
        2776            1129 :         for (uint32_t k = 0; k < actualDeleteCount; k++) {
        2777                 :             JSBool hole;
        2778                 :             Value fromValue;
        2779            2704 :             if (!JS_CHECK_OPERATION_LIMIT(cx) ||
        2780             689 :                 !GetElement(cx, obj, actualStart + k, &hole, &fromValue) ||
        2781            1326 :                 (!hole && !arr->defineElement(cx, k, fromValue)))
        2782                 :             {
        2783              18 :                 return false;
        2784                 :             }
        2785                 :         }
        2786                 :     }
        2787                 :
        2788                 :     /* Step 11. */
        2789             747 :     uint32_t itemCount = (argc >= 2) ? (argc - 2) : 0;
        2790                 :
        2791             747 :     if (itemCount < actualDeleteCount) {
        2792                 :         /* Step 12: the array is being shrunk. */
        2793             225 :         uint32_t sourceIndex = actualStart + actualDeleteCount;
        2794             225 :         uint32_t targetIndex = actualStart + itemCount;
        2795             225 :         uint32_t finalLength = len - actualDeleteCount + itemCount;
        2796                 :
        2797             225 :         if (CanOptimizeForDenseStorage(obj, 0, len, cx)) {
        2798                 :             /* Steps 12(a)-(b). */
        2799              52 :             obj->moveDenseArrayElements(targetIndex, sourceIndex, len - sourceIndex);
        2800                 :
        2801                 :             /*
        2802                 :              * Update the initialized length. Do so before shrinking so that we
        2803                 :              * can apply the write barrier to the old slots.
        2804                 :              */
        2805              52 :             if (cx->typeInferenceEnabled())
        2806              52 :                 obj->setDenseArrayInitializedLength(finalLength);
        2807                 :
        2808                 :             /* Steps 12(c)-(d). */
        2809              52 :             obj->shrinkElements(cx, finalLength);
        2810                 :
        2811                 :             /* Fix running enumerators for the deleted items. */
        2812              52 :             if (!js_SuppressDeletedElements(cx, obj, finalLength, len))
        2813               0 :                 return false;
        2814                 :         } else {
        2815                 :             /*
        2816                 :              * This is all very slow if the length is very large. We don't yet
        2817                 :              * have the ability to iterate in sorted order, so we just do the
        2818                 :              * pessimistic thing and let JS_CHECK_OPERATION_LIMIT handle the
        2819                 :              * fallout.
        2820                 :              */
        2821                 :
        2822                 :             /* Steps 12(a)-(b). */
        2823             569 :             for (uint32_t from = sourceIndex, to = targetIndex; from < len; from++, to++) {
        2824                 :                 JSBool hole;
        2825                 :                 Value fromValue;
        2826            1278 :                 if (!JS_CHECK_OPERATION_LIMIT(cx) ||
        2827             432 :                     !GetElement(cx, obj, from, &hole, &fromValue) ||
        2828             414 :                     !SetOrDeleteArrayElement(cx, obj, to, hole, fromValue))
        2829                 :                 {
        2830              36 :                     return false;
        2831                 :                 }
        2832                 :             }
        2833                 :
        2834                 :             /* Steps 12(c)-(d). */
        2835             439 :             for (uint32_t k = len; k > finalLength; k--) {
        2836             320 :                 if (DeleteArrayElement(cx, obj, k - 1, true) < 0)
        2837              18 :                     return false;
        2838                 :             }
        2839                 :         }
        2840             522 :     } else if (itemCount > actualDeleteCount) {
        2841                 :         /* Step 13. */
        2842                 :
        2843                 :         /*
        2844                 :          * Optimize only if the array is already dense and we can extend it to
        2845                 :          * its new length.
        2846                 :          */
        2847             513 :         if (obj->isDenseArray()) {
        2848                 :             JSObject::EnsureDenseResult res =
        2849                 :                 obj->ensureDenseArrayElements(cx, obj->getArrayLength(),
        2850             459 :                                               itemCount - actualDeleteCount);
        2851             459 :             if (res == JSObject::ED_FAILED)
        2852               0 :                 return false;
        2853                 :
        2854             459 :             if (res == JSObject::ED_SPARSE) {
        2855             369 :                 if (!obj->makeDenseArraySlow(cx))
        2856               0 :                     return false;
        2857                 :             } else {
        2858              90 :                 JS_ASSERT(res == JSObject::ED_OK);
        2859                 :             }
        2860                 :         }
        2861                 :
        2862             513 :         if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) {
        2863                 :             obj->moveDenseArrayElements(actualStart + itemCount,
        2864                 :                                         actualStart + actualDeleteCount,
        2865              50 :                                         len - (actualStart + actualDeleteCount));
        2866                 :
        2867              50 :             if (cx->typeInferenceEnabled())
        2868              50 :                 obj->setDenseArrayInitializedLength(len + itemCount - actualDeleteCount);
        2869                 :         } else {
        2870          110222 :             for (double k = len - actualDeleteCount; k > actualStart; k--) {
        2871          109795 :                 double from = k + actualDeleteCount - 1;
        2872          109795 :                 double to = k + itemCount - 1;
        2873                 :
        2874                 :                 JSBool hole;
        2875                 :                 Value fromValue;
        2876          329367 :                 if (!JS_CHECK_OPERATION_LIMIT(cx) ||
        2877          109795 :                     !GetElement(cx, obj, from, &hole, &fromValue) ||
        2878          109777 :                     !SetOrDeleteArrayElement(cx, obj, to, hole, fromValue))
        2879                 :                 {
        2880              36 :                     return false;
        2881                 :                 }
        2882                 :             }
        2883                 :         }
        2884                 :     }
        2885                 :
        2886                 :     /* Step 10. */
        2887             657 :     Value *items = args.array() + 2;
        2888                 :
        2889                 :     /* Steps 14-15. */
        2890            2124 :     for (uint32_t k = actualStart, i = 0; i < itemCount; i++, k++) {
        2891            1476 :         if (!SetArrayElement(cx, obj, k, items[i]))
        2892               9 :             return false;
        2893                 :     }
        2894                 :
        2895                 :     /* Step 16. */
        2896             648 :     double finalLength = double(len) - actualDeleteCount + itemCount;
        2897             648 :     if (!js_SetLengthProperty(cx, obj, finalLength))
        2898               0 :         return false;
        2899                 :
        2900                 :     /* Step 17. */
        2901             648 :     args.rval().setObject(*arr);
        2902             648 :     return true;
        2903                 : }
        2904                 :
        2905                 : #ifdef JS_METHODJIT
        2906                 : void JS_FASTCALL
        2907             114 : mjit::stubs::ArrayConcatTwoArrays(VMFrame &f)
        2908                 : {
        2909             114 :     JSObject *result = &f.regs.sp[-3].toObject();
        2910             114 :     JSObject *obj1 = &f.regs.sp[-2].toObject();
        2911             114 :     JSObject *obj2 = &f.regs.sp[-1].toObject();
        2912                 :
        2913             114 :     JS_ASSERT(result->isDenseArray() && obj1->isDenseArray() && obj2->isDenseArray());
        2914                 :
        2915             114 :     uint32_t initlen1 = obj1->getDenseArrayInitializedLength();
        2916             114 :     JS_ASSERT(initlen1 == obj1->getArrayLength());
        2917                 :
        2918             114 :     uint32_t initlen2 = obj2->getDenseArrayInitializedLength();
        2919             114 :     JS_ASSERT(initlen2 == obj2->getArrayLength());
        2920                 :
        2921                 :     /* No overflow here due to nslots limit. */
        2922             114 :     uint32_t len = initlen1 + initlen2;
        2923                 :
        2924             114 :     if (!result->ensureElements(f.cx, len))
        2925               0 :         THROW();
        2926                 :
        2927             114 :     JS_ASSERT(!result->getDenseArrayInitializedLength());
        2928             114 :     result->setDenseArrayInitializedLength(len);
        2929                 :
        2930             114 :     result->initDenseArrayElements(0, obj1->getDenseArrayElements(), initlen1);
        2931             114 :     result->initDenseArrayElements(initlen1, obj2->getDenseArrayElements(), initlen2);
        2932                 :
        2933             114 :     result->setDenseArrayLength(len);
        2934                 : }
        2935                 : #endif /* JS_METHODJIT */
        2936                 :
        2937                 : /*
        2938                 :  * Python-esque sequence operations.
        2939                 :  */
        2940                 : JSBool
        2941             561 : js::array_concat(JSContext *cx, unsigned argc, Value *vp)
        2942                 : {
        2943                 :     /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
        2944             561 :     Value *p = JS_ARGV(cx, vp) - 1;
        2945                 :
        2946                 :     /* Create a new Array object and root it using *vp. */
        2947             561 :     JSObject *aobj = ToObject(cx, &vp[1]);
        2948             561 :     if (!aobj)
        2949               0 :         return false;
        2950                 :
        2951                 :     JSObject *nobj;
        2952                 :     uint32_t length;
        2953             561 :     if (aobj->isDenseArray()) {
        2954             561 :         length = aobj->getArrayLength();
        2955             561 :         const Value *vector = aobj->getDenseArrayElements();
        2956             561 :         uint32_t initlen = aobj->getDenseArrayInitializedLength();
        2957             561 :         nobj = NewDenseCopiedArray(cx, initlen, vector);
        2958             561 :         if (!nobj)
        2959               0 :             return JS_FALSE;
        2960             561 :         TryReuseArrayType(aobj, nobj);
        2961             561 :         nobj->setArrayLength(cx, length);
        2962             561 :         vp->setObject(*nobj);
        2963             561 :         if (argc == 0)
        2964              18 :             return JS_TRUE;
        2965             543 :         argc--;
        2966             543 :         p++;
        2967                 :     } else {
        2968               0 :         nobj = NewDenseEmptyArray(cx);
        2969               0 :         if (!nobj)
        2970               0 :             return JS_FALSE;
        2971               0 :         vp->setObject(*nobj);
        2972               0 :         length = 0;
        2973                 :     }
        2974                 :
        2975                 :     /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */
        2976            1122 :     for (unsigned i = 0; i <= argc; i++) {
        2977             579 :         if (!JS_CHECK_OPERATION_LIMIT(cx))
        2978               0 :             return false;
        2979             579 :         const Value &v = p[i];
        2980             579 :         if (v.isObject()) {
        2981             579 :             JSObject &obj = v.toObject();
        2982             579 :             if (ObjectClassIs(obj, ESClass_Array, cx)) {
        2983                 :                 uint32_t alength;
        2984             552 :                 if (!js_GetLengthProperty(cx, &obj, &alength))
        2985               0 :                     return false;
        2986            3723 :                 for (uint32_t slot = 0; slot < alength; slot++) {
        2987                 :                     JSBool hole;
        2988                 :                     Value tmp;
        2989            3171 :                     if (!JS_CHECK_OPERATION_LIMIT(cx) || !GetElement(cx, &obj, slot, &hole, &tmp))
        2990               0 :                         return false;
        2991                 :
        2992                 :                     /*
        2993                 :                      * Per ECMA 262, 15.4.4.4, step 9, ignore nonexistent
        2994                 :                      * properties.
        2995                 :                      */
        2996            3171 :                     if (!hole && !SetArrayElement(cx, nobj, length + slot, tmp))
        2997               0 :                         return false;
        2998                 :                 }
        2999             552 :                 length += alength;
        3000             552 :                 continue;
        3001                 :             }
        3002                 :         }
        3003                 :
        3004              27 :         if (!SetArrayElement(cx, nobj, length, v))
        3005               0 :             return false;
        3006              27 :         length++;
        3007                 :     }
        3008                 :
        3009             543 :     return js_SetLengthProperty(cx, nobj, length);
        3010                 : }
        3011                 :
        3012                 : static JSBool
        3013            3051 : array_slice(JSContext *cx, unsigned argc, Value *vp)
        3014                 : {
        3015                 :     JSObject *nobj;
        3016                 :     uint32_t length, begin, end, slot;
        3017                 :     JSBool hole;
        3018                 :
        3019            3051 :     CallArgs args = CallArgsFromVp(argc, vp);
        3020                 :
        3021            3051 :     JSObject *obj = ToObject(cx, &args.thisv());
        3022            3051 :     if (!obj)
        3023               0 :         return false;
        3024                 :
        3025            3051 :     if (!js_GetLengthProperty(cx, obj, &length))
        3026               0 :         return JS_FALSE;
        3027            3051 :     begin = 0;
        3028            3051 :     end = length;
        3029                 :
        3030            3051 :     if (args.length() > 0) {
        3031                 :         double d;
        3032            3033 :         if (!ToInteger(cx, args[0], &d))
        3033               0 :             return false;
        3034            3033 :         if (d < 0) {
        3035               0 :             d += length;
        3036               0 :             if (d < 0)
        3037               0 :                 d = 0;
        3038            3033 :         } else if (d > length) {
        3039               0 :             d = length;
        3040                 :         }
        3041            3033 :         begin = (uint32_t)d;
        3042                 :
        3043            3033 :         if (args.hasDefined(1)) {
        3044              36 :             if (!ToInteger(cx, args[1], &d))
        3045               0 :                 return false;
        3046              36 :             if (d < 0) {
        3047               0 :                 d += length;
        3048               0 :                 if (d < 0)
        3049               0 :                     d = 0;
        3050              36 :             } else if (d > length) {
        3051               0 :                 d = length;
        3052                 :             }
        3053              36 :             end = (uint32_t)d;
        3054                 :         }
        3055                 :     }
        3056                 :
        3057            3051 :     if (begin > end)
        3058               0 :         begin = end;
        3059                 :
        3060            5247 :     if (obj->isDenseArray() && end <= obj->getDenseArrayInitializedLength() &&
        3061            2196 :         !js_PrototypeHasIndexedProperties(cx, obj)) {
        3062            2196 :         nobj = NewDenseCopiedArray(cx, end - begin, obj->getDenseArrayElements() + begin);
        3063            2196 :         if (!nobj)
        3064               0 :             return JS_FALSE;
        3065            2196 :         TryReuseArrayType(obj, nobj);
        3066            2196 :         args.rval().setObject(*nobj);
        3067            2196 :         return JS_TRUE;
        3068                 :     }
        3069                 :
        3070             855 :     nobj = NewDenseAllocatedArray(cx, end - begin);
        3071             855 :     if (!nobj)
        3072               0 :         return JS_FALSE;
        3073             855 :     TryReuseArrayType(obj, nobj);
        3074                 :
        3075            1710 :     AutoValueRooter tvr(cx);
        3076            2646 :     for (slot = begin; slot < end; slot++) {
        3077            3582 :         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
        3078            1791 :             !GetElement(cx, obj, slot, &hole, tvr.addr())) {
        3079               0 :             return JS_FALSE;
        3080                 :         }
        3081            1791 :         if (!hole && !SetArrayElement(cx, nobj, slot - begin, tvr.value()))
        3082               0 :             return JS_FALSE;
        3083                 :     }
        3084                 :
        3085             855 :     args.rval().setObject(*nobj);
        3086             855 :     return JS_TRUE;
        3087                 : }
        3088                 :
        3089                 : enum IndexOfKind {
        3090                 :     IndexOf,
        3091                 :     LastIndexOf
        3092                 : };
        3093                 :
        3094                 : static JSBool
        3095           15480 : array_indexOfHelper(JSContext *cx, IndexOfKind mode, CallArgs &args)
        3096                 : {
        3097                 :     uint32_t length, i, stop;
        3098                 :     Value tosearch;
        3099                 :     int direction;
        3100                 :     JSBool hole;
        3101                 :
        3102           15480 :     JSObject *obj = ToObject(cx, &args.thisv());
        3103           15480 :     if (!obj)
        3104               0 :         return false;
        3105           15480 :     if (!js_GetLengthProperty(cx, obj, &length))
        3106               0 :         return JS_FALSE;
        3107           15480 :     if (length == 0)
        3108             198 :         goto not_found;
        3109                 :
        3110           15282 :     if (args.length() <= 1) {
        3111           15282 :         i = (mode == LastIndexOf) ? length - 1 : 0;
        3112           15282 :         tosearch = (args.length() != 0) ? args[0] : UndefinedValue();
        3113                 :     } else {
        3114                 :         double start;
        3115                 :
        3116               0 :         tosearch = args[0];
        3117               0 :         if (!ToInteger(cx, args[1], &start))
        3118               0 :             return false;
        3119               0 :         if (start < 0) {
        3120               0 :             start += length;
        3121               0 :             if (start < 0) {
        3122               0 :                 if (mode == LastIndexOf)
        3123               0 :                     goto not_found;
        3124               0 :                 i = 0;
        3125                 :             } else {
        3126               0 :                 i = (uint32_t)start;
        3127                 :             }
        3128               0 :         } else if (start >= length) {
        3129               0 :             if (mode == IndexOf)
        3130               0 :                 goto not_found;
        3131               0 :             i = length - 1;
        3132                 :         } else {
        3133               0 :             i = (uint32_t)start;
        3134                 :         }
        3135                 :     }
        3136                 :
        3137           15282 :     if (mode == LastIndexOf) {
        3138               0 :         stop = 0;
        3139               0 :         direction = -1;
        3140                 :     } else {
        3141           15282 :         stop = length - 1;
        3142           15282 :         direction = 1;
        3143                 :     }
        3144                 :
        3145          459234 :     for (;;) {
        3146                 :         Value elt;
        3147          949032 :         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
        3148          474516 :             !GetElement(cx, obj, (uint32_t)i, &hole, &elt)) {
        3149               0 :             return JS_FALSE;
        3150                 :         }
        3151          474516 :         if (!hole) {
        3152                 :             bool equal;
        3153          474516 :             if (!StrictlyEqual(cx, elt, tosearch, &equal))
        3154               0 :                 return false;
        3155          474516 :             if (equal) {
        3156            4311 :                 args.rval().setNumber(i);
        3157            4311 :                 return true;
        3158                 :             }
        3159                 :         }
        3160          470205 :         if (i == stop)
        3161           10971 :             goto not_found;
        3162          459234 :         i += direction;
        3163                 :     }
        3164                 :
        3165                 :   not_found:
        3166           11169 :     args.rval().setInt32(-1);
        3167           11169 :     return JS_TRUE;
        3168                 : }
        3169                 :
        3170                 : static JSBool
        3171           15480 : array_indexOf(JSContext *cx, unsigned argc, Value *vp)
        3172                 : {
        3173           15480 :     CallArgs args = CallArgsFromVp(argc, vp);
        3174           15480 :     return array_indexOfHelper(cx, IndexOf, args);
        3175                 : }
        3176                 :
        3177                 : static JSBool
        3178               0 : array_lastIndexOf(JSContext *cx, unsigned argc, Value *vp)
        3179                 : {
        3180               0 :     CallArgs args = CallArgsFromVp(argc, vp);
        3181               0 :     return array_indexOfHelper(cx, LastIndexOf, args);
        3182                 : }
        3183                 :
        3184                 : /* ECMA 15.4.4.16-15.4.4.18. */
        3185                 : class ArrayForEachBehavior
        3186                 : {
        3187                 :   public:
        3188            2142 :     static bool shouldExit(Value &callval, Value *rval) { return false; }
        3189             972 :     static Value lateExitValue() { return UndefinedValue(); }
        3190                 : };
        3191                 :
        3192                 : class ArrayEveryBehavior
        3193                 : {
        3194                 :   public:
        3195             315 :     static bool shouldExit(Value &callval, Value *rval)
        3196                 :     {
        3197             315 :         if (!js_ValueToBoolean(callval)) {
        3198               0 :             *rval = BooleanValue(false);
        3199               0 :             return true;
        3200                 :         }
        3201             315 :         return false;
        3202                 :     }
        3203               9 :     static Value lateExitValue() { return BooleanValue(true); }
        3204                 : };
        3205                 :
        3206                 : class ArraySomeBehavior
        3207                 : {
        3208                 :   public:
        3209            9473 :     static bool shouldExit(Value &callval, Value *rval)
        3210                 :     {
        3211            9473 :         if (js_ValueToBoolean(callval)) {
        3212               9 :             *rval = BooleanValue(true);
        3213               9 :             return true;
        3214                 :         }
        3215            9464 :         return false;
        3216                 :     }
        3217            9464 :     static Value lateExitValue() { return BooleanValue(false); }
        3218                 : };
        3219                 :
        3220                 : template <class Behavior>
        3221                 : static inline bool
        3222           15509 : array_readonlyCommon(JSContext *cx, CallArgs &args)
        3223                 : {
        3224                 :     /* Step 1. */
        3225           15509 :     JSObject *obj = ToObject(cx, &args.thisv());
        3226           15509 :     if (!obj)
        3227               0 :         return false;
        3228                 :
        3229                 :     /* Step 2-3. */
        3230                 :     uint32_t len;
        3231           15509 :     if (!js_GetLengthProperty(cx, obj, &len))
        3232               0 :         return false;
        3233                 :
        3234                 :     /* Step 4. */
        3235           15509 :     if (args.length() == 0) {
        3236               0 :         js_ReportMissingArg(cx, args.calleev(), 0);
        3237               0 :         return false;
        3238                 :     }
        3239           15509 :     JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
        3240           15509 :     if (!callable)
        3241               0 :         return false;
        3242                 :
        3243                 :     /* Step 5. */
        3244           15509 :     Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
        3245                 :
        3246                 :     /* Step 6. */
        3247           15509 :     uint32_t k = 0;
        3248                 :
        3249                 :     /* Step 7. */
        3250           31018 :     InvokeArgsGuard ag;
        3251           42939 :     while (k < len) {
        3252           16985 :         if (!JS_CHECK_OPERATION_LIMIT(cx))
        3253               0 :             return false;
        3254                 :
        3255                 :         /* Step a, b, and c.i. */
        3256                 :         Value kValue;
        3257                 :         JSBool kNotPresent;
        3258           16985 :         if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
        3259               0 :             return false;
        3260                 :
        3261                 :         /* Step c.ii-iii. */
        3262           16985 :         if (!kNotPresent) {
        3263           16985 :             if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag))
        3264               0 :                 return false;
        3265           16985 :             ag.setCallee(ObjectValue(*callable));
        3266           16985 :             ag.thisv() = thisv;
        3267           16985 :             ag[0] = kValue;
        3268           16985 :             ag[1] = NumberValue(k);
        3269           16985 :             ag[2] = ObjectValue(*obj);
        3270           16985 :             if (!Invoke(cx, ag))
        3271            5055 :                 return false;
        3272                 :
        3273           11930 :             if (Behavior::shouldExit(ag.rval(), &args.rval()))
        3274               9 :                 return true;
        3275                 :         }
        3276                 :
        3277                 :         /* Step d. */
        3278           11921 :         k++;
        3279                 :     }
        3280                 :
        3281                 :     /* Step 8. */
        3282           10445 :     args.rval() = Behavior::lateExitValue();
        3283           10445 :     return true;
        3284                 :  }
        3285                 :
        3286                 : /* ES5 15.4.4.16. */
        3287                 : static JSBool
        3288               9 : array_every(JSContext *cx, unsigned argc, Value *vp)
        3289                 : {
        3290               9 :     CallArgs args = CallArgsFromVp(argc, vp);
        3291               9 :     return array_readonlyCommon<ArrayEveryBehavior>(cx, args);
        3292                 : }
        3293                 :
        3294                 : /* ES5 15.4.4.17. */
        3295                 : static JSBool
        3296           14528 : array_some(JSContext *cx, unsigned argc, Value *vp)
        3297                 : {
        3298           14528 :     CallArgs args = CallArgsFromVp(argc, vp);
        3299           14528 :     return array_readonlyCommon<ArraySomeBehavior>(cx, args);
        3300                 : }
        3301                 :
        3302                 : /* ES5 15.4.4.18. */
        3303                 : static JSBool
        3304             972 : array_forEach(JSContext *cx, unsigned argc, Value *vp)
        3305                 : {
        3306             972 :     CallArgs args = CallArgsFromVp(argc, vp);
        3307             972 :     return array_readonlyCommon<ArrayForEachBehavior>(cx, args);
        3308                 : }
        3309                 :
        3310                 : /* ES5 15.4.4.19. */
        3311                 : static JSBool
        3312            1251 : array_map(JSContext *cx, unsigned argc, Value *vp)
        3313                 : {
        3314            1251 :     CallArgs args = CallArgsFromVp(argc, vp);
        3315                 :
        3316                 :     /* Step 1. */
        3317            1251 :     JSObject *obj = ToObject(cx, &args.thisv());
        3318            1251 :     if (!obj)
        3319               0 :         return false;
        3320                 :
        3321                 :     /* Step 2-3. */
        3322                 :     uint32_t len;
        3323            1251 :     if (!js_GetLengthProperty(cx, obj, &len))
        3324               0 :         return false;
        3325                 :
        3326                 :     /* Step 4. */
        3327            1251 :     if (args.length() == 0) {
        3328               0 :         js_ReportMissingArg(cx, args.calleev(), 0);
        3329               0 :         return false;
        3330                 :     }
        3331            1251 :     JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
        3332            1251 :     if (!callable)
        3333               0 :         return false;
        3334                 :
        3335                 :     /* Step 5. */
        3336            1251 :     Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
        3337                 :
        3338                 :     /* Step 6. */
        3339            1251 :     JSObject *arr = NewDenseAllocatedArray(cx, len);
        3340            1251 :     if (!arr)
        3341               0 :         return false;
        3342            1251 :     TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array);
        3343            1251 :     if (!newtype)
        3344               0 :         return false;
        3345            1251 :     arr->setType(newtype);
        3346                 :
        3347                 :     /* Step 7. */
        3348            1251 :     uint32_t k = 0;
        3349                 :
        3350                 :     /* Step 8. */
        3351            2502 :     InvokeArgsGuard ag;
        3352            6723 :     while (k < len) {
        3353            4230 :         if (!JS_CHECK_OPERATION_LIMIT(cx))
        3354               0 :             return false;
        3355                 :
        3356                 :         /* Step a, b, and c.i. */
        3357                 :         JSBool kNotPresent;
        3358                 :         Value kValue;
        3359            4230 :         if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
        3360               0 :             return false;
        3361                 :
        3362                 :         /* Step c.ii-iii. */
        3363            4230 :         if (!kNotPresent) {
        3364            4230 :             if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag))
        3365               0 :                 return false;
        3366            4230 :             ag.setCallee(ObjectValue(*callable));
        3367            4230 :             ag.thisv() = thisv;
        3368            4230 :             ag[0] = kValue;
        3369            4230 :             ag[1] = NumberValue(k);
        3370            4230 :             ag[2] = ObjectValue(*obj);
        3371            4230 :             if (!Invoke(cx, ag))
        3372               9 :                 return false;
        3373            4221 :             if(!SetArrayElement(cx, arr, k, ag.rval()))
        3374               0 :                 return false;
        3375                 :         }
        3376                 :
        3377                 :         /* Step d. */
        3378            4221 :         k++;
        3379                 :     }
        3380                 :
        3381                 :     /* Step 9. */
        3382            1242 :     args.rval().setObject(*arr);
        3383            1242 :     return true;
        3384                 : }
        3385                 :
        3386                 : /* ES5 15.4.4.20. */
        3387                 : static JSBool
        3388             567 : array_filter(JSContext *cx, unsigned argc, Value *vp)
        3389                 : {
        3390             567 :     CallArgs args = CallArgsFromVp(argc, vp);
        3391                 :
        3392                 :     /* Step 1. */
        3393             567 :     JSObject *obj = ToObject(cx, &args.thisv());
        3394             567 :     if (!obj)
        3395               0 :         return false;
        3396                 :
        3397                 :     /* Step 2-3. */
        3398                 :     uint32_t len;
        3399             567 :     if (!js_GetLengthProperty(cx, obj, &len))
        3400               0 :         return false;
        3401                 :
        3402                 :     /* Step 4. */
        3403             567 :     if (args.length() == 0) {
        3404               0 :         js_ReportMissingArg(cx, args.calleev(), 0);
        3405               0 :         return false;
        3406                 :     }
        3407             567 :     JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
        3408             567 :     if (!callable)
        3409               0 :         return false;
        3410                 :
        3411                 :     /* Step 5. */
        3412             567 :     Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
        3413                 :
        3414                 :     /* Step 6. */
        3415             567 :     JSObject *arr = NewDenseAllocatedArray(cx, 0);
        3416             567 :     if (!arr)
        3417               0 :         return false;
        3418             567 :     TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array);
        3419             567 :     if (!newtype)
        3420               0 :         return false;
        3421             567 :     arr->setType(newtype);
        3422                 :
        3423                 :     /* Step 7. */
        3424             567 :     uint32_t k = 0;
        3425                 :
        3426                 :     /* Step 8. */
        3427             567 :     uint32_t to = 0;
        3428                 :
        3429                 :     /* Step 9. */
        3430            1134 :     InvokeArgsGuard ag;
        3431            5994 :     while (k < len) {
        3432            4860 :         if (!JS_CHECK_OPERATION_LIMIT(cx))
        3433               0 :             return false;
        3434                 :
        3435                 :         /* Step a, b, and c.i. */
        3436                 :         JSBool kNotPresent;
        3437                 :         Value kValue;
        3438            4860 :         if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
        3439               0 :             return false;
        3440                 :
        3441                 :         /* Step c.ii-iii. */
        3442            4860 :         if (!kNotPresent) {
        3443            4860 :             if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag))
        3444               0 :                 return false;
        3445            4860 :             ag.setCallee(ObjectValue(*callable));
        3446            4860 :             ag.thisv() = thisv;
        3447            4860 :             ag[0] = kValue;
        3448            4860 :             ag[1] = NumberValue(k);
        3449            4860 :             ag[2] = ObjectValue(*obj);
        3450            4860 :             if (!Invoke(cx, ag))
        3451               0 :                 return false;
        3452                 :
        3453            4860 :             if (js_ValueToBoolean(ag.rval())) {
        3454             936 :                 if(!SetArrayElement(cx, arr, to, kValue))
        3455               0 :                     return false;
        3456             936 :                 to++;
        3457                 :             }
        3458                 :         }
        3459                 :
        3460                 :         /* Step d. */
        3461            4860 :         k++;
        3462                 :     }
        3463                 :
        3464                 :     /* Step 10. */
        3465             567 :     args.rval().setObject(*arr);
        3466             567 :     return true;
        3467                 : }
        3468                 :
        3469                 : /* ES5 15.4.4.21-15.4.4.22. */
        3470                 : class ArrayReduceBehavior
        3471                 : {
        3472                 :   public:
        3473               0 :     static void initialize(uint32_t len, uint32_t *start, uint32_t *end, int32_t *step)
        3474                 :     {
        3475               0 :         *start = 0;
        3476               0 :         *step = 1;
        3477               0 :         *end = len;
        3478               0 :     }
        3479                 : };
        3480                 :
        3481                 : class ArrayReduceRightBehavior
        3482                 : {
        3483                 :   public:
        3484               0 :     static void initialize(uint32_t len, uint32_t *start, uint32_t *end, int32_t *step)
        3485                 :     {
        3486               0 :         *start = len - 1;
        3487               0 :         *step = -1;
        3488                 :         /*
        3489                 :          * We rely on (well defined) unsigned integer underflow to check our
        3490                 :          * end condition after visiting the full range (including 0).
        3491                 :          */
        3492               0 :         *end = UINT32_MAX;
        3493               0 :     }
        3494                 : };
        3495                 :
        3496                 : template<class Behavior>
        3497                 : static inline bool
        3498               0 : array_reduceCommon(JSContext *cx, CallArgs &args)
        3499                 : {
        3500                 :     /* Step 1. */
        3501               0 :     JSObject *obj = ToObject(cx, &args.thisv());
        3502               0 :     if (!obj)
        3503               0 :         return false;
        3504                 :
        3505                 :     /* Step 2-3. */
        3506                 :     uint32_t len;
        3507               0 :     if (!js_GetLengthProperty(cx, obj, &len))
        3508               0 :         return false;
        3509                 :
        3510                 :     /* Step 4. */
        3511               0 :     if (args.length() == 0) {
        3512               0 :         js_ReportMissingArg(cx, args.calleev(), 0);
        3513               0 :         return false;
        3514                 :     }
        3515               0 :     JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
        3516               0 :     if (!callable)
        3517               0 :         return false;
        3518                 :
        3519                 :     /* Step 5. */
        3520               0 :     if (len == 0 && args.length() < 2) {
        3521               0 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_EMPTY_ARRAY_REDUCE);
        3522               0 :         return false;
        3523                 :     }
        3524                 :
        3525                 :     /* Step 6. */
        3526                 :     uint32_t k, end;
        3527                 :     int32_t step;
        3528               0 :     Behavior::initialize(len, &k, &end, &step);
        3529                 :
        3530                 :     /* Step 7-8. */
        3531                 :     Value accumulator;
        3532               0 :     if (args.length() >= 2) {
        3533               0 :         accumulator = args[1];
        3534                 :     } else {
        3535               0 :         JSBool kNotPresent = true;
        3536               0 :         while (kNotPresent && k != end) {
        3537               0 :             if (!GetElement(cx, obj, k, &kNotPresent, &accumulator))
        3538               0 :                 return false;
        3539               0 :             k += step;
        3540                 :         }
        3541               0 :         if (kNotPresent) {
        3542               0 :             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_EMPTY_ARRAY_REDUCE);
        3543               0 :             return false;
        3544                 :         }
        3545                 :     }
        3546                 :
        3547                 :     /* Step 9. */
        3548               0 :     InvokeArgsGuard ag;
        3549               0 :     while (k != end) {
        3550               0 :         if (!JS_CHECK_OPERATION_LIMIT(cx))
        3551               0 :             return false;
        3552                 :
        3553                 :         /* Step a, b, and c.i. */
        3554                 :         JSBool kNotPresent;
        3555                 :         Value kValue;
        3556               0 :         if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
        3557               0 :             return false;
        3558                 :
        3559                 :         /* Step c.ii. */
        3560               0 :         if (!kNotPresent) {
        3561               0 :             if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 4, &ag))
        3562               0 :                 return false;
        3563               0 :             ag.setCallee(ObjectValue(*callable));
        3564               0 :             ag.thisv() = UndefinedValue();
        3565               0 :             ag[0] = accumulator;
        3566               0 :             ag[1] = kValue;
        3567               0 :             ag[2] = NumberValue(k);
        3568               0 :             ag[3] = ObjectValue(*obj);
        3569               0 :             if (!Invoke(cx, ag))
        3570               0 :                 return false;
        3571               0 :             accumulator = ag.rval();
        3572                 :         }
        3573                 :
        3574                 :         /* Step d. */
        3575               0 :         k += step;
        3576                 :     }
        3577                 :
        3578                 :     /* Step 10. */
        3579               0 :     args.rval() = accumulator;
        3580               0 :     return true;
        3581                 : }
        3582                 :
        3583                 : /* ES5 15.4.4.21. */
        3584                 : static JSBool
        3585               0 : array_reduce(JSContext *cx, unsigned argc, Value *vp)
        3586                 : {
        3587               0 :     CallArgs args = CallArgsFromVp(argc, vp);
        3588               0 :     return array_reduceCommon<ArrayReduceBehavior>(cx, args);
        3589                 : }
        3590                 :
        3591                 : /* ES5 15.4.4.22. */
        3592                 : static JSBool
        3593               0 : array_reduceRight(JSContext *cx, unsigned argc, Value *vp)
        3594                 : {
        3595               0 :     CallArgs args = CallArgsFromVp(argc, vp);
        3596               0 :     return array_reduceCommon<ArrayReduceRightBehavior>(cx, args);
        3597                 : }
        3598                 :
        3599                 : static JSBool
        3600             270 : array_isArray(JSContext *cx, unsigned argc, Value *vp)
        3601                 : {
        3602             270 :     CallArgs args = CallArgsFromVp(argc, vp);
        3603             270 :     bool isArray = args.length() > 0 && IsObjectWithClass(args[0], ESClass_Array, cx);
        3604             270 :     args.rval().setBoolean(isArray);
        3605             270 :     return true;
        3606                 : }
        3607                 :
        3608                 : #define GENERIC JSFUN_GENERIC_NATIVE
        3609                 :
        3610                 : static JSFunctionSpec array_methods[] = {
        3611                 : #if JS_HAS_TOSOURCE
        3612                 :     JS_FN(js_toSource_str,      array_toSource,     0,0),
        3613                 : #endif
        3614                 :     JS_FN(js_toString_str,      array_toString,     0,0),
        3615                 :     JS_FN(js_toLocaleString_str,array_toLocaleString,0,0),
        3616                 :
        3617                 :     /* Perl-ish methods. */
        3618                 :     JS_FN("join",               array_join,         1,JSFUN_GENERIC_NATIVE),
        3619                 :     JS_FN("reverse",            array_reverse,      0,JSFUN_GENERIC_NATIVE),
        3620                 :     JS_FN("sort",               array_sort,         1,JSFUN_GENERIC_NATIVE),
        3621                 :     JS_FN("push",               array_push,         1,JSFUN_GENERIC_NATIVE),
        3622                 :     JS_FN("pop",                array_pop,          0,JSFUN_GENERIC_NATIVE),
        3623                 :     JS_FN("shift",              array_shift,        0,JSFUN_GENERIC_NATIVE),
        3624                 :     JS_FN("unshift",            array_unshift,      1,JSFUN_GENERIC_NATIVE),
        3625                 :     JS_FN("splice",             array_splice,       2,JSFUN_GENERIC_NATIVE),
        3626                 :
        3627                 :     /* Pythonic sequence methods. */
        3628                 :     JS_FN("concat",             array_concat,       1,JSFUN_GENERIC_NATIVE),
        3629                 :     JS_FN("slice",              array_slice,        2,JSFUN_GENERIC_NATIVE),
        3630                 :
        3631                 :     JS_FN("indexOf",            array_indexOf,      1,JSFUN_GENERIC_NATIVE),
        3632                 :     JS_FN("lastIndexOf",        array_lastIndexOf,  1,JSFUN_GENERIC_NATIVE),
        3633                 :     JS_FN("forEach",            array_forEach,      1,JSFUN_GENERIC_NATIVE),
        3634                 :     JS_FN("map",                array_map,          1,JSFUN_GENERIC_NATIVE),
        3635                 :     JS_FN("reduce",             array_reduce,       1,JSFUN_GENERIC_NATIVE),
        3636                 :     JS_FN("reduceRight",        array_reduceRight,  1,JSFUN_GENERIC_NATIVE),
        3637                 :     JS_FN("filter",             array_filter,       1,JSFUN_GENERIC_NATIVE),
        3638                 :     JS_FN("some",               array_some,         1,JSFUN_GENERIC_NATIVE),
        3639                 :     JS_FN("every",              array_every,        1,JSFUN_GENERIC_NATIVE),
        3640                 :
        3641                 :     JS_FS_END
        3642                 : };
        3643                 :
        3644                 : static JSFunctionSpec array_static_methods[] = {
        3645                 :     JS_FN("isArray",            array_isArray,      1,0),
        3646                 :     JS_FS_END
        3647                 : };
        3648                 :
        3649                 : /* ES5 15.4.2 */
        3650                 : JSBool
        3651           76076 : js_Array(JSContext *cx, unsigned argc, Value *vp)
        3652                 : {
        3653           76076 :     CallArgs args = CallArgsFromVp(argc, vp);
        3654           76076 :     TypeObject *type = GetTypeCallerInitObject(cx, JSProto_Array);
        3655           76076 :     if (!type)
        3656               0 :         return JS_FALSE;
        3657                 :
        3658           76076 :     if (args.length() != 1 || !args[0].isNumber()) {
        3659           27448 :         if (!InitArrayTypes(cx, type, args.array(), args.length()))
        3660               0 :             return false;
        3661           27448 :         JSObject *obj = (args.length() == 0)
        3662                 :                         ? NewDenseEmptyArray(cx)
        3663           27448 :                         : NewDenseCopiedArray(cx, args.length(), args.array());
        3664           27448 :         if (!obj)
        3665               0 :             return false;
        3666           27448 :         obj->setType(type);
        3667           27448 :         args.rval().setObject(*obj);
        3668           27448 :         return true;
        3669                 :     }
        3670                 :
        3671                 :     uint32_t length;
        3672           48628 :     if (args[0].isInt32()) {
        3673           48574 :         int32_t i = args[0].toInt32();
        3674           48574 :         if (i < 0) {
        3675               9 :             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
        3676               9 :             return false;
        3677                 :         }
        3678           48565 :         length = uint32_t(i);
        3679                 :     } else {
        3680              54 :         double d = args[0].toDouble();
        3681              54 :         length = js_DoubleToECMAUint32(d);
        3682              54 :         if (d != double(length)) {
        3683              36 :             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
        3684              36 :             return false;
        3685                 :         }
        3686                 :     }
        3687                 :
        3688           48583 :     JSObject *obj = NewDenseUnallocatedArray(cx, length);
        3689           48583 :     if (!obj)
        3690               0 :         return false;
        3691                 :
        3692           48583 :     obj->setType(type);
        3693                 :
        3694                 :     /* If the length calculation overflowed, make sure that is marked for the new type. */
        3695           48583 :     if (obj->getArrayLength() > INT32_MAX)
        3696              18 :         obj->setArrayLength(cx, obj->getArrayLength());
        3697                 :
        3698           48583 :     args.rval().setObject(*obj);
        3699           48583 :     return true;
        3700                 : }
        3701                 :
        3702                 : JSObject *
        3703           19345 : js_InitArrayClass(JSContext *cx, JSObject *obj)
        3704                 : {
        3705           19345 :     JS_ASSERT(obj->isNative());
        3706                 :
        3707           38690 :     RootedVar<GlobalObject*> global(cx);
        3708           19345 :     global = &obj->asGlobal();
        3709                 :
        3710           38690 :     RootedVarObject arrayProto(cx);
        3711           19345 :     arrayProto = global->createBlankPrototype(cx, &SlowArrayClass);
        3712           19345 :     if (!arrayProto || !AddLengthProperty(cx, arrayProto))
        3713               0 :         return NULL;
        3714           19345 :     arrayProto->setArrayLength(cx, 0);
        3715                 :
        3716           38690 :     RootedVarFunction ctor(cx);
        3717           19345 :     ctor = global->createConstructor(cx, js_Array, CLASS_ATOM(cx, Array), 1);
        3718           19345 :     if (!ctor)
        3719               0 :         return NULL;
        3720                 :
        3721                 :     /*
        3722                 :      * The default 'new' type of Array.prototype is required by type inference
        3723                 :      * to have unknown properties, to simplify handling of e.g. heterogenous
        3724                 :      * arrays in JSON and script literals and allows setDenseArrayElement to
        3725                 :      * be used without updating the indexed type set for such default arrays.
        3726                 :      */
        3727           19345 :     if (!arrayProto->setNewTypeUnknown(cx))
        3728               0 :         return NULL;
        3729                 :
        3730           19345 :     if (!LinkConstructorAndPrototype(cx, ctor, arrayProto))
        3731               0 :         return NULL;
        3732                 :
        3733           38690 :     if (!DefinePropertiesAndBrand(cx, arrayProto, NULL, array_methods) ||
        3734           19345 :         !DefinePropertiesAndBrand(cx, ctor, NULL, array_static_methods))
        3735                 :     {
        3736               0 :         return NULL;
        3737                 :     }
        3738                 :
        3739           19345 :     if (!DefineConstructorAndPrototype(cx, global, JSProto_Array, ctor, arrayProto))
        3740               0 :         return NULL;
        3741                 :
        3742           19345 :     return arrayProto;
        3743                 : }
        3744                 :
        3745                 : /*
        3746                 :  * Array allocation functions.
        3747                 :  */
        3748                 : namespace js {
        3749                 :
        3750                 : static inline bool
        3751         2106373 : EnsureNewArrayElements(JSContext *cx, JSObject *obj, uint32_t length)
        3752                 : {
        3753                 :     /*
        3754                 :      * If ensureElements creates dynamically allocated slots, then having
        3755                 :      * fixedSlots is a waste.
        3756                 :      */
        3757         4212746 :     DebugOnly<uint32_t> cap = obj->getDenseArrayCapacity();
        3758                 :
        3759         2106373 :     if (!obj->ensureElements(cx, length))
        3760               0 :         return false;
        3761                 :
        3762         2106373 :     JS_ASSERT_IF(cap, !obj->hasDynamicElements());
        3763                 :
        3764         2106373 :     return true;
        3765                 : }
        3766                 :
        3767                 : template<bool allocateCapacity>
        3768                 : static JS_ALWAYS_INLINE JSObject *
        3769         2266237 : NewArray(JSContext *cx, uint32_t length, JSObject *proto)
        3770                 : {
        3771         2266237 :     gc::AllocKind kind = GuessArrayGCKind(length);
        3772                 :
        3773                 : #ifdef JS_THREADSAFE
        3774         2266237 :     JS_ASSERT(CanBeFinalizedInBackground(kind, &ArrayClass));
        3775         2266237 :     kind = GetBackgroundAllocKind(kind);
        3776                 : #endif
        3777                 :
        3778         2266237 :     GlobalObject *parent = GetCurrentGlobal(cx);
        3779                 :
        3780         2266237 :     NewObjectCache &cache = cx->compartment->newObjectCache;
        3781                 :
        3782         2266237 :     NewObjectCache::EntryIndex entry = -1;
        3783         2266237 :     if (cache.lookupGlobal(&ArrayClass, parent, kind, &entry)) {
        3784         2045720 :         JSObject *obj = cache.newObjectFromHit(cx, entry);
        3785         2045720 :         if (!obj)
        3786               0 :             return NULL;
        3787                 :         /* Fixup the elements pointer and length, which may be incorrect. */
        3788         2045720 :         obj->setFixedElements();
        3789         2045720 :         obj->setArrayLength(cx, length);
        3790         1887507 :         if (allocateCapacity && !EnsureNewArrayElements(cx, obj, length))
        3791               0 :             return NULL;
        3792         2045720 :         return obj;
        3793                 :     }
        3794                 :
        3795          441034 :     Root<GlobalObject*> parentRoot(cx, &parent);
        3796                 :
        3797          220517 :     if (!proto && !FindProto(cx, &ArrayClass, parentRoot, &proto))
        3798               0 :         return NULL;
        3799                 :
        3800          441034 :     RootObject protoRoot(cx, &proto);
        3801          441034 :     RootedVarTypeObject type(cx);
        3802                 :
        3803          220517 :     type = proto->getNewType(cx);
        3804          220517 :     if (!type)
        3805               0 :         return NULL;
        3806                 :
        3807                 :     /*
        3808                 :      * Get a shape with zero fixed slots, regardless of the size class.
        3809                 :      * See JSObject::createDenseArray.
        3810                 :      */
        3811          441034 :     RootedVarShape shape(cx);
        3812          220517 :     shape = EmptyShape::getInitialShape(cx, &ArrayClass, proto,
        3813                 :                                         parent, gc::FINALIZE_OBJECT0);
        3814          220517 :     if (!shape)
        3815               0 :         return NULL;
        3816                 :
        3817          220517 :     JSObject* obj = JSObject::createDenseArray(cx, kind, shape, type, length);
        3818          220517 :     if (!obj)
        3819               0 :         return NULL;
        3820                 :
        3821          220517 :     if (entry != -1)
        3822          220517 :         cache.fillGlobal(entry, &ArrayClass, parent, kind, obj);
        3823                 :
        3824          218866 :     if (allocateCapacity && !EnsureNewArrayElements(cx, obj, length))
        3825               0 :         return NULL;
        3826                 :
        3827          220517 :     Probes::createObject(cx, obj);
        3828          220517 :     return obj;
        3829                 : }
        3830                 :
        3831                 : JSObject * JS_FASTCALL
        3832           26239 : NewDenseEmptyArray(JSContext *cx, JSObject *proto)
        3833                 : {
        3834           26239 :     return NewArray<false>(cx, 0, proto);
        3835                 : }
        3836                 :
        3837                 : JSObject * JS_FASTCALL
        3838         2043956 : NewDenseAllocatedArray(JSContext *cx, uint32_t length, JSObject *proto)
        3839                 : {
        3840         2043956 :     return NewArray<true>(cx, length, proto);
        3841                 : }
        3842                 :
        3843                 : JSObject * JS_FASTCALL
        3844               0 : NewDenseAllocatedEmptyArray(JSContext *cx, uint32_t length, JSObject *proto)
        3845                 : {
        3846               0 :     return NewArray<true>(cx, length, proto);
        3847                 : }
        3848                 :
        3849                 : JSObject * JS_FASTCALL
        3850           58256 : NewDenseUnallocatedArray(JSContext *cx, uint32_t length, JSObject *proto)
        3851                 : {
        3852           58256 :     return NewArray<false>(cx, length, proto);
        3853                 : }
        3854                 :
        3855                 : #ifdef JS_METHODJIT
        3856                 : JSObject * JS_FASTCALL
        3857           75369 : mjit::stubs::NewDenseUnallocatedArray(VMFrame &f, uint32_t length)
        3858                 : {
        3859           75369 :     JSObject *proto = (JSObject *) f.scratch;
        3860           75369 :     JSObject *obj = NewArray<false>(f.cx, length, proto);
        3861           75369 :     if (!obj)
        3862               0 :         THROWV(NULL);
        3863                 :
        3864           75369 :     return obj;
        3865                 : }
        3866                 : #endif
        3867                 :
        3868                 : JSObject *
        3869           62417 : NewDenseCopiedArray(JSContext *cx, uint32_t length, const Value *vp, JSObject *proto /* = NULL */)
        3870                 : {
        3871           62417 :     JSObject* obj = NewArray<true>(cx, length, proto);
        3872           62417 :     if (!obj)
        3873               0 :         return NULL;
        3874                 :
        3875           62417 :     JS_ASSERT(obj->getDenseArrayCapacity() >= length);
        3876                 :
        3877           62417 :     obj->setDenseArrayInitializedLength(vp ? length : 0);
        3878                 :
        3879           62417 :     if (vp)
        3880           43156 :         obj->initDenseArrayElements(0, vp, length);
        3881                 :
        3882           62417 :     return obj;
        3883                 : }
        3884                 :
        3885                 : JSObject *
        3886          210451 : NewSlowEmptyArray(JSContext *cx)
        3887                 : {
        3888          210451 :     JSObject *obj = NewBuiltinClassInstance(cx, &SlowArrayClass);
        3889          210451 :     if (!obj || !AddLengthProperty(cx, obj))
        3890               0 :         return NULL;
        3891                 :
        3892          210451 :     obj->setArrayLength(cx, 0);
        3893          210451 :     return obj;
        3894                 : }
        3895                 :
        3896                 : }
        3897                 :
        3898                 :
        3899                 : #ifdef DEBUG
        3900                 : JSBool
        3901               0 : js_ArrayInfo(JSContext *cx, unsigned argc, jsval *vp)
        3902                 : {
        3903               0 :     CallArgs args = CallArgsFromVp(argc, vp);
        3904                 :     JSObject *array;
        3905                 :
        3906               0 :     for (unsigned i = 0; i < args.length(); i++) {
        3907               0 :         Value arg = args[i];
        3908                 :
        3909               0 :         char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, arg, NULL);
        3910               0 :         if (!bytes)
        3911               0 :             return JS_FALSE;
        3912               0 :         if (arg.isPrimitive() ||
        3913               0 :             !(array = arg.toObjectOrNull())->isArray()) {
        3914               0 :             fprintf(stderr, "%s: not array\n", bytes);
        3915               0 :             cx->free_(bytes);
        3916               0 :             continue;
        3917                 :         }
        3918                 :         fprintf(stderr, "%s: %s (len %u", bytes,
        3919               0 :                 array->isDenseArray() ? "dense" : "sparse",
        3920               0 :                 array->getArrayLength());
        3921               0 :         if (array->isDenseArray()) {
        3922                 :             fprintf(stderr, ", capacity %u",
        3923               0 :                     array->getDenseArrayCapacity());
        3924                 :         }
        3925               0 :         fputs(")\n", stderr);
        3926               0 :         cx->free_(bytes);
        3927                 :     }
        3928                 :
        3929               0 :     args.rval().setUndefined();
        3930               0 :     return true;
        3931                 : }
        3932                 : #endif

Generated by: LCOV version 1.7