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

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

Generated by: LCOV version 1.7