/[jscoverage]/trunk/js/jsarray.cpp
ViewVC logotype

Diff of /trunk/js/jsarray.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 459 by siliconforks, Tue Dec 9 03:37:47 2008 UTC revision 460 by siliconforks, Sat Sep 26 23:15:22 2009 UTC
# Line 41  Line 41 
41  /*  /*
42   * JS array class.   * JS array class.
43   *   *
44   * Array objects begin as "dense" arrays, optimized for numeric-only property   * Array objects begin as "dense" arrays, optimized for index-only property
45   * access over a vector of slots (obj->dslots) with high load factor.  Array   * access over a vector of slots (obj->dslots) with high load factor.  Array
46   * methods optimize for denseness by testing that the object's class is   * methods optimize for denseness by testing that the object's class is
47   * &js_ArrayClass, and can then directly manipulate the slots for efficiency.   * &js_ArrayClass, and can then directly manipulate the slots for efficiency.
# Line 49  Line 49 
49   * We track these pieces of metadata for arrays in dense mode:   * We track these pieces of metadata for arrays in dense mode:
50   *  - the array's length property as a uint32, in JSSLOT_ARRAY_LENGTH,   *  - the array's length property as a uint32, in JSSLOT_ARRAY_LENGTH,
51   *  - the number of indices that are filled (non-holes), in JSSLOT_ARRAY_COUNT,   *  - the number of indices that are filled (non-holes), in JSSLOT_ARRAY_COUNT,
52   *  - the net number of slots starting at dslots (DENSELEN), in dslots[-1] if   *  - the net number of slots starting at dslots (capacity), in dslots[-1] if
53   *    dslots is non-NULL.   *    dslots is non-NULL.
54   *   *
55   * In dense mode, holes in the array are represented by JSVAL_HOLE.  The final   * In dense mode, holes in the array are represented by JSVAL_HOLE.  The final
56   * slot in fslots (JSSLOT_ARRAY_LOOKUP_HOLDER) is used to store the single jsid   * slot in fslots is unused.
57   * "in use" by a lookupProperty caller.   *
58     * NB: the capacity and length of a dense array are entirely unrelated!  The
59     * length may be greater than, less than, or equal to the capacity.  See
60     * array_length_setter for an explanation of how the first, most surprising
61     * case may occur.
62   *   *
63   * Arrays are converted to use js_SlowArrayClass when any of these conditions   * Arrays are converted to use js_SlowArrayClass when any of these conditions
64   * are met:   * are met:
65   *  - the load factor (COUNT / DENSELEN) is less than 0.25, and there are   *  - the load factor (COUNT / capacity) is less than 0.25, and there are
66   *    more than MIN_SPARSE_INDEX slots total   *    more than MIN_SPARSE_INDEX slots total
67   *  - a property is set that is non-numeric (and not "length"); or   *  - a property is set that is not indexed (and not "length"); or
68   *  - a hole is filled below DENSELEN (possibly implicitly through methods like   *  - a property is defined that has non-default property attributes.
  *    |reverse| or |splice|).  
  *  
  * In the latter two cases, property creation order is no longer index order,  
  * which necessitates use of a structure that keeps track of property creation  
  * order.  (ES4, due to expectations baked into web script, requires that  
  * enumeration order be the order in which properties were created.)  
69   *   *
70   * An alternative in the latter case (out-of-order index set) would be to   * Dense arrays do not track property creation order, so unlike other native
71   * maintain the scope to track property enumeration order, but still use   * objects and slow arrays, enumerating an array does not necessarily visit the
72   * the fast slot access.  That would have the same memory cost as just using   * properties in the order they were created.  We could instead maintain the
73   * a js_SlowArrayClass, but have the same performance characteristics as   * scope to track property enumeration order, but still use the fast slot
74   * a dense array for slot accesses, at some cost in code complexity.   * access.  That would have the same memory cost as just using a
75     * js_SlowArrayClass, but have the same performance characteristics as a dense
76     * array for slot accesses, at some cost in code complexity.
77   */   */
78  #include "jsstddef.h"  #include "jsstddef.h"
79  #include <stdlib.h>  #include <stdlib.h>
# Line 105  Line 105 
105  #define MAXSTR   "4294967295"  #define MAXSTR   "4294967295"
106    
107  /* Small arrays are dense, no matter what. */  /* Small arrays are dense, no matter what. */
108  #define MIN_SPARSE_INDEX 32  #define MIN_SPARSE_INDEX 256
109    
110    static inline bool
111    INDEX_TOO_BIG(jsuint index)
112    {
113        return index > JS_BIT(29) - 1;
114    }
115    
 #define INDEX_TOO_BIG(index) ((index) > JS_BIT(29) - 1)  
116  #define INDEX_TOO_SPARSE(array, index)                                         \  #define INDEX_TOO_SPARSE(array, index)                                         \
117      (INDEX_TOO_BIG(index) ||                                                   \      (INDEX_TOO_BIG(index) ||                                                   \
118       ((index) > ARRAY_DENSE_LENGTH(array) && (index) >= MIN_SPARSE_INDEX &&    \       ((index) > js_DenseArrayCapacity(array) && (index) >= MIN_SPARSE_INDEX && \
119        (index) > (uint32)((array)->fslots[JSSLOT_ARRAY_COUNT] + 1) * 4))        (index) > (uint32)((array)->fslots[JSSLOT_ARRAY_COUNT] + 1) * 4))
120    
121  JS_STATIC_ASSERT(sizeof(JSScopeProperty) > 4 * sizeof(jsval));  JS_STATIC_ASSERT(sizeof(JSScopeProperty) > 4 * sizeof(jsval));
# Line 242  Line 247 
247  }  }
248    
249  static JSBool  static JSBool
250  IndexToValue(JSContext *cx, jsuint index, jsval *vp)  IndexToValue(JSContext *cx, jsdouble index, jsval *vp)
251  {  {
252      if (index <= JSVAL_INT_MAX) {      return js_NewWeaklyRootedNumber(cx, index, vp);
         *vp = INT_TO_JSVAL(index);  
         return JS_TRUE;  
     }  
     return JS_NewDoubleValue(cx, (jsdouble)index, vp);  
253  }  }
254    
255  JSBool JS_FASTCALL  JSBool JS_FASTCALL
# Line 312  Line 313 
313  }  }
314    
315  static JSBool  static JSBool
316  ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldlen, uint32 len)  ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldsize, uint32 size)
317  {  {
318      jsval *slots, *newslots;      jsval *slots, *newslots;
319    
320      if (len == 0) {      if (size == 0) {
321          if (obj->dslots) {          if (obj->dslots) {
322              JS_free(cx, obj->dslots - 1);              JS_free(cx, obj->dslots - 1);
323              obj->dslots = NULL;              obj->dslots = NULL;
# Line 324  Line 325 
325          return JS_TRUE;          return JS_TRUE;
326      }      }
327    
328      if (len > ~(uint32)0 / sizeof(jsval)) {      /*
329         * MAX_DSLOTS_LENGTH is the maximum net capacity supported. Since we allocate
330         * one additional slot to hold the array length, we have to use >= here.
331         */
332        if (size >= MAX_DSLOTS_LENGTH) {
333          js_ReportAllocationOverflow(cx);          js_ReportAllocationOverflow(cx);
334          return JS_FALSE;          return JS_FALSE;
335      }      }
336    
337      slots = obj->dslots ? obj->dslots - 1 : NULL;      slots = obj->dslots ? obj->dslots - 1 : NULL;
338      newslots = (jsval *) JS_realloc(cx, slots, sizeof (jsval) * (len + 1));      newslots = (jsval *) JS_realloc(cx, slots, (size + 1) * sizeof(jsval));
339      if (!newslots)      if (!newslots)
340          return JS_FALSE;          return JS_FALSE;
341    
342      obj->dslots = newslots + 1;      obj->dslots = newslots + 1;
343      ARRAY_SET_DENSE_LENGTH(obj, len);      js_SetDenseArrayCapacity(obj, size);
344    
345      for (slots = obj->dslots + oldlen; slots < obj->dslots + len; slots++)      for (slots = obj->dslots + oldsize; slots < obj->dslots + size; slots++)
346          *slots = JSVAL_HOLE;          *slots = JSVAL_HOLE;
347    
348      return JS_TRUE;      return JS_TRUE;
349  }  }
350    
351    /*
352     * When a dense array with CAPACITY_DOUBLING_MAX or fewer slots needs to grow,
353     * double its capacity, to push() N elements in amortized O(N) time.
354     *
355     * Above this limit, grow by 12.5% each time. Speed is still amortized O(N),
356     * with a higher constant factor, and we waste less space.
357     */
358    #define CAPACITY_DOUBLING_MAX    (1024 * 1024)
359    
360    /*
361     * Round up all large allocations to a multiple of this (1MB), so as not to
362     * waste space if malloc gives us 1MB-sized chunks (as jemalloc does).
363     */
364    #define CAPACITY_CHUNK  (1024 * 1024 / sizeof(jsval))
365    
366  static JSBool  static JSBool
367  EnsureLength(JSContext *cx, JSObject *obj, uint32 len)  EnsureCapacity(JSContext *cx, JSObject *obj, uint32 capacity)
368  {  {
369      uint32 oldlen = ARRAY_DENSE_LENGTH(obj);      uint32 oldsize = js_DenseArrayCapacity(obj);
370    
371      if (len > oldlen) {      if (capacity > oldsize) {
372          return ResizeSlots(cx, obj, oldlen,          /*
373                             len + ARRAY_GROWBY - (len % ARRAY_GROWBY));           * If this overflows uint32, capacity is very large. nextsize will end
374             * up being less than capacity, the code below will thus disregard it,
375             * and ResizeSlots will fail.
376             *
377             * The way we use dslots[-1] forces a few +1s and -1s here. For
378             * example, (oldsize * 2 + 1) produces the sequence 7, 15, 31, 63, ...
379             * which makes the total allocation size (with dslots[-1]) a power
380             * of two.
381             */
382            uint32 nextsize = (oldsize <= CAPACITY_DOUBLING_MAX)
383                              ? oldsize * 2 + 1
384                              : oldsize + (oldsize >> 3);
385    
386            capacity = JS_MAX(capacity, nextsize);
387            if (capacity >= CAPACITY_CHUNK)
388                capacity = JS_ROUNDUP(capacity + 1, CAPACITY_CHUNK) - 1;  /* -1 for dslots[-1] */
389            else if (capacity < ARRAY_CAPACITY_MIN)
390                capacity = ARRAY_CAPACITY_MIN;
391            return ResizeSlots(cx, obj, oldsize, capacity);
392      }      }
393      return JS_TRUE;      return JS_TRUE;
394  }  }
395    
396    static bool
397    ReallyBigIndexToId(JSContext* cx, jsdouble index, jsid* idp)
398    {
399        JSAutoTempValueRooter dval(cx);
400        if (!js_NewDoubleInRootedValue(cx, index, dval.addr()) ||
401            !js_ValueToStringId(cx, dval.value(), idp)) {
402            return JS_FALSE;
403        }
404        return JS_TRUE;
405    }
406    
407    static bool
408    IndexToId(JSContext* cx, JSObject* obj, jsdouble index, JSBool* hole, jsid* idp,
409              JSBool createAtom = JS_FALSE)
410    {
411        if (index <= JSVAL_INT_MAX) {
412            *idp = INT_TO_JSID(index);
413            return JS_TRUE;
414        }
415    
416        if (index <= jsuint(-1)) {
417            if (!BigIndexToId(cx, obj, jsuint(index), createAtom, idp))
418                return JS_FALSE;
419            if (hole && JSVAL_IS_VOID(*idp))
420                *hole = JS_TRUE;
421            return JS_TRUE;
422        }
423    
424        return ReallyBigIndexToId(cx, index, idp);
425    }
426    
427  /*  /*
428   * If the property at the given index exists, get its value into location   * If the property at the given index exists, get its value into location
429   * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp   * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp
# Line 362  Line 431 
431   * properly rooted and can be used as GC-protected storage for temporaries.   * properly rooted and can be used as GC-protected storage for temporaries.
432   */   */
433  static JSBool  static JSBool
434  GetArrayElement(JSContext *cx, JSObject *obj, jsuint index, JSBool *hole,  GetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole,
435                  jsval *vp)                  jsval *vp)
436  {  {
437      jsid id;      JS_ASSERT(index >= 0);
438      JSObject *obj2;      if (OBJ_IS_DENSE_ARRAY(cx, obj) && index < js_DenseArrayCapacity(obj) &&
439      JSProperty *prop;          (*vp = obj->dslots[jsuint(index)]) != JSVAL_HOLE) {
   
     if (OBJ_IS_DENSE_ARRAY(cx, obj) && index < ARRAY_DENSE_LENGTH(obj) &&  
         (*vp = obj->dslots[index]) != JSVAL_HOLE) {  
440          *hole = JS_FALSE;          *hole = JS_FALSE;
441          return JS_TRUE;          return JS_TRUE;
442      }      }
443    
444      if (index <= JSVAL_INT_MAX) {      JSAutoTempIdRooter idr(cx);
445          id = INT_TO_JSID(index);  
446      } else {      *hole = JS_FALSE;
447          if (!BigIndexToId(cx, obj, index, JS_FALSE, &id))      if (!IndexToId(cx, obj, index, hole, idr.addr()))
448              return JS_FALSE;          return JS_FALSE;
449          if (JSVAL_IS_VOID(id)) {      if (*hole) {
450              *hole = JS_TRUE;          *vp = JSVAL_VOID;
451              *vp = JSVAL_VOID;          return JS_TRUE;
             return JS_TRUE;  
         }  
452      }      }
453    
454      if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))      JSObject *obj2;
455        JSProperty *prop;
456        if (!OBJ_LOOKUP_PROPERTY(cx, obj, idr.id(), &obj2, &prop))
457          return JS_FALSE;          return JS_FALSE;
458      if (!prop) {      if (!prop) {
459          *hole = JS_TRUE;          *hole = JS_TRUE;
460          *vp = JSVAL_VOID;          *vp = JSVAL_VOID;
461      } else {      } else {
462          OBJ_DROP_PROPERTY(cx, obj2, prop);          OBJ_DROP_PROPERTY(cx, obj2, prop);
463          if (!OBJ_GET_PROPERTY(cx, obj, id, vp))          if (!OBJ_GET_PROPERTY(cx, obj, idr.id(), vp))
464              return JS_FALSE;              return JS_FALSE;
465          *hole = JS_FALSE;          *hole = JS_FALSE;
466      }      }
# Line 405  Line 471 
471   * Set the value of the property at the given index to v assuming v is rooted.   * Set the value of the property at the given index to v assuming v is rooted.
472   */   */
473  static JSBool  static JSBool
474  SetArrayElement(JSContext *cx, JSObject *obj, jsuint index, jsval v)  SetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, jsval v)
475  {  {
476      jsid id;      JS_ASSERT(index >= 0);
477    
478      if (OBJ_IS_DENSE_ARRAY(cx, obj)) {      if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
479          /* Predicted/prefeched code should favor the remains-dense case. */          /* Predicted/prefetched code should favor the remains-dense case. */
480          if (!INDEX_TOO_SPARSE(obj, index)) {          if (index <= jsuint(-1)) {
481              if (!EnsureLength(cx, obj, index + 1))              jsuint idx = jsuint(index);
482                  return JS_FALSE;              if (!INDEX_TOO_SPARSE(obj, idx)) {
483              if (index >= (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH])                  JS_ASSERT(idx + 1 > idx);
484                  obj->fslots[JSSLOT_ARRAY_LENGTH] = index + 1;                  if (!EnsureCapacity(cx, obj, idx + 1))
485              if (obj->dslots[index] == JSVAL_HOLE)                      return JS_FALSE;
486                  obj->fslots[JSSLOT_ARRAY_COUNT]++;                  if (idx >= uint32(obj->fslots[JSSLOT_ARRAY_LENGTH]))
487              obj->dslots[index] = v;                      obj->fslots[JSSLOT_ARRAY_LENGTH] = idx + 1;
488              return JS_TRUE;                  if (obj->dslots[idx] == JSVAL_HOLE)
489                        obj->fslots[JSSLOT_ARRAY_COUNT]++;
490                    obj->dslots[idx] = v;
491                    return JS_TRUE;
492                }
493          }          }
494    
495          if (!js_MakeArraySlow(cx, obj))          if (!js_MakeArraySlow(cx, obj))
496              return JS_FALSE;              return JS_FALSE;
497      }      }
498    
499      if (index <= JSVAL_INT_MAX) {      JSAutoTempIdRooter idr(cx);
500          id = INT_TO_JSID(index);  
501      } else {      if (!IndexToId(cx, obj, index, NULL, idr.addr(), JS_TRUE))
502          if (!BigIndexToId(cx, obj, index, JS_TRUE, &id))          return JS_FALSE;
503              return JS_FALSE;      JS_ASSERT(!JSVAL_IS_VOID(idr.id()));
504          JS_ASSERT(!JSVAL_IS_VOID(id));  
505      }      return OBJ_SET_PROPERTY(cx, obj, idr.id(), &v);
     return OBJ_SET_PROPERTY(cx, obj, id, &v);  
506  }  }
507    
508  static JSBool  static JSBool
509  DeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index)  DeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index)
510  {  {
511      jsid id;      JS_ASSERT(index >= 0);
     jsval junk;  
   
512      if (OBJ_IS_DENSE_ARRAY(cx, obj)) {      if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
513          if (index < ARRAY_DENSE_LENGTH(obj)) {          if (index <= jsuint(-1)) {
514              if (obj->dslots[index] != JSVAL_HOLE)              jsuint idx = jsuint(index);
515                  obj->fslots[JSSLOT_ARRAY_COUNT]--;              if (!INDEX_TOO_SPARSE(obj, idx) && idx < js_DenseArrayCapacity(obj)) {
516              obj->dslots[index] = JSVAL_HOLE;                  if (obj->dslots[idx] != JSVAL_HOLE)
517                        obj->fslots[JSSLOT_ARRAY_COUNT]--;
518                    obj->dslots[idx] = JSVAL_HOLE;
519                    return JS_TRUE;
520                }
521          }          }
522          return JS_TRUE;          return JS_TRUE;
523      }      }
524    
525      if (index <= JSVAL_INT_MAX) {      JSAutoTempIdRooter idr(cx);
526          id = INT_TO_JSID(index);  
527      } else {      if (!IndexToId(cx, obj, index, NULL, idr.addr()))
528          if (!BigIndexToId(cx, obj, index, JS_FALSE, &id))          return JS_FALSE;
529              return JS_FALSE;      if (JSVAL_IS_VOID(idr.id()))
530          if (JSVAL_IS_VOID(id))          return JS_TRUE;
531              return JS_TRUE;  
532      }      jsval junk;
533      return OBJ_DELETE_PROPERTY(cx, obj, id, &junk);      return OBJ_DELETE_PROPERTY(cx, obj, idr.id(), &junk);
534  }  }
535    
536  /*  /*
# Line 467  Line 538 
538   * its value to v assuming v is rooted.   * its value to v assuming v is rooted.
539   */   */
540  static JSBool  static JSBool
541  SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index,  SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index,
542                          JSBool hole, jsval v)                          JSBool hole, jsval v)
543  {  {
544      if (hole) {      if (hole) {
# Line 478  Line 549 
549  }  }
550    
551  JSBool  JSBool
552  js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length)  js_SetLengthProperty(JSContext *cx, JSObject *obj, jsdouble length)
553  {  {
554      jsval v;      jsval v;
555      jsid id;      jsid id;
# Line 580  Line 651 
651      }      }
652    
653      if (OBJ_IS_DENSE_ARRAY(cx, obj)) {      if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
654          if (ARRAY_DENSE_LENGTH(obj) && !ResizeSlots(cx, obj, oldlen, newlen))          /* Don't reallocate if we're not actually shrinking our slots. */
655            jsuint oldsize = js_DenseArrayCapacity(obj);
656            if (oldsize >= newlen && !ResizeSlots(cx, obj, oldsize, newlen))
657              return JS_FALSE;              return JS_FALSE;
658      } else if (oldlen - newlen < (1 << 24)) {      } else if (oldlen - newlen < (1 << 24)) {
659          do {          do {
660              --oldlen;              --oldlen;
661              if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||              if (!JS_CHECK_OPERATION_LIMIT(cx) ||
662                  !DeleteArrayElement(cx, obj, oldlen)) {                  !DeleteArrayElement(cx, obj, oldlen)) {
663                  return JS_FALSE;                  return JS_FALSE;
664              }              }
# Line 606  Line 679 
679          JS_PUSH_TEMP_ROOT_OBJECT(cx, iter, &tvr);          JS_PUSH_TEMP_ROOT_OBJECT(cx, iter, &tvr);
680          gap = oldlen - newlen;          gap = oldlen - newlen;
681          for (;;) {          for (;;) {
682              ok = (JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&              ok = (JS_CHECK_OPERATION_LIMIT(cx) &&
683                    JS_NextProperty(cx, iter, &id));                    JS_NextProperty(cx, iter, &id));
684              if (!ok)              if (!ok)
685                  break;                  break;
# Line 627  Line 700 
700      return JS_TRUE;      return JS_TRUE;
701  }  }
702    
703    /*
704     * We have only indexed properties up to capacity (excepting holes), plus the
705     * length property. For all else, we delegate to the prototype.
706     */
707    static inline bool
708    IsDenseArrayId(JSContext *cx, JSObject *obj, jsid id)
709    {
710        JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
711    
712        uint32 i;
713        return id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom) ||
714               (js_IdIsIndex(id, &i) &&
715                obj->fslots[JSSLOT_ARRAY_LENGTH] != 0 &&
716                i < js_DenseArrayCapacity(obj) &&
717                obj->dslots[i] != JSVAL_HOLE);
718    }
719    
720  static JSBool  static JSBool
721  array_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,  array_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
722                       JSProperty **propp)                       JSProperty **propp)
723  {  {
     uint32 i;  
     union { JSProperty *p; jsval *v; } u;  
   
724      if (!OBJ_IS_DENSE_ARRAY(cx, obj))      if (!OBJ_IS_DENSE_ARRAY(cx, obj))
725          return js_LookupProperty(cx, obj, id, objp, propp);          return js_LookupProperty(cx, obj, id, objp, propp);
726    
727      /*      if (IsDenseArrayId(cx, obj, id)) {
728       * We have only indexed properties up to DENSELEN (excepting holes), plus          *propp = (JSProperty *) id;
729       * the length property. For all else, we delegate to the prototype.          *objp = obj;
730       */          return JS_TRUE;
     if (id != ATOM_TO_JSID(cx->runtime->atomState.lengthAtom) &&  
         (!js_IdIsIndex(id, &i) ||  
          obj->fslots[JSSLOT_ARRAY_LENGTH] == 0 ||  
          i >= ARRAY_DENSE_LENGTH(obj) ||  
          obj->dslots[i] == JSVAL_HOLE))  
     {  
         JSObject *proto = STOBJ_GET_PROTO(obj);  
   
         if (!proto) {  
             *objp = NULL;  
             *propp = NULL;  
             return JS_TRUE;  
         }  
   
         return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);  
731      }      }
732    
733      /* FIXME 417501: threadsafety: could race with a lookup on another thread.      JSObject *proto = STOBJ_GET_PROTO(obj);
734       * If we can only have a single lookup active per context, we could      if (!proto) {
735       * pigeonhole this on the context instead. */          *objp = NULL;
736      JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER]));          *propp = NULL;
737      obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER] = (jsval) id;          return JS_TRUE;
738      u.v = &(obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER]);      }
739      *propp = u.p;      return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
     *objp = obj;  
     return JS_TRUE;  
740  }  }
741    
742  static void  static void
743  array_dropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)  array_dropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
744  {  {
745      JS_ASSERT_IF(OBJ_IS_DENSE_ARRAY(cx, obj),      JS_ASSERT(IsDenseArrayId(cx, obj, (jsid) prop));
746                   !JSVAL_IS_VOID(obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER]));  }
747  #ifdef DEBUG  
748      obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER] = JSVAL_VOID;  JSBool
749  #endif  js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, JSProperty *prop,
750                                 jsval *vp)
751    {
752        jsid id = (jsid) prop;
753        JS_ASSERT(IsDenseArrayId(cx, obj, id));
754    
755        uint32 i;
756        if (!js_IdIsIndex(id, &i)) {
757            JS_ASSERT(id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom));
758            return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp);
759        }
760        *vp = obj->dslots[i];
761        return JS_TRUE;
762  }  }
763    
764  static JSBool  static JSBool
# Line 695  Line 777 
777      if (!OBJ_IS_DENSE_ARRAY(cx, obj))      if (!OBJ_IS_DENSE_ARRAY(cx, obj))
778          return js_GetProperty(cx, obj, id, vp);          return js_GetProperty(cx, obj, id, vp);
779    
780      if (!js_IdIsIndex(ID_TO_VALUE(id), &i) || i >= ARRAY_DENSE_LENGTH(obj) ||      if (!js_IdIsIndex(ID_TO_VALUE(id), &i) || i >= js_DenseArrayCapacity(obj) ||
781          obj->dslots[i] == JSVAL_HOLE) {          obj->dslots[i] == JSVAL_HOLE) {
782          JSObject *obj2;          JSObject *obj2;
783          JSProperty *prop;          JSProperty *prop;
# Line 781  Line 863 
863          return js_SetProperty(cx, obj, id, vp);          return js_SetProperty(cx, obj, id, vp);
864      }      }
865    
866      if (!EnsureLength(cx, obj, i + 1))      if (!EnsureCapacity(cx, obj, i + 1))
867          return JS_FALSE;          return JS_FALSE;
868    
869      if (i >= (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH])      if (i >= (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH])
# Line 792  Line 874 
874      return JS_TRUE;      return JS_TRUE;
875  }  }
876    
877    JSBool
878    js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj)
879    {
880        /*
881         * Walk up the prototype chain and see if this indexed element already
882         * exists. If we hit the end of the prototype chain, it's safe to set the
883         * element on the original object.
884         */
885       while ((obj = JSVAL_TO_OBJECT(obj->fslots[JSSLOT_PROTO])) != NULL) {
886            /*
887             * If the prototype is a non-native object (possibly a dense array), or
888             * a native object (possibly a slow array) that has indexed properties,
889             * return true.
890             */
891            if (!OBJ_IS_NATIVE(obj))
892                return JS_TRUE;
893            if (SCOPE_HAS_INDEXED_PROPERTIES(OBJ_SCOPE(obj)))
894                return JS_TRUE;
895        }
896        return JS_FALSE;
897    }
898    
899  #ifdef JS_TRACER  #ifdef JS_TRACER
900  JSBool FASTCALL  JSBool FASTCALL
901  js_Array_dense_setelem(JSContext* cx, JSObject* obj, jsint i, jsval v)  js_Array_dense_setelem(JSContext* cx, JSObject* obj, jsint i, jsval v)
902  {  {
903      JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));      JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
904    
905      do {      /*
906          jsuint length = ARRAY_DENSE_LENGTH(obj);       * Let the interpreter worry about negative array indexes.
907          if ((jsuint)i < length) {       */
908              if (obj->dslots[i] == JSVAL_HOLE) {      JS_ASSERT((MAX_DSLOTS_LENGTH > JSVAL_INT_MAX) == (sizeof(jsval) != sizeof(uint32)));
909                  if (cx->runtime->anyArrayProtoHasElement)      if (MAX_DSLOTS_LENGTH > JSVAL_INT_MAX) {
910                      break;          /*
911                  if (i >= obj->fslots[JSSLOT_ARRAY_LENGTH])           * Have to check for negative values bleeding through on 64-bit machines only,
912                      obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1;           * since we can't allocate large enough arrays for this on 32-bit machines.
913                  obj->fslots[JSSLOT_ARRAY_COUNT]++;           */
914              }          if (i < 0)
915              obj->dslots[i] = v;              return JS_FALSE;
916              return JS_TRUE;      }
917          }  
918      } while (0);      /*
919      return OBJ_SET_PROPERTY(cx, obj, INT_TO_JSID(i), &v);       * If needed, grow the array as long it remains dense, otherwise fall off trace.
920         */
921        jsuint u = jsuint(i);
922        jsuint capacity = js_DenseArrayCapacity(obj);
923        if ((u >= capacity) && (INDEX_TOO_SPARSE(obj, u) || !EnsureCapacity(cx, obj, u + 1)))
924            return JS_FALSE;
925    
926        if (obj->dslots[u] == JSVAL_HOLE) {
927            if (js_PrototypeHasIndexedProperties(cx, obj))
928                return JS_FALSE;
929    
930            if (u >= jsuint(obj->fslots[JSSLOT_ARRAY_LENGTH]))
931                obj->fslots[JSSLOT_ARRAY_LENGTH] = u + 1;
932            ++obj->fslots[JSSLOT_ARRAY_COUNT];
933        }
934    
935        obj->dslots[u] = v;
936        return JS_TRUE;
937  }  }
938  #endif  #endif
939    
# Line 831  Line 952 
952      if (!isIndex || attrs != JSPROP_ENUMERATE) {      if (!isIndex || attrs != JSPROP_ENUMERATE) {
953          if (!ENSURE_SLOW_ARRAY(cx, obj))          if (!ENSURE_SLOW_ARRAY(cx, obj))
954              return JS_FALSE;              return JS_FALSE;
         if (isIndex && STOBJ_IS_DELEGATE(obj))  
             cx->runtime->anyArrayProtoHasElement = JS_TRUE;  
955          return js_DefineProperty(cx, obj, id, value, getter, setter, attrs, propp);          return js_DefineProperty(cx, obj, id, value, getter, setter, attrs, propp);
956      }      }
957    
# Line 870  Line 989 
989          return JS_TRUE;          return JS_TRUE;
990      }      }
991    
992      if (js_IdIsIndex(id, &i) && i < ARRAY_DENSE_LENGTH(obj) &&      if (js_IdIsIndex(id, &i) && i < js_DenseArrayCapacity(obj) &&
993          obj->dslots[i] != JSVAL_HOLE) {          obj->dslots[i] != JSVAL_HOLE) {
994          obj->fslots[JSSLOT_ARRAY_COUNT]--;          obj->fslots[JSSLOT_ARRAY_COUNT]--;
995          obj->dslots[i] = JSVAL_HOLE;          obj->dslots[i] = JSVAL_HOLE;
# Line 945  Line 1064 
1064  array_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,  array_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
1065                  jsval *statep, jsid *idp)                  jsval *statep, jsid *idp)
1066  {  {
1067      uint32 length, i;      uint32 capacity, i;
1068      JSIndexIterState *ii;      JSIndexIterState *ii;
1069    
1070      switch (enum_op) {      switch (enum_op) {
1071        case JSENUMERATE_INIT:        case JSENUMERATE_INIT:
1072          JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));          JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
1073          length = ARRAY_DENSE_LENGTH(obj);          capacity = js_DenseArrayCapacity(obj);
1074          if (idp)          if (idp)
1075              *idp = INT_TO_JSVAL(obj->fslots[JSSLOT_ARRAY_COUNT]);              *idp = INT_TO_JSVAL(obj->fslots[JSSLOT_ARRAY_COUNT]);
1076          ii = NULL;          ii = NULL;
1077          for (i = 0; i != length; ++i) {          for (i = 0; i != capacity; ++i) {
1078              if (obj->dslots[i] == JSVAL_HOLE) {              if (obj->dslots[i] == JSVAL_HOLE) {
1079                  if (!ii) {                  if (!ii) {
1080                      ii = (JSIndexIterState *)                      ii = (JSIndexIterState *)
1081                           JS_malloc(cx, offsetof(JSIndexIterState, holes) +                           JS_malloc(cx, offsetof(JSIndexIterState, holes) +
1082                                     JS_BITMAP_SIZE(length));                                     JS_BITMAP_SIZE(capacity));
1083                      if (!ii)                      if (!ii)
1084                          return JS_FALSE;                          return JS_FALSE;
1085                      ii->hasHoles = JS_TRUE;                      ii->hasHoles = JS_TRUE;
1086                      memset(ii->holes, 0, JS_BITMAP_SIZE(length));                      memset(ii->holes, 0, JS_BITMAP_SIZE(capacity));
1087                  }                  }
1088                  JS_SET_BIT(ii->holes, i);                  JS_SET_BIT(ii->holes, i);
1089              }              }
1090          }          }
1091          if (!ii) {          if (!ii) {
1092              /* Array has no holes. */              /* Array has no holes. */
1093              if (length <= PACKED_UINT_PAIR_MASK) {              if (capacity <= PACKED_UINT_PAIR_MASK) {
1094                  *statep = UINT_PAIR_TO_BOOLEAN_JSVAL(0, length);                  *statep = UINT_PAIR_TO_BOOLEAN_JSVAL(0, capacity);
1095                  break;                  break;
1096              }              }
1097              ii = (JSIndexIterState *)              ii = (JSIndexIterState *)
# Line 982  Line 1101 
1101              ii->hasHoles = JS_FALSE;              ii->hasHoles = JS_FALSE;
1102          }          }
1103          ii->index = 0;          ii->index = 0;
1104          ii->length = length;          ii->length = capacity;
1105          *statep = (jsval) ii | INDEX_ITER_TAG;          *statep = (jsval) ii | INDEX_ITER_TAG;
1106          JS_ASSERT(*statep & JSVAL_INT);          JS_ASSERT(*statep & JSVAL_INT);
1107          break;          break;
1108    
1109        case JSENUMERATE_NEXT:        case JSENUMERATE_NEXT:
1110          if (JSVAL_TAG(*statep) == JSVAL_BOOLEAN) {          if (JSVAL_TAG(*statep) == JSVAL_BOOLEAN) {
1111              BOOLEAN_JSVAL_TO_UINT_PAIR(*statep, i, length);              BOOLEAN_JSVAL_TO_UINT_PAIR(*statep, i, capacity);
1112              if (i != length) {              if (i != capacity) {
1113                  *idp = INT_TO_JSID(i);                  *idp = INT_TO_JSID(i);
1114                  *statep = UINT_PAIR_TO_BOOLEAN_JSVAL(i + 1, length);                  *statep = UINT_PAIR_TO_BOOLEAN_JSVAL(i + 1, capacity);
1115                  break;                  break;
1116              }              }
1117          } else {          } else {
# Line 1055  Line 1174 
1174  static void  static void
1175  array_trace(JSTracer *trc, JSObject *obj)  array_trace(JSTracer *trc, JSObject *obj)
1176  {  {
1177      uint32 length;      uint32 capacity;
1178      size_t i;      size_t i;
1179      jsval v;      jsval v;
1180    
1181      JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));      JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
1182    
1183      length = ARRAY_DENSE_LENGTH(obj);      capacity = js_DenseArrayCapacity(obj);
1184      for (i = 0; i < length; i++) {      for (i = 0; i < capacity; i++) {
1185          v = obj->dslots[i];          v = obj->dslots[i];
1186          if (JSVAL_IS_TRACEABLE(v)) {          if (JSVAL_IS_TRACEABLE(v)) {
1187              JS_SET_TRACING_INDEX(trc, "array_dslots", i);              JS_SET_TRACING_INDEX(trc, "array_dslots", i);
# Line 1079  Line 1198 
1198      }      }
1199  }  }
1200    
1201  static JSObjectMap *  extern JSObjectOps js_ArrayObjectOps;
 array_newObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,  
                    JSClass *clasp, JSObject *obj)  
 {  
 #ifdef DEBUG  
     extern JSClass js_ArrayClass;  
     extern JSObjectOps js_ArrayObjectOps;  
 #endif  
     JSObjectMap *map = (JSObjectMap *) JS_malloc(cx, sizeof(*map));  
     if (!map)  
         return NULL;  
   
     map->nrefs = nrefs;  
     JS_ASSERT(ops == &js_ArrayObjectOps);  
     map->ops = ops;  
     JS_ASSERT(clasp == &js_ArrayClass);  
     map->freeslot = JSSLOT_FREE(clasp);  
   
     return map;  
 }  
1202    
1203  void  static const JSObjectMap SharedArrayMap = { &js_ArrayObjectOps };
 array_destroyObjectMap(JSContext *cx, JSObjectMap *map)  
 {  
     JS_free(cx, map);  
 }  
1204    
1205  JSObjectOps js_ArrayObjectOps = {  JSObjectOps js_ArrayObjectOps = {
1206      array_newObjectMap,   array_destroyObjectMap,      &SharedArrayMap,
1207      array_lookupProperty, array_defineProperty,      array_lookupProperty, array_defineProperty,
1208      array_getProperty,    array_setProperty,      array_getProperty,    array_setProperty,
1209      array_getAttributes,  array_setAttributes,      array_getAttributes,  array_setAttributes,
# Line 1115  Line 1211 
1211      array_enumerate,      js_CheckAccess,      array_enumerate,      js_CheckAccess,
1212      NULL,                 array_dropProperty,      NULL,                 array_dropProperty,
1213      NULL,                 NULL,      NULL,                 NULL,
1214      NULL,                 js_HasInstance,      js_HasInstance,       array_trace,
1215      js_SetProtoOrParent,  js_SetProtoOrParent,      NULL,                 NULL,
1216      array_trace,          NULL,      NULL
     NULL,                 NULL  
1217  };  };
1218    
1219  static JSObjectOps *  static JSObjectOps *
# Line 1152  Line 1247 
1247  JSBool  JSBool
1248  js_MakeArraySlow(JSContext *cx, JSObject *obj)  js_MakeArraySlow(JSContext *cx, JSObject *obj)
1249  {  {
     JSObjectMap *map, *oldmap;  
     uint32 i, length;  
   
1250      JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass);      JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass);
1251    
1252      /* Create a native scope. */      /* Create a native scope. */
1253      map = js_NewObjectMap(cx, obj->map->nrefs, &js_SlowArrayObjectOps,      JSScope *scope = js_NewScope(cx, &js_SlowArrayObjectOps,
1254                            &js_SlowArrayClass, obj);                                   &js_SlowArrayClass, obj);
1255      if (!map)      if (!scope)
1256          return JS_FALSE;          return JS_FALSE;
1257    
1258      length = ARRAY_DENSE_LENGTH(obj);      uint32 capacity = js_DenseArrayCapacity(obj);
1259      if (length) {      if (capacity) {
1260          map->freeslot = STOBJ_NSLOTS(obj) + JS_INITIAL_NSLOTS;          scope->freeslot = STOBJ_NSLOTS(obj) + JS_INITIAL_NSLOTS;
1261          obj->dslots[-1] = JS_INITIAL_NSLOTS + length;          obj->dslots[-1] = JS_INITIAL_NSLOTS + capacity;
1262      } else {      } else {
1263          map->freeslot = STOBJ_NSLOTS(obj);          scope->freeslot = STOBJ_NSLOTS(obj);
1264      }      }
1265    
1266      /* Create new properties pointing to existing values in dslots */      /* Create new properties pointing to existing values in dslots */
1267      for (i = 0; i < length; i++) {      for (uint32 i = 0; i < capacity; i++) {
1268          jsid id;          jsid id;
1269          JSScopeProperty *sprop;          JSScopeProperty *sprop;
1270    
# Line 1184  Line 1276 
1276              continue;              continue;
1277          }          }
1278    
1279          sprop = js_AddScopeProperty(cx, (JSScope *)map, id, NULL, NULL,          sprop = js_AddScopeProperty(cx, scope, id, NULL, NULL,
1280                                      i + JS_INITIAL_NSLOTS, JSPROP_ENUMERATE,                                      i + JS_INITIAL_NSLOTS, JSPROP_ENUMERATE,
1281                                      0, 0);                                      0, 0);
1282          if (!sprop)          if (!sprop)
# Line 1197  Line 1289 
1289       * we can tell when only named properties have been added to a dense array       * we can tell when only named properties have been added to a dense array
1290       * to make it slow-but-not-sparse.       * to make it slow-but-not-sparse.
1291       */       */
1292      length = obj->fslots[JSSLOT_ARRAY_LENGTH];      {
1293      obj->fslots[JSSLOT_ARRAY_COUNT] = INT_FITS_IN_JSVAL(length)          uint32 length = obj->fslots[JSSLOT_ARRAY_LENGTH];
1294                                        ? INT_TO_JSVAL(length)          obj->fslots[JSSLOT_ARRAY_COUNT] = INT_FITS_IN_JSVAL(length)
1295                                        : JSVAL_VOID;                                            ? INT_TO_JSVAL(length)
1296                                              : JSVAL_VOID;
1297        }
1298    
1299      /* Make sure we preserve any flags borrowing bits in classword. */      /* Make sure we preserve any flags borrowing bits in classword. */
1300      obj->classword ^= (jsuword) &js_ArrayClass;      obj->classword ^= (jsuword) &js_ArrayClass;
1301      obj->classword |= (jsuword) &js_SlowArrayClass;      obj->classword |= (jsuword) &js_SlowArrayClass;
1302    
1303      /* Swap in our new map. */      obj->map = &scope->map;
     oldmap = obj->map;  
     obj->map = map;  
     array_destroyObjectMap(cx, oldmap);  
   
1304      return JS_TRUE;      return JS_TRUE;
1305    
1306  out_bad:    out_bad:
1307      js_DestroyObjectMap(cx, map);      js_DestroyScope(cx, scope);
1308      return JS_FALSE;      return JS_FALSE;
1309  }  }
1310    
# Line 1255  Line 1345 
1345      growth = (size_t) -1;      growth = (size_t) -1;
1346  #endif  #endif
1347    
1348      if (op == TO_SOURCE) {      /*
1349          if (IS_SHARP(he)) {       * We must check for the sharp bit and skip js_LeaveSharpObject when it is
1350         * set even when op is not TO_SOURCE. A script can overwrite the default
1351         * toSource implementation and trigger a call, for example, to the
1352         * toString method during serialization of the object graph (bug 369696).
1353         */
1354        if (IS_SHARP(he)) {
1355  #if JS_HAS_SHARP_VARS  #if JS_HAS_SHARP_VARS
1356              nchars = js_strlen(chars);          nchars = js_strlen(chars);
1357  #else  #else
1358              chars[0] = '[';          chars[0] = '[';
1359              chars[1] = ']';          chars[1] = ']';
1360              chars[2] = 0;          chars[2] = 0;
1361              nchars = 2;          nchars = 2;
1362  #endif  #endif
1363              goto make_string;          goto make_string;
1364          }      }
1365    
1366        if (op == TO_SOURCE) {
1367          /*          /*
1368           * Always allocate 2 extra chars for closing ']' and terminating 0           * Always allocate 2 extra chars for closing ']' and terminating 0
1369           * and then preallocate 1 + extratail to include starting '['.           * and then preallocate 1 + extratail to include starting '['.
# Line 1326  Line 1422 
1422    
1423      /* Use rval to locally root each element value as we loop and convert. */      /* Use rval to locally root each element value as we loop and convert. */
1424      for (index = 0; index < length; index++) {      for (index = 0; index < length; index++) {
1425          ok = (JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&          ok = (JS_CHECK_OPERATION_LIMIT(cx) &&
1426                GetArrayElement(cx, obj, index, &hole, rval));                GetArrayElement(cx, obj, index, &hole, rval));
1427          if (!ok)          if (!ok)
1428              goto done;              goto done;
# Line 1379  Line 1475 
1475              goto done;              goto done;
1476          }          }
1477          growth *= sizeof(jschar);          growth *= sizeof(jschar);
         JS_COUNT_OPERATION(cx, JSOW_ALLOCATION);  
1478          if (!chars) {          if (!chars) {
1479              chars = (jschar *) malloc(growth);              chars = (jschar *) malloc(growth);
1480              if (!chars)              if (!chars)
# Line 1484  Line 1579 
1579      return array_join_sub(cx, obj, TO_LOCALE_STRING, NULL, vp);      return array_join_sub(cx, obj, TO_LOCALE_STRING, NULL, vp);
1580  }  }
1581    
1582    enum TargetElementsType {
1583        TargetElementsAllHoles,
1584        TargetElementsMayContainValues
1585    };
1586    
1587    enum SourceVectorType {
1588        SourceVectorAllValues,
1589        SourceVectorMayContainHoles
1590    };
1591    
1592  static JSBool  static JSBool
1593  InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint end,  InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, jsval *vector,
1594                    jsval *vector)                    TargetElementsType targetType, SourceVectorType vectorType)
1595  {  {
1596      if (OBJ_IS_DENSE_ARRAY(cx, obj)) {      JS_ASSERT(count < MAXINDEX);
         if (!EnsureLength(cx, obj, end))  
             return JS_FALSE;  
1597    
1598          if (end > (uint32)obj->fslots[JSSLOT_ARRAY_LENGTH])      /*
1599              obj->fslots[JSSLOT_ARRAY_LENGTH] = end;       * Optimize for dense arrays so long as adding the given set of elements
1600         * wouldn't otherwise make the array slow.
1601         */
1602        if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) &&
1603            start <= MAXINDEX - count && !INDEX_TOO_BIG(start + count)) {
1604    
1605          memcpy(obj->dslots + start, vector, sizeof(jsval) * (end - start));  #ifdef DEBUG_jwalden
1606            {
1607                /* Verify that overwriteType and writeType were accurate. */
1608                JSAutoTempIdRooter idr(cx, JSVAL_ZERO);
1609                for (jsuint i = 0; i < count; i++) {
1610                    JS_ASSERT_IF(vectorType == SourceVectorAllValues, vector[i] != JSVAL_HOLE);
1611    
1612                    jsdouble index = jsdouble(start) + i;
1613                    if (targetType == TargetElementsAllHoles && index < jsuint(-1)) {
1614                        JS_ASSERT(ReallyBigIndexToId(cx, index, idr.addr()));
1615                        JSObject* obj2;
1616                        JSProperty* prop;
1617                        JS_ASSERT(OBJ_LOOKUP_PROPERTY(cx, obj, idr.id(), &obj2, &prop));
1618                        JS_ASSERT(!prop);
1619                    }
1620                }
1621            }
1622    #endif
1623    
1624            jsuint newlen = start + count;
1625            JS_ASSERT(jsdouble(start) + count == jsdouble(newlen));
1626            if (!EnsureCapacity(cx, obj, newlen))
1627                return JS_FALSE;
1628    
1629            if (newlen > uint32(obj->fslots[JSSLOT_ARRAY_LENGTH]))
1630                obj->fslots[JSSLOT_ARRAY_LENGTH] = newlen;
1631    
1632            JS_ASSERT(count < size_t(-1) / sizeof(jsval));
1633            if (targetType == TargetElementsMayContainValues) {
1634                jsuint valueCount = 0;
1635                for (jsuint i = 0; i < count; i++) {
1636                     if (obj->dslots[start + i] != JSVAL_HOLE)
1637                         valueCount++;
1638                }
1639                JS_ASSERT(uint32(obj->fslots[JSSLOT_ARRAY_COUNT]) >= valueCount);
1640                obj->fslots[JSSLOT_ARRAY_COUNT] -= valueCount;
1641            }
1642            memcpy(obj->dslots + start, vector, sizeof(jsval) * count);
1643            if (vectorType == SourceVectorAllValues) {
1644                obj->fslots[JSSLOT_ARRAY_COUNT] += count;
1645            } else {
1646                jsuint valueCount = 0;
1647                for (jsuint i = 0; i < count; i++) {
1648                     if (obj->dslots[start + i] != JSVAL_HOLE)
1649                         valueCount++;
1650                }
1651                obj->fslots[JSSLOT_ARRAY_COUNT] += valueCount;
1652            }
1653            JS_ASSERT_IF(count != 0, obj->dslots[newlen - 1] != JSVAL_HOLE);
1654          return JS_TRUE;          return JS_TRUE;
1655      }      }
1656    
1657      while (start != end) {      jsval* end = vector + count;
1658          if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||      while (vector != end && start < MAXINDEX) {
1659            if (!JS_CHECK_OPERATION_LIMIT(cx) ||
1660              !SetArrayElement(cx, obj, start++, *vector++)) {              !SetArrayElement(cx, obj, start++, *vector++)) {
1661              return JS_FALSE;              return JS_FALSE;
1662          }          }
1663      }      }
1664    
1665        if (vector == end)
1666            return JS_TRUE;
1667    
1668        /* Finish out any remaining elements past the max array index. */
1669        if (OBJ_IS_DENSE_ARRAY(cx, obj) && !ENSURE_SLOW_ARRAY(cx, obj))
1670            return JS_FALSE;
1671    
1672        JS_ASSERT(start == MAXINDEX);
1673        jsval tmp[2] = {JSVAL_NULL, JSVAL_NULL};
1674        jsdouble* dp = js_NewWeaklyRootedDouble(cx, MAXINDEX);
1675        if (!dp)
1676            return JS_FALSE;
1677        tmp[0] = DOUBLE_TO_JSVAL(dp);
1678        JSAutoTempValueRooter(cx, JS_ARRAY_LENGTH(tmp), tmp);
1679        JSAutoTempIdRooter idr(cx);
1680        do {
1681            tmp[1] = *vector++;
1682            if (!js_ValueToStringId(cx, tmp[0], idr.addr()) ||
1683                !OBJ_SET_PROPERTY(cx, obj, idr.id(), &tmp[1])) {
1684                return JS_FALSE;
1685            }
1686            *dp += 1;
1687        } while (vector != end);
1688    
1689      return JS_TRUE;      return JS_TRUE;
1690  }  }
1691    
# Line 1517  Line 1698 
1698      obj->fslots[JSSLOT_ARRAY_LENGTH] = length;      obj->fslots[JSSLOT_ARRAY_LENGTH] = length;
1699    
1700      if (vector) {      if (vector) {
1701          if (!EnsureLength(cx, obj, length))          if (!EnsureCapacity(cx, obj, length))
1702              return JS_FALSE;              return JS_FALSE;
1703    
1704          jsuint count = length;          jsuint count = length;
# Line 1541  Line 1722 
1722  static JSString* FASTCALL  static JSString* FASTCALL
1723  Array_p_join(JSContext* cx, JSObject* obj, JSString *str)  Array_p_join(JSContext* cx, JSObject* obj, JSString *str)
1724  {  {
1725      jsval v;      JSAutoTempValueRooter tvr(cx);
1726      if (!array_join_sub(cx, obj, TO_STRING, str, &v))      if (!array_join_sub(cx, obj, TO_STRING, str, tvr.addr())) {
1727            js_SetBuiltinError(cx);
1728          return NULL;          return NULL;
1729      JS_ASSERT(JSVAL_IS_STRING(v));      }
1730      return JSVAL_TO_STRING(v);      return JSVAL_TO_STRING(tvr.value());
1731  }  }
1732    
1733  static JSString* FASTCALL  static JSString* FASTCALL
1734  Array_p_toString(JSContext* cx, JSObject* obj)  Array_p_toString(JSContext* cx, JSObject* obj)
1735  {  {
1736      jsval v;      JSAutoTempValueRooter tvr(cx);
1737      if (!array_join_sub(cx, obj, TO_STRING, NULL, &v))      if (!array_join_sub(cx, obj, TO_STRING, NULL, tvr.addr())) {
1738            js_SetBuiltinError(cx);
1739          return NULL;          return NULL;
1740      JS_ASSERT(JSVAL_IS_STRING(v));      }
1741      return JSVAL_TO_STRING(v);      return JSVAL_TO_STRING(tvr.value());
1742  }  }
1743  #endif  #endif
1744    
# Line 1591  Line 1774 
1774      obj = JS_THIS_OBJECT(cx, vp);      obj = JS_THIS_OBJECT(cx, vp);
1775      if (!obj || !js_GetLengthProperty(cx, obj, &len))      if (!obj || !js_GetLengthProperty(cx, obj, &len))
1776          return JS_FALSE;          return JS_FALSE;
1777        *vp = OBJECT_TO_JSVAL(obj);
1778    
1779        if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj)) {
1780            /* An empty array or an array with no elements is already reversed. */
1781            if (len == 0 || !obj->dslots)
1782                return JS_TRUE;
1783    
1784            /*
1785             * It's actually surprisingly complicated to reverse an array due to the
1786             * orthogonality of array length and array capacity while handling
1787             * leading and trailing holes correctly.  Reversing seems less likely to
1788             * be a common operation than other array mass-mutation methods, so for
1789             * now just take a probably-small memory hit (in the absence of too many
1790             * holes in the array at its start) and ensure that the capacity is
1791             * sufficient to hold all the elements in the array if it were full.
1792             */
1793            if (!EnsureCapacity(cx, obj, len))
1794                return JS_FALSE;
1795    
1796            jsval* lo = &obj->dslots[0];
1797            jsval* hi = &obj->dslots[len - 1];
1798            for (; lo < hi; lo++, hi--) {
1799                 jsval tmp = *lo;
1800                 *lo = *hi;
1801                 *hi = tmp;
1802            }
1803    
1804            /*
1805             * Per ECMA-262, don't update the length of the array, even if the new
1806             * array has trailing holes (and thus the original array began with
1807             * holes).
1808             */
1809            return JS_TRUE;
1810        }
1811    
1812      ok = JS_TRUE;      ok = JS_TRUE;
1813      JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);      JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
1814      half = len / 2;      half = len / 2;
1815      for (i = 0; i < half; i++) {      for (i = 0; i < half; i++) {
1816          ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&          ok = JS_CHECK_OPERATION_LIMIT(cx) &&
1817               GetArrayElement(cx, obj, i, &hole, &tvr.u.value) &&               GetArrayElement(cx, obj, i, &hole, &tvr.u.value) &&
1818               GetArrayElement(cx, obj, len - i - 1, &hole2, vp) &&               GetArrayElement(cx, obj, len - i - 1, &hole2, vp) &&
1819               SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, tvr.u.value) &&               SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, tvr.u.value) &&
# Line 1618  Line 1835 
1835  } MSortArgs;  } MSortArgs;
1836    
1837  /* Helper function for js_MergeSort. */  /* Helper function for js_MergeSort. */
1838  static JSBool  static JS_REQUIRES_STACK JSBool
1839  MergeArrays(MSortArgs *msa, void *src, void *dest, size_t run1, size_t run2)  MergeArrays(MSortArgs *msa, void *src, void *dest, size_t run1, size_t run2)
1840  {  {
1841      void *arg, *a, *b, *c;      void *arg, *a, *b, *c;
# Line 1679  Line 1896 
1896   * This sort is stable, i.e. sequence of equal elements is preserved.   * This sort is stable, i.e. sequence of equal elements is preserved.
1897   * See also bug #224128.   * See also bug #224128.
1898   */   */
1899  JSBool  JS_REQUIRES_STACK JSBool
1900  js_MergeSort(void *src, size_t nel, size_t elsize,  js_MergeSort(void *src, size_t nel, size_t elsize,
1901               JSComparator cmp, void *arg, void *tmp)               JSComparator cmp, void *arg, void *tmp)
1902  {  {
# Line 1765  Line 1982 
1982      jsval       *elemroot;      /* stack needed for js_Invoke */      jsval       *elemroot;      /* stack needed for js_Invoke */
1983  } CompareArgs;  } CompareArgs;
1984    
1985  static JSBool  static JS_REQUIRES_STACK JSBool
1986  sort_compare(void *arg, const void *a, const void *b, int *result)  sort_compare(void *arg, const void *a, const void *b, int *result)
1987  {  {
1988      jsval av = *(const jsval *)a, bv = *(const jsval *)b;      jsval av = *(const jsval *)a, bv = *(const jsval *)b;
# Line 1781  Line 1998 
1998      JS_ASSERT(!JSVAL_IS_VOID(av));      JS_ASSERT(!JSVAL_IS_VOID(av));
1999      JS_ASSERT(!JSVAL_IS_VOID(bv));      JS_ASSERT(!JSVAL_IS_VOID(bv));
2000    
2001      if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP))      if (!JS_CHECK_OPERATION_LIMIT(cx))
2002          return JS_FALSE;          return JS_FALSE;
2003    
2004      invokevp = ca->elemroot;      invokevp = ca->elemroot;
# Line 1819  Line 2036 
2036    
2037      JS_ASSERT(JSVAL_IS_STRING(av));      JS_ASSERT(JSVAL_IS_STRING(av));
2038      JS_ASSERT(JSVAL_IS_STRING(bv));      JS_ASSERT(JSVAL_IS_STRING(bv));
2039      if (!JS_CHECK_OPERATION_LIMIT((JSContext *)arg, JSOW_JUMP))      if (!JS_CHECK_OPERATION_LIMIT((JSContext *)arg))
2040          return JS_FALSE;          return JS_FALSE;
2041    
2042      *result = (int) js_CompareStrings(JSVAL_TO_STRING(av), JSVAL_TO_STRING(bv));      *result = (int) js_CompareStrings(JSVAL_TO_STRING(av), JSVAL_TO_STRING(bv));
# Line 1833  Line 2050 
2050   */   */
2051  JS_STATIC_ASSERT(JSVAL_NULL == 0);  JS_STATIC_ASSERT(JSVAL_NULL == 0);
2052    
2053  static JSBool  static JS_REQUIRES_STACK JSBool
2054  array_sort(JSContext *cx, uintN argc, jsval *vp)  array_sort(JSContext *cx, uintN argc, jsval *vp)
2055  {  {
2056      jsval *argv, fval, *vec, *mergesort_tmp, v;      jsval *argv, fval, *vec, *mergesort_tmp, v;
# Line 1842  Line 2059 
2059      jsuint len, newlen, i, undefs;      jsuint len, newlen, i, undefs;
2060      JSTempValueRooter tvr;      JSTempValueRooter tvr;
2061      JSBool hole;      JSBool hole;
2062      bool ok;      JSBool ok;
2063      size_t elemsize;      size_t elemsize;
2064      JSString *str;      JSString *str;
2065    
# Line 1913  Line 2130 
2130      newlen = 0;      newlen = 0;
2131      all_strings = JS_TRUE;      all_strings = JS_TRUE;
2132      for (i = 0; i < len; i++) {      for (i = 0; i < len; i++) {
2133          ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP);          ok = JS_CHECK_OPERATION_LIMIT(cx);
2134          if (!ok)          if (!ok)
2135              goto out;              goto out;
2136    
# Line 1999  Line 2216 
2216              i = newlen;              i = newlen;
2217              do {              do {
2218                  --i;                  --i;
2219                  ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP);                  ok = JS_CHECK_OPERATION_LIMIT(cx);
2220                  if (!ok)                  if (!ok)
2221                      goto out;                      goto out;
2222                  v = vec[i];                  v = vec[i];
# Line 2065  Line 2282 
2282       * easier.       * easier.
2283       */       */
2284      tvr.count = newlen;      tvr.count = newlen;
2285      ok = InitArrayElements(cx, obj, 0, newlen, vec);      ok = InitArrayElements(cx, obj, 0, newlen, vec, TargetElementsMayContainValues,
2286                               SourceVectorAllValues);
2287      if (!ok)      if (!ok)
2288          goto out;          goto out;
2289    
# Line 2078  Line 2296 
2296      /* Set undefs that sorted after the rest of elements. */      /* Set undefs that sorted after the rest of elements. */
2297      while (undefs != 0) {      while (undefs != 0) {
2298          --undefs;          --undefs;
2299          if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||          if (!JS_CHECK_OPERATION_LIMIT(cx) ||
2300              !SetArrayElement(cx, obj, newlen++, JSVAL_VOID)) {              !SetArrayElement(cx, obj, newlen++, JSVAL_VOID)) {
2301              return JS_FALSE;              return JS_FALSE;
2302          }          }
# Line 2086  Line 2304 
2304    
2305      /* Re-create any holes that sorted to the end of the array. */      /* Re-create any holes that sorted to the end of the array. */
2306      while (len > newlen) {      while (len > newlen) {
2307          if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||          if (!JS_CHECK_OPERATION_LIMIT(cx) ||
2308              !DeleteArrayElement(cx, obj, --len)) {              !DeleteArrayElement(cx, obj, --len)) {
2309              return JS_FALSE;              return JS_FALSE;
2310          }          }
# Line 2101  Line 2319 
2319  static JSBool  static JSBool
2320  array_push_slowly(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)  array_push_slowly(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2321  {  {
2322      jsuint length, newlength;      jsuint length;
2323    
2324      if (!js_GetLengthProperty(cx, obj, &length))      if (!js_GetLengthProperty(cx, obj, &length))
2325          return JS_FALSE;          return JS_FALSE;
2326      newlength = length + argc;      if (!InitArrayElements(cx, obj, length, argc, argv, TargetElementsMayContainValues,
2327      if (!InitArrayElements(cx, obj, length, newlength, argv))                             SourceVectorAllValues)) {
2328          return JS_FALSE;          return JS_FALSE;
2329        }
2330    
2331      /* Per ECMA-262, return the new array length. */      /* Per ECMA-262, return the new array length. */
2332        jsdouble newlength = length + jsdouble(argc);
2333      if (!IndexToValue(cx, newlength, rval))      if (!IndexToValue(cx, newlength, rval))
2334          return JS_FALSE;          return JS_FALSE;
2335      return js_SetLengthProperty(cx, obj, newlength);      return js_SetLengthProperty(cx, obj, newlength);
# Line 2125  Line 2345 
2345          return array_push_slowly(cx, obj, 1, &v, rval);          return array_push_slowly(cx, obj, 1, &v, rval);
2346      }      }
2347    
2348      if (!EnsureLength(cx, obj, length + 1))      if (!EnsureCapacity(cx, obj, length + 1))
2349          return JS_FALSE;          return JS_FALSE;
2350      obj->fslots[JSSLOT_ARRAY_LENGTH] = length + 1;      obj->fslots[JSSLOT_ARRAY_LENGTH] = length + 1;
2351    
# Line 2135  Line 2355 
2355      return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], rval);      return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], rval);
2356  }  }
2357    
2358    JSBool JS_FASTCALL
2359    js_ArrayCompPush(JSContext *cx, JSObject *obj, jsval v)
2360    {
2361        JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
2362        uint32_t length = (uint32_t) obj->fslots[JSSLOT_ARRAY_LENGTH];
2363        JS_ASSERT(length <= js_DenseArrayCapacity(obj));
2364    
2365        if (length == js_DenseArrayCapacity(obj)) {
2366            if (length >= ARRAY_INIT_LIMIT) {
2367                JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
2368                                       JSMSG_ARRAY_INIT_TOO_BIG);
2369                return JS_FALSE;
2370            }
2371    
2372            if (!EnsureCapacity(cx, obj, length + 1))
2373                return JS_FALSE;
2374        }
2375        obj->fslots[JSSLOT_ARRAY_LENGTH] = length + 1;
2376        obj->fslots[JSSLOT_ARRAY_COUNT]++;
2377        obj->dslots[length] = v;
2378        return JS_TRUE;
2379    }
2380    
2381  #ifdef JS_TRACER  #ifdef JS_TRACER
2382  static jsval FASTCALL  static jsval FASTCALL
2383  Array_p_push1(JSContext* cx, JSObject* obj, jsval v)  Array_p_push1(JSContext* cx, JSObject* obj, jsval v)
2384  {  {
2385      if (OBJ_IS_DENSE_ARRAY(cx, obj)      JSAutoTempValueRooter tvr(cx, v);
2386          ? array_push1_dense(cx, obj, v, &v)      if (OBJ_IS_DENSE_ARRAY(cx, obj)
2387          : array_push_slowly(cx, obj, 1, &v, &v)) {          ? array_push1_dense(cx, obj, v, tvr.addr())
2388          return v;          : array_push_slowly(cx, obj, 1, tvr.addr(), tvr.addr())) {
2389            return tvr.value();
2390      }      }
2391      return JSVAL_ERROR_COOKIE;      js_SetBuiltinError(cx);
2392        return JSVAL_VOID;
2393  }  }
2394  #endif  #endif
2395    
# Line 2203  Line 2448 
2448          return JS_FALSE;          return JS_FALSE;
2449      obj->fslots[JSSLOT_ARRAY_LENGTH] = index;      obj->fslots[JSSLOT_ARRAY_LENGTH] = index;
2450      return JS_TRUE;      return JS_TRUE;
       
2451  }  }
2452    
2453  #ifdef JS_TRACER  #ifdef JS_TRACER
2454  static jsval FASTCALL  static jsval FASTCALL
2455  Array_p_pop(JSContext* cx, JSObject* obj)  Array_p_pop(JSContext* cx, JSObject* obj)
2456  {  {
2457      jsval v;      JSAutoTempValueRooter tvr(cx);
2458      if (OBJ_IS_DENSE_ARRAY(cx, obj)      if (OBJ_IS_DENSE_ARRAY(cx, obj)
2459          ? array_pop_dense(cx, obj, &v)          ? array_pop_dense(cx, obj, tvr.addr())
2460          : array_pop_slowly(cx, obj, &v)) {          : array_pop_slowly(cx, obj, tvr.addr())) {
2461          return v;          return tvr.value();
2462      }      }
2463      return JSVAL_ERROR_COOKIE;      js_SetBuiltinError(cx);
2464        return JSVAL_VOID;
2465  }  }
2466  #endif  #endif
2467    
# Line 2228  Line 2473 
2473      obj = JS_THIS_OBJECT(cx, vp);      obj = JS_THIS_OBJECT(cx, vp);
2474      if (!obj)      if (!obj)
2475          return JS_FALSE;          return JS_FALSE;
2476      if (OBJ_IS_DENSE_ARRAY(cx, obj))      if (OBJ_IS_DENSE_ARRAY(cx, obj))
2477          return array_pop_dense(cx, obj, vp);          return array_pop_dense(cx, obj, vp);
2478      return array_pop_slowly(cx, obj, vp);      return array_pop_slowly(cx, obj, vp);
2479  }  }
# Line 2238  Line 2483 
2483  {  {
2484      JSObject *obj;      JSObject *obj;
2485      jsuint length, i;      jsuint length, i;
2486      JSBool hole, ok;      JSBool hole;
     JSTempValueRooter tvr;  
2487    
2488      obj = JS_THIS_OBJECT(cx, vp);      obj = JS_THIS_OBJECT(cx, vp);
2489      if (!obj || !js_GetLengthProperty(cx, obj, &length))      if (!obj || !js_GetLengthProperty(cx, obj, &length))
# Line 2249  Line 2493 
2493      } else {      } else {
2494          length--;          length--;
2495    
2496          /* Get the to-be-deleted property's value into vp ASAP. */          if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) &&
2497          if (!GetArrayElement(cx, obj, 0, &hole, vp))              length < js_DenseArrayCapacity(obj)) {
2498              return JS_FALSE;              if (JS_LIKELY(obj->dslots != NULL)) {
2499                    *vp = obj->dslots[0];
2500                    if (*vp == JSVAL_HOLE)
2501                        *vp = JSVAL_VOID;
2502                    else
2503                        obj->fslots[JSSLOT_ARRAY_COUNT]--;
2504                    memmove(obj->dslots, obj->dslots + 1, length * sizeof(jsval));
2505                    obj->dslots[length] = JSVAL_HOLE;
2506                } else {
2507                    /*
2508                     * We don't need to modify the indexed properties of an empty array
2509                     * with an explicitly set non-zero length when shift() is called on
2510                     * it, but note fallthrough to reduce the length by one.
2511                     */
2512                    JS_ASSERT(obj->fslots[JSSLOT_ARRAY_COUNT] == 0);
2513                    *vp = JSVAL_VOID;
2514                }
2515            } else {
2516                /* Get the to-be-deleted property's value into vp ASAP. */
2517                if (!GetArrayElement(cx, obj, 0, &hole, vp))
2518                    return JS_FALSE;
2519    
2520          /* Slide down the array above the first element. */              /* Slide down the array above the first element. */
2521          ok = JS_TRUE;              JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
2522          JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);              for (i = 0; i != length; i++) {
2523          for (i = 0; i != length; i++) {                  if (!JS_CHECK_OPERATION_LIMIT(cx) ||
2524              ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&                      !GetArrayElement(cx, obj, i + 1, &hole, tvr.addr()) ||
2525                   GetArrayElement(cx, obj, i + 1, &hole, &tvr.u.value) &&                      !SetOrDeleteArrayElement(cx, obj, i, hole, tvr.value())) {
2526                   SetOrDeleteArrayElement(cx, obj, i, hole, tvr.u.value);                      return JS_FALSE;
2527              if (!ok)                  }
2528                  break;              }
         }  
         JS_POP_TEMP_ROOT(cx, &tvr);  
         if (!ok)  
             return JS_FALSE;  
2529    
2530          /* Delete the only or last element when it exist. */              /* Delete the only or last element when it exists. */
2531          if (!hole && !DeleteArrayElement(cx, obj, length))              if (!hole && !DeleteArrayElement(cx, obj, length))
2532              return JS_FALSE;                  return JS_FALSE;
2533            }
2534      }      }
2535      return js_SetLengthProperty(cx, obj, length);      return js_SetLengthProperty(cx, obj, length);
2536  }  }
# Line 2279  Line 2540 
2540  {  {
2541      JSObject *obj;      JSObject *obj;
2542      jsval *argv;      jsval *argv;
2543      jsuint length, last;      jsuint length;
2544      JSBool hole, ok;      JSBool hole;
2545      JSTempValueRooter tvr;      jsdouble last, newlen;
2546    
2547      obj = JS_THIS_OBJECT(cx, vp);      obj = JS_THIS_OBJECT(cx, vp);
2548      if (!obj || !js_GetLengthProperty(cx, obj, &length))      if (!obj || !js_GetLengthProperty(cx, obj, &length))
2549          return JS_FALSE;          return JS_FALSE;
2550        newlen = length;
2551      if (argc > 0) {      if (argc > 0) {
2552          /* Slide up the array to make room for argc at the bottom. */          /* Slide up the array to make room for argc at the bottom. */
2553          argv = JS_ARGV(cx, vp);          argv = JS_ARGV(cx, vp);
2554          if (length > 0) {          if (length > 0) {
2555              last = length;              if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) &&
2556              ok = JS_TRUE;                  !INDEX_TOO_SPARSE(obj, newlen + argc)) {
2557              JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);                  JS_ASSERT(newlen + argc == length + argc);
2558              do {                  if (!EnsureCapacity(cx, obj, length + argc))
2559                  --last;                      return JS_FALSE;
2560                  ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&                  memmove(obj->dslots + argc, obj->dslots, length * sizeof(jsval));
2561                       GetArrayElement(cx, obj, last, &hole, &tvr.u.value) &&                  for (uint32 i = 0; i < argc; i++)
2562                       SetOrDeleteArrayElement(cx, obj, last + argc, hole,                      obj->dslots[i] = JSVAL_HOLE;
2563                                               tvr.u.value);              } else {
2564                  if (!ok)                  last = length;
2565                      break;                  jsdouble upperIndex = last + argc;
2566              } while (last != 0);                  JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
2567              JS_POP_TEMP_ROOT(cx, &tvr);                  do {
2568              if (!ok)                      --last, --upperIndex;
2569                  return JS_FALSE;                      if (!JS_CHECK_OPERATION_LIMIT(cx) ||
2570                            !GetArrayElement(cx, obj, last, &hole, tvr.addr()) ||
2571                            !SetOrDeleteArrayElement(cx, obj, upperIndex, hole, tvr.value())) {
2572                            return JS_FALSE;
2573                        }
2574                    } while (last != 0);
2575                }
2576          }          }
2577    
2578          /* Copy from argv to the bottom of the array. */          /* Copy from argv to the bottom of the array. */
2579          if (!InitArrayElements(cx, obj, 0, argc, argv))          if (!InitArrayElements(cx, obj, 0, argc, argv, TargetElementsAllHoles, SourceVectorAllValues))
2580              return JS_FALSE;              return JS_FALSE;
2581    
2582          length += argc;          newlen += argc;
2583          if (!js_SetLengthProperty(cx, obj, length))          if (!js_SetLengthProperty(cx, obj, newlen))
2584              return JS_FALSE;              return JS_FALSE;
2585      }      }
2586    
2587      /* Follow Perl by returning the new array length. */      /* Follow Perl by returning the new array length. */
2588      return IndexToValue(cx, length, vp);      return IndexToValue(cx, newlen, vp);
2589  }  }
2590    
2591  static JSBool  static JSBool
# Line 2327  Line 2595 
2595      JSObject *obj;      JSObject *obj;
2596      jsuint length, begin, end, count, delta, last;      jsuint length, begin, end, count, delta, last;
2597      jsdouble d;      jsdouble d;
2598      JSBool hole, ok;      JSBool hole;
2599      JSObject *obj2;      JSObject *obj2;
     JSTempValueRooter tvr;  
2600    
2601      /*      /*
2602       * Create a new array value to return.  Our ECMA v2 proposal specs       * Create a new array value to return.  Our ECMA v2 proposal specs
# Line 2386  Line 2653 
2653          argv++;          argv++;
2654      }      }
2655    
2656      MUST_FLOW_THROUGH("out");      JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
     JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);  
2657    
2658      /* If there are elements to remove, put them into the return value. */      /* If there are elements to remove, put them into the return value. */
2659      if (count > 0) {      if (count > 0) {
2660          for (last = begin; last < end; last++) {          if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) &&
2661              ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&              !js_PrototypeHasIndexedProperties(cx, obj2) &&
2662                   GetArrayElement(cx, obj, last, &hole, &tvr.u.value);              end <= js_DenseArrayCapacity(obj)) {
2663              if (!ok)              if (!InitArrayObject(cx, obj2, count, obj->dslots + begin,
2664                  goto out;                                   obj->fslots[JSSLOT_ARRAY_COUNT] !=
2665                                     obj->fslots[JSSLOT_ARRAY_LENGTH])) {
2666                    return JS_FALSE;
2667                }
2668            } else {
2669                for (last = begin; last < end; last++) {
2670                    if (!JS_CHECK_OPERATION_LIMIT(cx) ||
2671                        !GetArrayElement(cx, obj, last, &hole, tvr.addr())) {
2672                        return JS_FALSE;
2673                    }
2674    
2675              /* Copy tvr.u.value to new array unless it's a hole. */                  /* Copy tvr.value() to the new array unless it's a hole. */
2676              if (!hole) {                  if (!hole && !SetArrayElement(cx, obj2, last - begin, tvr.value()))
2677                  ok = SetArrayElement(cx, obj2, last - begin, tvr.u.value);                      return JS_FALSE;
                 if (!ok)  
                     goto out;  
2678              }              }
         }  
2679    
2680          ok = js_SetLengthProperty(cx, obj2, end - begin);              if (!js_SetLengthProperty(cx, obj2, count))
2681          if (!ok)                  return JS_FALSE;
2682              goto out;          }
2683      }      }
2684    
2685      /* Find the direction (up or down) to copy and make way for argv. */      /* Find the direction (up or down) to copy and make way for argv. */
2686      if (argc > count) {      if (argc > count) {
2687          delta = (jsuint)argc - count;          delta = (jsuint)argc - count;
2688          last = length;          last = length;
2689          /* (uint) end could be 0, so can't use vanilla >= test */          if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) &&
2690          while (last-- > end) {              length <= js_DenseArrayCapacity(obj) &&
2691              ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&              (length == 0 || obj->dslots[length - 1] != JSVAL_HOLE)) {
2692                   GetArrayElement(cx, obj, last, &hole, &tvr.u.value) &&              if (!EnsureCapacity(cx, obj, length + delta))
2693                   SetOrDeleteArrayElement(cx, obj, last + delta, hole,                  return JS_FALSE;
2694                                           tvr.u.value);              /* (uint) end could be 0, so we can't use a vanilla >= test. */
2695              if (!ok)              while (last-- > end) {
2696                  goto out;                  jsval srcval = obj->dslots[last];
2697                    jsval* dest = &obj->dslots[last + delta];
2698                    if (*dest == JSVAL_HOLE && srcval != JSVAL_HOLE)
2699                        obj->fslots[JSSLOT_ARRAY_COUNT]++;
2700                    *dest = srcval;
2701                }
2702                obj->fslots[JSSLOT_ARRAY_LENGTH] += delta;
2703            } else {
2704                /* (uint) end could be 0, so we can't use a vanilla >= test. */
2705                while (last-- > end) {
2706                    if (!JS_CHECK_OPERATION_LIMIT(cx) ||
2707                        !GetArrayElement(cx, obj, last, &hole, tvr.addr()) ||
2708                        !SetOrDeleteArrayElement(cx, obj, last + delta, hole, tvr.value())) {
2709                        return JS_FALSE;
2710                    }
2711                }
2712          }          }
2713          length += delta;          length += delta;
2714      } else if (argc < count) {      } else if (argc < count) {
2715          delta = count - (jsuint)argc;          delta = count - (jsuint)argc;
2716          for (last = end; last < length; last++) {          if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_PrototypeHasIndexedProperties(cx, obj) &&
2717              ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&              length <= js_DenseArrayCapacity(obj)) {
2718                   GetArrayElement(cx, obj, last, &hole, &tvr.u.value) &&              /* (uint) end could be 0, so we can't use a vanilla >= test. */
2719                   SetOrDeleteArrayElement(cx, obj, last - delta, hole,              for (last = end; last < length; last++) {
2720                                           tvr.u.value);                  jsval srcval = obj->dslots[last];
2721              if (!ok)                  jsval* dest = &obj->dslots[last - delta];
2722                  goto out;                  if (*dest == JSVAL_HOLE && srcval != JSVAL_HOLE)
2723                        obj->fslots[JSSLOT_ARRAY_COUNT]++;
2724                    *dest = srcval;
2725                }
2726            } else {
2727                for (last = end; last < length; last++) {
2728                    if (!JS_CHECK_OPERATION_LIMIT(cx) ||
2729                        !GetArrayElement(cx, obj, last, &hole, tvr.addr()) ||
2730                        !SetOrDeleteArrayElement(cx, obj, last - delta, hole, tvr.value())) {
2731                        return JS_FALSE;
2732                    }
2733                }
2734          }          }
2735          length -= delta;          length -= delta;
2736      }      }
2737    
2738      /* Copy from argv into the hole to complete the splice. */      /*
2739      ok = InitArrayElements(cx, obj, begin, begin + argc, argv);       * Copy from argv into the hole to complete the splice, and update length in
2740      if (!ok)       * case we deleted elements from the end.
2741          goto out;       */
2742        return InitArrayElements(cx, obj, begin, argc, argv, TargetElementsMayContainValues,
2743      /* Update length in case we deleted elements from the end. */                               SourceVectorAllValues) &&
2744      ok = js_SetLengthProperty(cx, obj, length);             js_SetLengthProperty(cx, obj, length);
   
 out:  
     JS_POP_TEMP_ROOT(cx, &tvr);  
     return ok;  
2745  }  }
2746    
2747  /*  /*
# Line 2471  Line 2765 
2765      aobj = JS_THIS_OBJECT(cx, vp);      aobj = JS_THIS_OBJECT(cx, vp);
2766      if (OBJ_IS_DENSE_ARRAY(cx, aobj)) {      if (OBJ_IS_DENSE_ARRAY(cx, aobj)) {
2767          /*          /*
2768           * Clone aobj but pass the minimum of its length and capacity (aka           * Clone aobj but pass the minimum of its length and capacity, to
2769           * "dense length"), to handle a = [1,2,3]; a.length = 10000 "dense"           * handle a = [1,2,3]; a.length = 10000 "dense" cases efficiently. In
2770           * cases efficiently. In such a case we'll pass 8 (not 3) due to the           * such a case we'll pass 8 (not 3) due to ARRAY_CAPACITY_MIN, which
2771           * ARRAY_GROWBY over-allocation policy, which will cause nobj to be           * will cause nobj to be over-allocated to 16. But in the normal case
2772           * over-allocated to 16. But in the normal case where length is <=           * where length is <= capacity, nobj and aobj will have the same
2773           * capacity, nobj and aobj will have the same dense length.           * capacity.
2774           */           */
2775          length = aobj->fslots[JSSLOT_ARRAY_LENGTH];          length = aobj->fslots[JSSLOT_ARRAY_LENGTH];
2776          jsuint capacity = ARRAY_DENSE_LENGTH(aobj);          jsuint capacity = js_DenseArrayCapacity(aobj);
2777          nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->dslots,          nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->dslots,
2778                                   aobj->fslots[JSSLOT_ARRAY_COUNT] !=                                   aobj->fslots[JSSLOT_ARRAY_COUNT] !=
2779                                   (jsval) length);                                   (jsval) length);
# Line 2504  Line 2798 
2798    
2799      /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */      /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */
2800      for (i = 0; i <= argc; i++) {      for (i = 0; i <= argc; i++) {
2801          ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP);          ok = JS_CHECK_OPERATION_LIMIT(cx);
2802          if (!ok)          if (!ok)
2803              goto out;              goto out;
2804          v = argv[i];          v = argv[i];
# Line 2525  Line 2819 
2819                  if (!ok)                  if (!ok)
2820                      goto out;                      goto out;
2821                  for (slot = 0; slot < alength; slot++) {                  for (slot = 0; slot < alength; slot++) {
2822                      ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&                      ok = JS_CHECK_OPERATION_LIMIT(cx) &&
2823                           GetArrayElement(cx, aobj, slot, &hole,                           GetArrayElement(cx, aobj, slot, &hole,
2824                                           &tvr.u.value);                                           &tvr.u.value);
2825                      if (!ok)                      if (!ok)
# Line 2567  Line 2861 
2861      JSObject *nobj, *obj;      JSObject *nobj, *obj;
2862      jsuint length, begin, end, slot;      jsuint length, begin, end, slot;
2863      jsdouble d;      jsdouble d;
2864      JSBool hole, ok;      JSBool hole;
     JSTempValueRooter tvr;  
2865    
2866      argv = JS_ARGV(cx, vp);      argv = JS_ARGV(cx, vp);
2867    
# Line 2611  Line 2904 
2904      if (begin > end)      if (begin > end)
2905          begin = end;          begin = end;
2906    
2907      if (OBJ_IS_DENSE_ARRAY(cx, obj) && end <= ARRAY_DENSE_LENGTH(obj)) {      if (OBJ_IS_DENSE_ARRAY(cx, obj) && end <= js_DenseArrayCapacity(obj) &&
2908            !js_PrototypeHasIndexedProperties(cx, obj)) {
2909          nobj = js_NewArrayObject(cx, end - begin, obj->dslots + begin,          nobj = js_NewArrayObject(cx, end - begin, obj->dslots + begin,
2910                                   obj->fslots[JSSLOT_ARRAY_COUNT] !=                                   obj->fslots[JSSLOT_ARRAY_COUNT] !=
2911                                   obj->fslots[JSSLOT_ARRAY_LENGTH]);                                   obj->fslots[JSSLOT_ARRAY_LENGTH]);
# Line 2627  Line 2921 
2921          return JS_FALSE;          return JS_FALSE;
2922      *vp = OBJECT_TO_JSVAL(nobj);      *vp = OBJECT_TO_JSVAL(nobj);
2923    
2924      MUST_FLOW_THROUGH("out");      JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
     JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);  
   
2925      for (slot = begin; slot < end; slot++) {      for (slot = begin; slot < end; slot++) {
2926          ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&          if (!JS_CHECK_OPERATION_LIMIT(cx) ||
2927               GetArrayElement(cx, obj, slot, &hole, &tvr.u.value);              !GetArrayElement(cx, obj, slot, &hole, tvr.addr())) {
2928          if (!ok)              return JS_FALSE;
             goto out;  
         if (!hole) {  
             ok = SetArrayElement(cx, nobj, slot - begin, tvr.u.value);  
             if (!ok)  
                 goto out;  
2929          }          }
2930            if (!hole && !SetArrayElement(cx, nobj, slot - begin, tvr.value()))
2931                return JS_FALSE;
2932      }      }
     ok = js_SetLengthProperty(cx, nobj, end - begin);  
2933    
2934  out:      return js_SetLengthProperty(cx, nobj, end - begin);
     JS_POP_TEMP_ROOT(cx, &tvr);  
     return ok;  
2935  }  }
2936    
2937  #if JS_HAS_ARRAY_EXTRAS  #if JS_HAS_ARRAY_EXTRAS
# Line 2703  Line 2989 
2989      }      }
2990    
2991      for (;;) {      for (;;) {
2992          if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||          if (!JS_CHECK_OPERATION_LIMIT(cx) ||
2993              !GetArrayElement(cx, obj, (jsuint)i, &hole, vp)) {              !GetArrayElement(cx, obj, (jsuint)i, &hole, vp)) {
2994              return JS_FALSE;              return JS_FALSE;
2995          }          }
# Line 2744  Line 3030 
3030    
3031  #define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT)  #define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT)
3032    
3033  static JSBool  static JS_REQUIRES_STACK JSBool
3034  array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, jsval *vp)  array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, jsval *vp)
3035  {  {
3036      JSObject *obj;      JSObject *obj;
# Line 2852  Line 3138 
3138      invokevp = elemroot + 1;      invokevp = elemroot + 1;
3139    
3140      for (i = start; i != end; i += step) {      for (i = start; i != end; i += step) {
3141          ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&          ok = JS_CHECK_OPERATION_LIMIT(cx) &&
3142               GetArrayElement(cx, obj, i, &hole, elemroot);               GetArrayElement(cx, obj, i, &hole, elemroot);
3143          if (!ok)          if (!ok)
3144              goto out;              goto out;
# Line 2928  Line 3214 
3214      return ok;      return ok;
3215  }  }
3216    
3217  static JSBool  static JS_REQUIRES_STACK JSBool
3218  array_forEach(JSContext *cx, uintN argc, jsval *vp)  array_forEach(JSContext *cx, uintN argc, jsval *vp)
3219  {  {
3220      return array_extra(cx, FOREACH, argc, vp);      return array_extra(cx, FOREACH, argc, vp);
3221  }  }
3222    
3223  static JSBool  static JS_REQUIRES_STACK JSBool
3224  array_map(JSContext *cx, uintN argc, jsval *vp)  array_map(JSContext *cx, uintN argc, jsval *vp)
3225  {  {
3226      return array_extra(cx, MAP, argc, vp);      return array_extra(cx, MAP, argc, vp);
3227  }  }
3228    
3229  static JSBool  static JS_REQUIRES_STACK JSBool
3230  array_reduce(JSContext *cx, uintN argc, jsval *vp)  array_reduce(JSContext *cx, uintN argc, jsval *vp)
3231  {  {
3232      return array_extra(cx, REDUCE, argc, vp);      return array_extra(cx, REDUCE, argc, vp);
3233  }  }
3234    
3235  static JSBool  static JS_REQUIRES_STACK JSBool
3236  array_reduceRight(JSContext *cx, uintN argc, jsval *vp)  array_reduceRight(JSContext *cx, uintN argc, jsval *vp)
3237  {  {
3238      return array_extra(cx, REDUCE_RIGHT, argc, vp);      return array_extra(cx, REDUCE_RIGHT, argc, vp);
3239  }  }
3240    
3241  static JSBool  static JS_REQUIRES_STACK JSBool
3242  array_filter(JSContext *cx, uintN argc, jsval *vp)  array_filter(JSContext *cx, uintN argc, jsval *vp)
3243  {  {
3244      return array_extra(cx, FILTER, argc, vp);      return array_extra(cx, FILTER, argc, vp);
3245  }  }
3246    
3247  static JSBool  static JS_REQUIRES_STACK JSBool
3248  array_some(JSContext *cx, uintN argc, jsval *vp)  array_some(JSContext *cx, uintN argc, jsval *vp)
3249  {  {
3250      return array_extra(cx, SOME, argc, vp);      return array_extra(cx, SOME, argc, vp);
3251  }  }
3252    
3253  static JSBool  static JS_REQUIRES_STACK JSBool
3254  array_every(JSContext *cx, uintN argc, jsval *vp)  array_every(JSContext *cx, uintN argc, jsval *vp)
3255  {  {
3256      return array_extra(cx, EVERY, argc, vp);      return array_extra(cx, EVERY, argc, vp);
# Line 3029  Line 3315 
3315      jsval *vector;      jsval *vector;
3316    
3317      /* If called without new, replace obj with a new Array object. */      /* If called without new, replace obj with a new Array object. */
3318      if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {      if (!JS_IsConstructing(cx)) {
3319          obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL, 0);          obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL, 0);
3320          if (!obj)          if (!obj)
3321              return JS_FALSE;              return JS_FALSE;
# Line 3060  Line 3346 
3346  #ifdef JS_TRACER  #ifdef JS_TRACER
3347    
3348  JSObject* FASTCALL  JSObject* FASTCALL
3349  js_FastNewArray(JSContext* cx, JSObject* proto)  js_NewEmptyArray(JSContext* cx, JSObject* proto)
3350  {  {
3351      JS_ASSERT(OBJ_IS_ARRAY(cx, proto));      JS_ASSERT(OBJ_IS_ARRAY(cx, proto));
3352    
# Line 3069  Line 3355 
3355      if (!obj)      if (!obj)
3356          return NULL;          return NULL;
3357    
3358      JSClass* clasp = &js_ArrayClass;      /* Initialize all fields of JSObject. */
3359      obj->classword = jsuword(clasp);      obj->map = const_cast<JSObjectMap *>(&SharedArrayMap);
3360        obj->classword = jsuword(&js_ArrayClass);
3361      obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);      obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
3362      obj->fslots[JSSLOT_PARENT] = proto->fslots[JSSLOT_PARENT];      obj->fslots[JSSLOT_PARENT] = proto->fslots[JSSLOT_PARENT];
3363    
# Line 3079  Line 3365 
3365      obj->fslots[JSSLOT_ARRAY_COUNT] = 0;      obj->fslots[JSSLOT_ARRAY_COUNT] = 0;
3366      for (unsigned i = JSSLOT_ARRAY_COUNT + 1; i != JS_INITIAL_NSLOTS; ++i)      for (unsigned i = JSSLOT_ARRAY_COUNT + 1; i != JS_INITIAL_NSLOTS; ++i)
3367          obj->fslots[i] = JSVAL_VOID;          obj->fslots[i] = JSVAL_VOID;
   
     JSObjectOps* ops = clasp->getObjectOps(cx, clasp);  
     obj->map = ops->newObjectMap(cx, 1, ops, clasp, obj);  
     if (!obj->map)  
         return NULL;  
3368      obj->dslots = NULL;      obj->dslots = NULL;
3369      return obj;      return obj;
3370  }  }
3371    
3372  JSObject* FASTCALL  JSObject* FASTCALL
3373  js_Array_1int(JSContext* cx, JSObject* proto, int32 i)  js_NewUninitializedArray(JSContext* cx, JSObject* proto, uint32 len)
3374  {  {
3375      JS_ASSERT(JS_ON_TRACE(cx));      JS_ASSERT(JS_ON_TRACE(cx));
3376      JSObject* obj = js_FastNewArray(cx, proto);      JSObject* obj = js_NewEmptyArray(cx, proto);
3377      if (obj)      if (!obj)
3378          obj->fslots[JSSLOT_ARRAY_LENGTH] = i;          return NULL;
3379        obj->fslots[JSSLOT_ARRAY_LENGTH] = len;
3380        if (!ResizeSlots(cx, obj, 0, JS_MAX(len, ARRAY_CAPACITY_MIN)))
3381            return NULL;
3382      return obj;      return obj;
3383  }  }
3384    
 #define ARRAY_CTOR_GUTS(exact_len, newslots_code)                             \  
     JS_ASSERT(JS_ON_TRACE(cx));                                               \  
     JSObject* obj = js_FastNewArray(cx, proto);                               \  
     if (obj) {                                                                \  
         const uint32 len = ARRAY_GROWBY;                                      \  
         jsval* newslots = (jsval*) JS_malloc(cx, sizeof (jsval) * (len + 1)); \  
         if (newslots) {                                                       \  
             obj->dslots = newslots + 1;                                       \  
             ARRAY_SET_DENSE_LENGTH(obj, len);                                 \  
             {newslots_code}                                                   \  
             while (++newslots < obj->dslots + len)                            \  
                 *newslots = JSVAL_HOLE;                                       \  
             obj->fslots[JSSLOT_ARRAY_LENGTH] = (exact_len);                   \  
             return obj;                                                       \  
         }                                                                     \  
     }                                                                         \  
     return NULL;  
   
 JSObject* FASTCALL  
 js_Array_1str(JSContext* cx, JSObject* proto, JSString *str)  
 {  
     ARRAY_CTOR_GUTS(1, *++newslots = STRING_TO_JSVAL(str);)  
 }  
   
 JSObject* FASTCALL  
 js_Array_2obj(JSContext* cx, JSObject* proto, JSObject *obj1, JSObject* obj2)  
 {  
     ARRAY_CTOR_GUTS(2,  
         *++newslots = OBJECT_TO_JSVAL(obj1);  
         *++newslots = OBJECT_TO_JSVAL(obj2);)  
 }  
   
 JSObject* FASTCALL  
 js_Array_3num(JSContext* cx, JSObject* proto, jsdouble n1, jsdouble n2, jsdouble n3)  
 {  
     ARRAY_CTOR_GUTS(3,  
         if (!js_NewDoubleInRootedValue(cx, n1, ++newslots))  
             return NULL;  
         if (!js_NewDoubleInRootedValue(cx, n2, ++newslots))  
             return NULL;  
         if (!js_NewDoubleInRootedValue(cx, n3, ++newslots))  
             return NULL;)  
 }  
   
3385  #endif /* JS_TRACER */  #endif /* JS_TRACER */
3386    
3387  JSObject *  JSObject *
# Line 3217  Line 3457 
3457                  OBJ_IS_DENSE_ARRAY(cx, array) ? "dense" : "sparse",                  OBJ_IS_DENSE_ARRAY(cx, array) ? "dense" : "sparse",
3458                  array->fslots[JSSLOT_ARRAY_LENGTH]);                  array->fslots[JSSLOT_ARRAY_LENGTH]);
3459          if (OBJ_IS_DENSE_ARRAY(cx, array)) {          if (OBJ_IS_DENSE_ARRAY(cx, array)) {
3460              fprintf(stderr, ", count %lu, denselen %lu",              fprintf(stderr, ", count %lu, capacity %lu",
3461                      array->fslots[JSSLOT_ARRAY_COUNT],                      array->fslots[JSSLOT_ARRAY_COUNT],
3462                      ARRAY_DENSE_LENGTH(array));                      js_DenseArrayCapacity(array));
3463          }          }
3464          fputs(")\n", stderr);          fputs(")\n", stderr);
3465          JS_free(cx, bytes);          JS_free(cx, bytes);
# Line 3419  Line 3659 
3659  }  }
3660    
3661  JS_DEFINE_CALLINFO_4(extern, BOOL,   js_Array_dense_setelem, CONTEXT, OBJECT, INT32, JSVAL,   0, 0)  JS_DEFINE_CALLINFO_4(extern, BOOL,   js_Array_dense_setelem, CONTEXT, OBJECT, INT32, JSVAL,   0, 0)
3662  JS_DEFINE_CALLINFO_2(extern, OBJECT, js_FastNewArray, CONTEXT, OBJECT,                        0, 0)  JS_DEFINE_CALLINFO_2(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT,                       0, 0)
3663  JS_DEFINE_CALLINFO_3(extern, OBJECT, js_Array_1int, CONTEXT, OBJECT, INT32,                   0, 0)  JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewUninitializedArray, CONTEXT, OBJECT, UINT32,       0, 0)
3664  JS_DEFINE_CALLINFO_3(extern, OBJECT, js_Array_1str, CONTEXT, OBJECT, STRING,                  0, 0)  JS_DEFINE_CALLINFO_3(extern, BOOL,   js_ArrayCompPush, CONTEXT, OBJECT, JSVAL,                0, 0)
 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_Array_2obj, CONTEXT, OBJECT, OBJECT, OBJECT,          0, 0)  
 JS_DEFINE_CALLINFO_5(extern, OBJECT, js_Array_3num, CONTEXT, OBJECT, DOUBLE, DOUBLE, DOUBLE,  0, 0)  

Legend:
Removed from v.459  
changed lines
  Added in v.460

  ViewVC Help
Powered by ViewVC 1.1.24