LCOV - code coverage report
Current view: directory - js/src - jsarray.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1810 1329 73.4 %
Date: 2012-06-02 Functions: 142 117 82.4 %

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

Generated by: LCOV version 1.7