/[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 506 by siliconforks, Sat Sep 26 23:15:22 2009 UTC revision 507 by siliconforks, Sun Jan 10 07:23:34 2010 UTC
# Line 1  Line 1 
1  /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-  /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2   * vim: set sw=4 ts=8 et tw=78:   * vim: set sw=4 ts=8 et tw=78:
3   *   *
4   * ***** BEGIN LICENSE BLOCK *****   * ***** BEGIN LICENSE BLOCK *****
# Line 75  Line 75 
75   * js_SlowArrayClass, but have the same performance characteristics as a dense   * js_SlowArrayClass, but have the same performance characteristics as a dense
76   * array for slot accesses, at some cost in code complexity.   * array for slot accesses, at some cost in code complexity.
77   */   */
 #include "jsstddef.h"  
78  #include <stdlib.h>  #include <stdlib.h>
79  #include <string.h>  #include <string.h>
80  #include "jstypes.h"  #include "jstypes.h"
81    #include "jsstdint.h"
82  #include "jsutil.h" /* Added by JSIFY */  #include "jsutil.h" /* Added by JSIFY */
83  #include "jsapi.h"  #include "jsapi.h"
84  #include "jsarray.h"  #include "jsarray.h"
85  #include "jsatom.h"  #include "jsatom.h"
86  #include "jsbit.h"  #include "jsbit.h"
87  #include "jsbool.h"  #include "jsbool.h"
88    #include "jstracer.h"
89  #include "jsbuiltins.h"  #include "jsbuiltins.h"
90  #include "jscntxt.h"  #include "jscntxt.h"
91  #include "jsversion.h"  #include "jsversion.h"
# Line 99  Line 100 
100  #include "jsscope.h"  #include "jsscope.h"
101  #include "jsstr.h"  #include "jsstr.h"
102  #include "jsstaticcheck.h"  #include "jsstaticcheck.h"
103    #include "jsvector.h"
104    
105    #include "jsatominlines.h"
106    
107  /* 2^32 - 1 as a number and a string */  /* 2^32 - 1 as a number and a string */
108  #define MAXINDEX 4294967295u  #define MAXINDEX 4294967295u
# Line 159  Line 163 
163          return JS_FALSE;          return JS_FALSE;
164    
165      str = JSVAL_TO_STRING(id);      str = JSVAL_TO_STRING(id);
166      cp = JSSTRING_CHARS(str);      cp = str->chars();
167      if (JS7_ISDEC(*cp) && JSSTRING_LENGTH(str) < sizeof(MAXSTR)) {      if (JS7_ISDEC(*cp) && str->length() < sizeof(MAXSTR)) {
168          jsuint index = JS7_UNDEC(*cp++);          jsuint index = JS7_UNDEC(*cp++);
169          jsuint oldIndex = 0;          jsuint oldIndex = 0;
170          jsuint c = 0;          jsuint c = 0;
# Line 220  Line 224 
224  JSBool  JSBool
225  js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)  js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
226  {  {
     JSTempValueRooter tvr;  
     jsid id;  
     JSBool ok;  
     jsint i;  
   
227      if (OBJ_IS_ARRAY(cx, obj)) {      if (OBJ_IS_ARRAY(cx, obj)) {
228          *lengthp = obj->fslots[JSSLOT_ARRAY_LENGTH];          *lengthp = obj->fslots[JSSLOT_ARRAY_LENGTH];
229          return JS_TRUE;          return JS_TRUE;
230      }      }
231    
232      JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);      JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
233      id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);      if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), tvr.addr()))
234      ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value);          return JS_FALSE;
235      if (ok) {  
236          if (JSVAL_IS_INT(tvr.u.value)) {      if (JSVAL_IS_INT(tvr.value())) {
237              i = JSVAL_TO_INT(tvr.u.value);          *lengthp = jsuint(jsint(JSVAL_TO_INT(tvr.value()))); /* jsuint cast does ToUint32 */
238              *lengthp = (jsuint)i;       /* jsuint cast does ToUint32 */          return JS_TRUE;
         } else {  
             *lengthp = js_ValueToECMAUint32(cx, &tvr.u.value);  
             ok = !JSVAL_IS_NULL(tvr.u.value);  
         }  
239      }      }
240      JS_POP_TEMP_ROOT(cx, &tvr);  
241      return ok;      *lengthp = js_ValueToECMAUint32(cx, tvr.addr());
242        return !JSVAL_IS_NULL(tvr.value());
243  }  }
244    
245  static JSBool  static JSBool
# Line 313  Line 309 
309  }  }
310    
311  static JSBool  static JSBool
312  ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldsize, uint32 size)  ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldlen, uint32 newlen,
313                bool initializeAllSlots = true)
314  {  {
315      jsval *slots, *newslots;      jsval *slots, *newslots;
316    
317      if (size == 0) {      if (newlen == 0) {
318          if (obj->dslots) {          if (obj->dslots) {
319              JS_free(cx, obj->dslots - 1);              cx->free(obj->dslots - 1);
320              obj->dslots = NULL;              obj->dslots = NULL;
321          }          }
322          return JS_TRUE;          return JS_TRUE;
323      }      }
324    
325      /*      if (newlen > MAX_DSLOTS_LENGTH) {
      * MAX_DSLOTS_LENGTH is the maximum net capacity supported. Since we allocate  
      * one additional slot to hold the array length, we have to use >= here.  
      */  
     if (size >= MAX_DSLOTS_LENGTH) {  
326          js_ReportAllocationOverflow(cx);          js_ReportAllocationOverflow(cx);
327          return JS_FALSE;          return JS_FALSE;
328      }      }
329    
330      slots = obj->dslots ? obj->dslots - 1 : NULL;      slots = obj->dslots ? obj->dslots - 1 : NULL;
331      newslots = (jsval *) JS_realloc(cx, slots, (size + 1) * sizeof(jsval));      newslots = (jsval *) cx->realloc(slots, (size_t(newlen) + 1) * sizeof(jsval));
332      if (!newslots)      if (!newslots)
333          return JS_FALSE;          return JS_FALSE;
334    
335      obj->dslots = newslots + 1;      obj->dslots = newslots + 1;
336      js_SetDenseArrayCapacity(obj, size);      js_SetDenseArrayCapacity(obj, newlen);
337    
338      for (slots = obj->dslots + oldsize; slots < obj->dslots + size; slots++)      if (initializeAllSlots) {
339          *slots = JSVAL_HOLE;          for (slots = obj->dslots + oldlen; slots < obj->dslots + newlen; slots++)
340                *slots = JSVAL_HOLE;
341        }
342    
343      return JS_TRUE;      return JS_TRUE;
344  }  }
# Line 364  Line 359 
359  #define CAPACITY_CHUNK  (1024 * 1024 / sizeof(jsval))  #define CAPACITY_CHUNK  (1024 * 1024 / sizeof(jsval))
360    
361  static JSBool  static JSBool
362  EnsureCapacity(JSContext *cx, JSObject *obj, uint32 capacity)  EnsureCapacity(JSContext *cx, JSObject *obj, uint32 newcap,
363                   bool initializeAllSlots = true)
364  {  {
365      uint32 oldsize = js_DenseArrayCapacity(obj);      uint32 oldcap = js_DenseArrayCapacity(obj);
366    
367      if (capacity > oldsize) {      if (newcap > oldcap) {
368          /*          /*
369           * If this overflows uint32, capacity is very large. nextsize will end           * If this overflows uint32, newcap is very large. nextsize will end
370           * up being less than capacity, the code below will thus disregard it,           * up being less than newcap, the code below will thus disregard it,
371           * and ResizeSlots will fail.           * and ResizeSlots will fail.
372           *           *
373           * The way we use dslots[-1] forces a few +1s and -1s here. For           * The way we use dslots[-1] forces a few +1s and -1s here. For
374           * example, (oldsize * 2 + 1) produces the sequence 7, 15, 31, 63, ...           * example, (oldcap * 2 + 1) produces the sequence 7, 15, 31, 63, ...
375           * which makes the total allocation size (with dslots[-1]) a power           * which makes the total allocation size (with dslots[-1]) a power
376           * of two.           * of two.
377           */           */
378          uint32 nextsize = (oldsize <= CAPACITY_DOUBLING_MAX)          uint32 nextsize = (oldcap <= CAPACITY_DOUBLING_MAX)
379                            ? oldsize * 2 + 1                            ? oldcap * 2 + 1
380                            : oldsize + (oldsize >> 3);                            : oldcap + (oldcap >> 3);
381    
382          capacity = JS_MAX(capacity, nextsize);          uint32 actualCapacity = JS_MAX(newcap, nextsize);
383          if (capacity >= CAPACITY_CHUNK)          if (actualCapacity >= CAPACITY_CHUNK)
384              capacity = JS_ROUNDUP(capacity + 1, CAPACITY_CHUNK) - 1;  /* -1 for dslots[-1] */              actualCapacity = JS_ROUNDUP(actualCapacity + 1, CAPACITY_CHUNK) - 1; /* -1 for dslots[-1] */
385          else if (capacity < ARRAY_CAPACITY_MIN)          else if (actualCapacity < ARRAY_CAPACITY_MIN)
386              capacity = ARRAY_CAPACITY_MIN;              actualCapacity = ARRAY_CAPACITY_MIN;
387          return ResizeSlots(cx, obj, oldsize, capacity);          if (!ResizeSlots(cx, obj, oldcap, actualCapacity, initializeAllSlots))
388                return JS_FALSE;
389            if (!initializeAllSlots) {
390                /*
391                 * Initialize the slots caller didn't actually ask for.
392                 */
393                for (jsval *slots = obj->dslots + newcap;
394                     slots < obj->dslots + actualCapacity;
395                     slots++) {
396                    *slots = JSVAL_HOLE;
397                }
398            }
399      }      }
400      return JS_TRUE;      return JS_TRUE;
401  }  }
# Line 453  Line 460 
460    
461      JSObject *obj2;      JSObject *obj2;
462      JSProperty *prop;      JSProperty *prop;
463      if (!OBJ_LOOKUP_PROPERTY(cx, obj, idr.id(), &obj2, &prop))      if (!obj->lookupProperty(cx, idr.id(), &obj2, &prop))
464          return JS_FALSE;          return JS_FALSE;
465      if (!prop) {      if (!prop) {
466          *hole = JS_TRUE;          *hole = JS_TRUE;
467          *vp = JSVAL_VOID;          *vp = JSVAL_VOID;
468      } else {      } else {
469          OBJ_DROP_PROPERTY(cx, obj2, prop);          obj2->dropProperty(cx, prop);
470          if (!OBJ_GET_PROPERTY(cx, obj, idr.id(), vp))          if (!obj->getProperty(cx, idr.id(), vp))
471              return JS_FALSE;              return JS_FALSE;
472          *hole = JS_FALSE;          *hole = JS_FALSE;
473      }      }
# Line 502  Line 509 
509          return JS_FALSE;          return JS_FALSE;
510      JS_ASSERT(!JSVAL_IS_VOID(idr.id()));      JS_ASSERT(!JSVAL_IS_VOID(idr.id()));
511    
512      return OBJ_SET_PROPERTY(cx, obj, idr.id(), &v);      return obj->setProperty(cx, idr.id(), &v);
513  }  }
514    
515  static JSBool  static JSBool
# Line 530  Line 537 
537          return JS_TRUE;          return JS_TRUE;
538    
539      jsval junk;      jsval junk;
540      return OBJ_DELETE_PROPERTY(cx, obj, idr.id(), &junk);      return obj->deleteProperty(cx, idr.id(), &junk);
541  }  }
542    
543  /*  /*
# Line 557  Line 564 
564      if (!IndexToValue(cx, length, &v))      if (!IndexToValue(cx, length, &v))
565          return JS_FALSE;          return JS_FALSE;
566      id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);      id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
567      return OBJ_SET_PROPERTY(cx, obj, id, &v);      return obj->setProperty(cx, id, &v);
568  }  }
569    
570  JSBool  JSBool
571  js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)  js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
572  {  {
573      JSErrorReporter older;      JSErrorReporter older = JS_SetErrorReporter(cx, NULL);
574      JSTempValueRooter tvr;      JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
575      jsid id;      jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
576      JSBool ok;      JSBool ok = obj->getProperty(cx, id, tvr.addr());
   
     older = JS_SetErrorReporter(cx, NULL);  
     JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);  
     id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);  
     ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value);  
577      JS_SetErrorReporter(cx, older);      JS_SetErrorReporter(cx, older);
578      if (ok) {      if (!ok)
579          *lengthp = ValueIsLength(cx, &tvr.u.value);          return false;
580          ok = !JSVAL_IS_NULL(tvr.u.value);  
581      }      *lengthp = ValueIsLength(cx, tvr.addr());
582      JS_POP_TEMP_ROOT(cx, &tvr);      return !JSVAL_IS_NULL(tvr.value());
     return ok;  
583  }  }
584    
585  JSBool  JSBool
# Line 586  Line 587 
587  {  {
588      JSClass *clasp;      JSClass *clasp;
589    
590      clasp = OBJ_GET_CLASS(cx, obj);      clasp = OBJ_GET_CLASS(cx, js_GetWrappedObject(cx, obj));
591      *answerp = (clasp == &js_ArgumentsClass || clasp == &js_ArrayClass ||      *answerp = (clasp == &js_ArgumentsClass || clasp == &js_ArrayClass ||
592                  clasp == &js_SlowArrayClass);                  clasp == &js_SlowArrayClass);
593      if (!*answerp) {      if (!*answerp) {
# Line 630  Line 631 
631      if (!OBJ_IS_ARRAY(cx, obj)) {      if (!OBJ_IS_ARRAY(cx, obj)) {
632          jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);          jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
633    
634          return OBJ_DEFINE_PROPERTY(cx, obj, lengthId, *vp, NULL, NULL,          return obj->defineProperty(cx, lengthId, *vp, NULL, NULL, JSPROP_ENUMERATE);
                                    JSPROP_ENUMERATE, NULL);  
635      }      }
636    
637      newlen = ValueIsLength(cx, vp);      newlen = ValueIsLength(cx, vp);
# Line 652  Line 652 
652    
653      if (OBJ_IS_DENSE_ARRAY(cx, obj)) {      if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
654          /* Don't reallocate if we're not actually shrinking our slots. */          /* Don't reallocate if we're not actually shrinking our slots. */
655          jsuint oldsize = js_DenseArrayCapacity(obj);          jsuint capacity = js_DenseArrayCapacity(obj);
656          if (oldsize >= newlen && !ResizeSlots(cx, obj, oldsize, newlen))          if (capacity > newlen && !ResizeSlots(cx, obj, capacity, newlen))
657              return JS_FALSE;              return JS_FALSE;
658      } else if (oldlen - newlen < (1 << 24)) {      } else if (oldlen - newlen < (1 << 24)) {
659          do {          do {
# Line 675  Line 675 
675          if (!iter)          if (!iter)
676              return JS_FALSE;              return JS_FALSE;
677    
678          /* Protect iter against GC in OBJ_DELETE_PROPERTY. */          /* Protect iter against GC under JSObject::deleteProperty. */
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 (;;) {
# Line 686  Line 686 
686              if (JSVAL_IS_VOID(id))              if (JSVAL_IS_VOID(id))
687                  break;                  break;
688              if (js_IdIsIndex(id, &index) && index - newlen < gap) {              if (js_IdIsIndex(id, &index) && index - newlen < gap) {
689                  ok = OBJ_DELETE_PROPERTY(cx, obj, id, &junk);                  ok = obj->deleteProperty(cx, id, &junk);
690                  if (!ok)                  if (!ok)
691                      break;                      break;
692              }              }
# Line 736  Line 736 
736          *propp = NULL;          *propp = NULL;
737          return JS_TRUE;          return JS_TRUE;
738      }      }
739      return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);      return proto->lookupProperty(cx, id, objp, propp);
740  }  }
741    
742  static void  static void
# Line 770  Line 770 
770          return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp);          return IndexToValue(cx, obj->fslots[JSSLOT_ARRAY_LENGTH], vp);
771    
772      if (id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom)) {      if (id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom)) {
773          *vp = STOBJ_GET_SLOT(obj, JSSLOT_PROTO);          *vp = OBJECT_TO_JSVAL(obj->getProto());
774          return JS_TRUE;          return JS_TRUE;
775      }      }
776    
# Line 800  Line 800 
800                  if (!js_NativeGet(cx, obj, obj2, sprop, vp))                  if (!js_NativeGet(cx, obj, obj2, sprop, vp))
801                      return JS_FALSE;                      return JS_FALSE;
802              }              }
803              OBJ_DROP_PROPERTY(cx, obj2, prop);              obj2->dropProperty(cx, prop);
804          }          }
805          return JS_TRUE;          return JS_TRUE;
806      }      }
# Line 882  Line 882 
882       * exists. If we hit the end of the prototype chain, it's safe to set the       * exists. If we hit the end of the prototype chain, it's safe to set the
883       * element on the original object.       * element on the original object.
884       */       */
885     while ((obj = JSVAL_TO_OBJECT(obj->fslots[JSSLOT_PROTO])) != NULL) {      while ((obj = obj->getProto()) != NULL) {
886          /*          /*
887           * If the prototype is a non-native object (possibly a dense array), or           * 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,           * a native object (possibly a slow array) that has indexed properties,
# Line 890  Line 890 
890           */           */
891          if (!OBJ_IS_NATIVE(obj))          if (!OBJ_IS_NATIVE(obj))
892              return JS_TRUE;              return JS_TRUE;
893          if (SCOPE_HAS_INDEXED_PROPERTIES(OBJ_SCOPE(obj)))          if (OBJ_SCOPE(obj)->hadIndexedProperties())
894              return JS_TRUE;              return JS_TRUE;
895      }      }
896      return JS_FALSE;      return JS_FALSE;
897  }  }
898    
899  #ifdef JS_TRACER  #ifdef JS_TRACER
 JSBool FASTCALL  
 js_Array_dense_setelem(JSContext* cx, JSObject* obj, jsint i, jsval v)  
 {  
     JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));  
900    
901    static inline JSBool FASTCALL
902    dense_grow(JSContext* cx, JSObject* obj, jsint i, jsval v)
903    {
904      /*      /*
905       * Let the interpreter worry about negative array indexes.       * Let the interpreter worry about negative array indexes.
906       */       */
907      JS_ASSERT((MAX_DSLOTS_LENGTH > JSVAL_INT_MAX) == (sizeof(jsval) != sizeof(uint32)));      JS_ASSERT((MAX_DSLOTS_LENGTH > MAX_DSLOTS_LENGTH32) == (sizeof(jsval) != sizeof(uint32)));
908      if (MAX_DSLOTS_LENGTH > JSVAL_INT_MAX) {      if (MAX_DSLOTS_LENGTH > MAX_DSLOTS_LENGTH32) {
909          /*          /*
910           * Have to check for negative values bleeding through on 64-bit machines only,           * Have to check for negative values bleeding through on 64-bit machines only,
911           * since we can't allocate large enough arrays for this on 32-bit machines.           * since we can't allocate large enough arrays for this on 32-bit machines.
# Line 935  Line 934 
934      obj->dslots[u] = v;      obj->dslots[u] = v;
935      return JS_TRUE;      return JS_TRUE;
936  }  }
937    
938    
939    JSBool FASTCALL
940    js_Array_dense_setelem(JSContext* cx, JSObject* obj, jsint i, jsval v)
941    {
942        JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
943        return dense_grow(cx, obj, i, v);
944    }
945    JS_DEFINE_CALLINFO_4(extern, BOOL, js_Array_dense_setelem, CONTEXT, OBJECT, INT32, JSVAL, 0, 0)
946    
947    JSBool FASTCALL
948    js_Array_dense_setelem_int(JSContext* cx, JSObject* obj, jsint i, int32 j)
949    {
950        JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
951    
952        jsval v;
953        if (JS_LIKELY(INT_FITS_IN_JSVAL(j))) {
954            v = INT_TO_JSVAL(j);
955        } else {
956            jsdouble d = (jsdouble)j;
957            if (!js_NewDoubleInRootedValue(cx, d, &v))
958                return JS_FALSE;
959        }
960    
961        return dense_grow(cx, obj, i, v);
962    }
963    JS_DEFINE_CALLINFO_4(extern, BOOL, js_Array_dense_setelem_int, CONTEXT, OBJECT, INT32, INT32, 0, 0)
964    
965    JSBool FASTCALL
966    js_Array_dense_setelem_double(JSContext* cx, JSObject* obj, jsint i, jsdouble d)
967    {
968        JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
969    
970        jsval v;
971        jsint j;
972    
973        if (JS_LIKELY(JSDOUBLE_IS_INT(d, j) && INT_FITS_IN_JSVAL(j))) {
974            v = INT_TO_JSVAL(j);
975        } else {
976            if (!js_NewDoubleInRootedValue(cx, d, &v))
977                return JS_FALSE;
978        }
979    
980        return dense_grow(cx, obj, i, v);
981    }
982    JS_DEFINE_CALLINFO_4(extern, BOOL, js_Array_dense_setelem_double, CONTEXT, OBJECT, INT32, DOUBLE, 0, 0)
983  #endif  #endif
984    
985  static JSBool  static JSBool
986  array_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,  array_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
987                       JSPropertyOp getter, JSPropertyOp setter, uintN attrs,                       JSPropertyOp getter, JSPropertyOp setter, uintN attrs)
                      JSProperty **propp)  
988  {  {
989      uint32 i;      uint32 i;
990      JSBool isIndex;      JSBool isIndex;
# Line 949  Line 993 
993          return JS_TRUE;          return JS_TRUE;
994    
995      isIndex = js_IdIsIndex(ID_TO_VALUE(id), &i);      isIndex = js_IdIsIndex(ID_TO_VALUE(id), &i);
996      if (!isIndex || attrs != JSPROP_ENUMERATE) {      if (!isIndex || attrs != JSPROP_ENUMERATE || !OBJ_IS_DENSE_ARRAY(cx, obj) || INDEX_TOO_SPARSE(obj, i)) {
997          if (!ENSURE_SLOW_ARRAY(cx, obj))          if (!ENSURE_SLOW_ARRAY(cx, obj))
998              return JS_FALSE;              return JS_FALSE;
999          return js_DefineProperty(cx, obj, id, value, getter, setter, attrs, propp);          return js_DefineProperty(cx, obj, id, value, getter, setter, attrs);
1000      }      }
1001    
1002      return array_setProperty(cx, obj, id, &value);      return array_setProperty(cx, obj, id, &value);
# Line 1021  Line 1065 
1065   * which is an int-tagged pointer that js_Enumerate creates, we set not one   * which is an int-tagged pointer that js_Enumerate creates, we set not one
1066   * but two lowest bits when tagging a JSIndexIterState pointer -- see   * but two lowest bits when tagging a JSIndexIterState pointer -- see
1067   * INDEX_ITER_TAG usage below. Thus, when slowarray_enumerate receives a state   * INDEX_ITER_TAG usage below. Thus, when slowarray_enumerate receives a state
1068   * tagged with JSVAL_BOOLEAN or with two lowest bits set, it knows that this   * tagged with JSVAL_SPECIAL or with two lowest bits set, it knows that this
1069   * is a fast state so it calls array_enumerate to continue enumerating the   * is a fast state so it calls array_enumerate to continue enumerating the
1070   * indexes present in the original fast array.   * indexes present in the original fast array.
1071   */   */
# Line 1029  Line 1073 
1073  #define PACKED_UINT_PAIR_BITS           14  #define PACKED_UINT_PAIR_BITS           14
1074  #define PACKED_UINT_PAIR_MASK           JS_BITMASK(PACKED_UINT_PAIR_BITS)  #define PACKED_UINT_PAIR_MASK           JS_BITMASK(PACKED_UINT_PAIR_BITS)
1075    
1076  #define UINT_PAIR_TO_BOOLEAN_JSVAL(i,j)                                       \  #define UINT_PAIR_TO_SPECIAL_JSVAL(i,j)                                \
1077      (JS_ASSERT((uint32) (i) <= PACKED_UINT_PAIR_MASK),                        \      (JS_ASSERT((uint32) (i) <= PACKED_UINT_PAIR_MASK),                        \
1078       JS_ASSERT((uint32) (j) <= PACKED_UINT_PAIR_MASK),                        \       JS_ASSERT((uint32) (j) <= PACKED_UINT_PAIR_MASK),                        \
1079       ((jsval) (i) << (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)) |               \       ((jsval) (i) << (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)) |               \
1080       ((jsval) (j) << (JSVAL_TAGBITS)) |                                       \       ((jsval) (j) << (JSVAL_TAGBITS)) |                                       \
1081       (jsval) JSVAL_BOOLEAN)       (jsval) JSVAL_SPECIAL)
1082    
1083  #define BOOLEAN_JSVAL_TO_UINT_PAIR(v,i,j)                                     \  #define SPECIAL_JSVAL_TO_UINT_PAIR(v,i,j)                              \
1084      (JS_ASSERT(JSVAL_TAG(v) == JSVAL_BOOLEAN),                                \      (JS_ASSERT(JSVAL_IS_SPECIAL(v)),                                   \
1085       (i) = (uint32) ((v) >> (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)),         \       (i) = (uint32) ((v) >> (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)),         \
1086       (j) = (uint32) ((v) >> JSVAL_TAGBITS) & PACKED_UINT_PAIR_MASK,           \       (j) = (uint32) ((v) >> JSVAL_TAGBITS) & PACKED_UINT_PAIR_MASK,           \
1087       JS_ASSERT((i) <= PACKED_UINT_PAIR_MASK))       JS_ASSERT((i) <= PACKED_UINT_PAIR_MASK))
# Line 1078  Line 1122 
1122              if (obj->dslots[i] == JSVAL_HOLE) {              if (obj->dslots[i] == JSVAL_HOLE) {
1123                  if (!ii) {                  if (!ii) {
1124                      ii = (JSIndexIterState *)                      ii = (JSIndexIterState *)
1125                           JS_malloc(cx, offsetof(JSIndexIterState, holes) +                           cx->malloc(offsetof(JSIndexIterState, holes) +
1126                                     JS_BITMAP_SIZE(capacity));                                     JS_BITMAP_SIZE(capacity));
1127                      if (!ii)                      if (!ii)
1128                          return JS_FALSE;                          return JS_FALSE;
# Line 1091  Line 1135 
1135          if (!ii) {          if (!ii) {
1136              /* Array has no holes. */              /* Array has no holes. */
1137              if (capacity <= PACKED_UINT_PAIR_MASK) {              if (capacity <= PACKED_UINT_PAIR_MASK) {
1138                  *statep = UINT_PAIR_TO_BOOLEAN_JSVAL(0, capacity);                  *statep = UINT_PAIR_TO_SPECIAL_JSVAL(0, capacity);
1139                  break;                  break;
1140              }              }
1141              ii = (JSIndexIterState *)              ii = (JSIndexIterState *)
1142                   JS_malloc(cx, offsetof(JSIndexIterState, holes));                   cx->malloc(offsetof(JSIndexIterState, holes));
1143              if (!ii)              if (!ii)
1144                  return JS_FALSE;                  return JS_FALSE;
1145              ii->hasHoles = JS_FALSE;              ii->hasHoles = JS_FALSE;
# Line 1107  Line 1151 
1151          break;          break;
1152    
1153        case JSENUMERATE_NEXT:        case JSENUMERATE_NEXT:
1154          if (JSVAL_TAG(*statep) == JSVAL_BOOLEAN) {          if (JSVAL_IS_SPECIAL(*statep)) {
1155              BOOLEAN_JSVAL_TO_UINT_PAIR(*statep, i, capacity);              SPECIAL_JSVAL_TO_UINT_PAIR(*statep, i, capacity);
1156              if (i != capacity) {              if (i != capacity) {
1157                  *idp = INT_TO_JSID(i);                  *idp = INT_TO_JSID(i);
1158                  *statep = UINT_PAIR_TO_BOOLEAN_JSVAL(i + 1, capacity);                  *statep = UINT_PAIR_TO_SPECIAL_JSVAL(i + 1, capacity);
1159                  break;                  break;
1160              }              }
1161          } else {          } else {
# Line 1133  Line 1177 
1177          /* FALL THROUGH */          /* FALL THROUGH */
1178    
1179        case JSENUMERATE_DESTROY:        case JSENUMERATE_DESTROY:
1180          if (JSVAL_TAG(*statep) != JSVAL_BOOLEAN) {          if (!JSVAL_IS_SPECIAL(*statep)) {
1181              JS_ASSERT((*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG);              JS_ASSERT((*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG);
1182              ii = (JSIndexIterState *) (*statep & ~INDEX_ITER_TAG);              ii = (JSIndexIterState *) (*statep & ~INDEX_ITER_TAG);
1183              JS_free(cx, ii);              cx->free(ii);
1184          }          }
1185          *statep = JSVAL_NULL;          *statep = JSVAL_NULL;
1186          break;          break;
# Line 1152  Line 1196 
1196    
1197      /* Are we continuing an enumeration that started when we were dense? */      /* Are we continuing an enumeration that started when we were dense? */
1198      if (enum_op != JSENUMERATE_INIT) {      if (enum_op != JSENUMERATE_INIT) {
1199          if (JSVAL_TAG(*statep) == JSVAL_BOOLEAN ||          if (JSVAL_IS_SPECIAL(*statep) ||
1200              (*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG) {              (*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG) {
1201              return array_enumerate(cx, obj, enum_op, statep, idp);              return array_enumerate(cx, obj, enum_op, statep, idp);
1202          }          }
# Line 1167  Line 1211 
1211  array_finalize(JSContext *cx, JSObject *obj)  array_finalize(JSContext *cx, JSObject *obj)
1212  {  {
1213      if (obj->dslots)      if (obj->dslots)
1214          JS_free(cx, obj->dslots - 1);          cx->free(obj->dslots - 1);
1215      obj->dslots = NULL;      obj->dslots = NULL;
1216  }  }
1217    
# Line 1178  Line 1222 
1222      size_t i;      size_t i;
1223      jsval v;      jsval v;
1224    
1225      JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));      JS_ASSERT(js_IsDenseArray(obj));
1226        obj->traceProtoAndParent(trc);
1227    
1228      capacity = js_DenseArrayCapacity(obj);      capacity = js_DenseArrayCapacity(obj);
1229      for (i = 0; i < capacity; i++) {      for (i = 0; i < capacity; i++) {
# Line 1188  Line 1233 
1233              JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v));              JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v));
1234          }          }
1235      }      }
   
     for (i = JSSLOT_PROTO; i <= JSSLOT_PARENT; ++i) {  
         v = STOBJ_GET_SLOT(obj, i);  
         if (JSVAL_IS_TRACEABLE(v)) {  
             JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);  
             JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v));  
         }  
     }  
1236  }  }
1237    
1238  extern JSObjectOps js_ArrayObjectOps;  extern JSObjectOps js_ArrayObjectOps;
1239    
1240  static const JSObjectMap SharedArrayMap = { &js_ArrayObjectOps };  static const JSObjectMap SharedArrayMap(&js_ArrayObjectOps, JSObjectMap::SHAPELESS);
1241    
1242  JSObjectOps js_ArrayObjectOps = {  JSObjectOps js_ArrayObjectOps = {
1243      &SharedArrayMap,      &SharedArrayMap,
# Line 1212  Line 1249 
1249      NULL,                 array_dropProperty,      NULL,                 array_dropProperty,
1250      NULL,                 NULL,      NULL,                 NULL,
1251      js_HasInstance,       array_trace,      js_HasInstance,       array_trace,
     NULL,                 NULL,  
1252      NULL      NULL
1253  };  };
1254    
# Line 1224  Line 1260 
1260    
1261  JSClass js_ArrayClass = {  JSClass js_ArrayClass = {
1262      "Array",      "Array",
1263      JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array) |      JSCLASS_HAS_RESERVED_SLOTS(2) |
1264      JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_NEW_ENUMERATE,      JSCLASS_HAS_CACHED_PROTO(JSProto_Array) |
1265        JSCLASS_NEW_ENUMERATE,
1266      JS_PropertyStub,    JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,      JS_PropertyStub,    JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,
1267      JS_EnumerateStub,   JS_ResolveStub,    js_TryValueOf,     array_finalize,      JS_EnumerateStub,   JS_ResolveStub,    js_TryValueOf,     array_finalize,
1268      array_getObjectOps, NULL,              NULL,              NULL,      array_getObjectOps, NULL,              NULL,              NULL,
# Line 1234  Line 1271 
1271    
1272  JSClass js_SlowArrayClass = {  JSClass js_SlowArrayClass = {
1273      "Array",      "Array",
1274      JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array),      JSCLASS_HAS_RESERVED_SLOTS(1) |
1275        JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
1276      slowarray_addProperty, JS_PropertyStub, JS_PropertyStub,  JS_PropertyStub,      slowarray_addProperty, JS_PropertyStub, JS_PropertyStub,  JS_PropertyStub,
1277      JS_EnumerateStub,      JS_ResolveStub,  js_TryValueOf,    JS_FinalizeStub,      JS_EnumerateStub,      JS_ResolveStub,  js_TryValueOf,    NULL,
1278      slowarray_getObjectOps, NULL,           NULL,             NULL,      slowarray_getObjectOps, NULL,           NULL,             NULL,
1279      NULL,                  NULL,            NULL,             NULL      NULL,                  NULL,            NULL,             NULL
1280  };  };
# Line 1247  Line 1285 
1285  JSBool  JSBool
1286  js_MakeArraySlow(JSContext *cx, JSObject *obj)  js_MakeArraySlow(JSContext *cx, JSObject *obj)
1287  {  {
1288      JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass);      JS_ASSERT(obj->getClass() == &js_ArrayClass);
1289    
1290      /* Create a native scope. */      /*
1291      JSScope *scope = js_NewScope(cx, &js_SlowArrayObjectOps,       * Create a native scope. All slow arrays other than Array.prototype get
1292                                   &js_SlowArrayClass, obj);       * the same initial shape.
1293         */
1294        uint32 emptyShape;
1295        JSObject *arrayProto = obj->getProto();
1296        if (arrayProto->getClass() == &js_ObjectClass) {
1297            /* obj is Array.prototype. */
1298            emptyShape = js_GenerateShape(cx, false);
1299        } else {
1300            /* arrayProto is Array.prototype. */
1301            JS_ASSERT(arrayProto->getClass() == &js_SlowArrayClass);
1302            emptyShape = OBJ_SCOPE(arrayProto)->emptyScope->shape;
1303        }
1304        JSScope *scope = JSScope::create(cx, &js_SlowArrayObjectOps, &js_SlowArrayClass, obj,
1305                                         emptyShape);
1306      if (!scope)      if (!scope)
1307          return JS_FALSE;          return JS_FALSE;
1308    
# Line 1276  Line 1327 
1327              continue;              continue;
1328          }          }
1329    
1330          sprop = js_AddScopeProperty(cx, scope, id, NULL, NULL,          sprop = scope->add(cx, id, NULL, NULL, i + JS_INITIAL_NSLOTS,
1331                                      i + JS_INITIAL_NSLOTS, JSPROP_ENUMERATE,                             JSPROP_ENUMERATE, 0, 0);
                                     0, 0);  
1332          if (!sprop)          if (!sprop)
1333              goto out_bad;              goto out_bad;
1334      }      }
# Line 1300  Line 1350 
1350      obj->classword ^= (jsuword) &js_ArrayClass;      obj->classword ^= (jsuword) &js_ArrayClass;
1351      obj->classword |= (jsuword) &js_SlowArrayClass;      obj->classword |= (jsuword) &js_SlowArrayClass;
1352    
1353      obj->map = &scope->map;      obj->map = scope;
1354      return JS_TRUE;      return JS_TRUE;
1355    
1356    out_bad:    out_bad:
1357      js_DestroyScope(cx, scope);      JSScope::destroy(cx, scope);
1358      return JS_FALSE;      return JS_FALSE;
1359  }  }
1360    
1361  enum ArrayToStringOp {  /* Transfer ownership of buffer to returned string. */
1362      TO_STRING,  static inline JSBool
1363      TO_LOCALE_STRING,  BufferToString(JSContext *cx, JSCharBuffer &cb, jsval *rval)
1364      TO_SOURCE  {
1365  };      JSString *str = js_NewStringFromCharBuffer(cx, cb);
1366        if (!str)
1367            return false;
1368        *rval = STRING_TO_JSVAL(str);
1369        return true;
1370    }
1371    
1372  /*  #if JS_HAS_TOSOURCE
  * When op is TO_STRING or TO_LOCALE_STRING sep indicates a separator to use  
  * or "," when sep is NULL.  
  * When op is TO_SOURCE sep must be NULL.  
  */  
1373  static JSBool  static JSBool
1374  array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op,  array_toSource(JSContext *cx, uintN argc, jsval *vp)
                JSString *sep, jsval *rval)  
1375  {  {
1376      JSBool ok, hole;      JS_CHECK_RECURSION(cx, return false);
     jsuint length, index;  
     jschar *chars, *ochars;  
     size_t nchars, growth, seplen, tmplen, extratail;  
     const jschar *sepstr;  
     JSString *str;  
     JSHashEntry *he;  
     JSAtom *atom;  
   
     JS_CHECK_RECURSION(cx, return JS_FALSE);  
1377    
1378      ok = js_GetLengthProperty(cx, obj, &length);      JSObject *obj = JS_THIS_OBJECT(cx, vp);
1379      if (!ok)      if (!obj ||
1380          return JS_FALSE;          (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass &&
1381             !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) {
1382            return false;
1383        }
1384    
1385      he = js_EnterSharpObject(cx, obj, NULL, &chars);      /* Find joins or cycles in the reachable object graph. */
1386        jschar *sharpchars;
1387        JSHashEntry *he = js_EnterSharpObject(cx, obj, NULL, &sharpchars);
1388      if (!he)      if (!he)
1389          return JS_FALSE;          return false;
1390  #ifdef DEBUG      bool initiallySharp = IS_SHARP(he);
1391      growth = (size_t) -1;  
1392  #endif      /* After this point, all paths exit through the 'out' label. */
1393        MUST_FLOW_THROUGH("out");
1394        bool ok = false;
1395    
1396      /*      /*
1397       * We must check for the sharp bit and skip js_LeaveSharpObject when it is       * This object will take responsibility for the jschar buffer until the
1398       * set even when op is not TO_SOURCE. A script can overwrite the default       * buffer is transferred to the returned JSString.
      * toSource implementation and trigger a call, for example, to the  
      * toString method during serialization of the object graph (bug 369696).  
1399       */       */
1400      if (IS_SHARP(he)) {      JSCharBuffer cb(cx);
1401    
1402        /* Cycles/joins are indicated by sharp objects. */
1403  #if JS_HAS_SHARP_VARS  #if JS_HAS_SHARP_VARS
1404          nchars = js_strlen(chars);      if (IS_SHARP(he)) {
1405            JS_ASSERT(sharpchars != 0);
1406            cb.replaceRawBuffer(sharpchars, js_strlen(sharpchars));
1407            goto make_string;
1408        } else if (sharpchars) {
1409            MAKE_SHARP(he);
1410            cb.replaceRawBuffer(sharpchars, js_strlen(sharpchars));
1411        }
1412  #else  #else
1413          chars[0] = '[';      if (IS_SHARP(he)) {
1414          chars[1] = ']';          if (!js_AppendLiteral(cb, "[]"))
1415          chars[2] = 0;              goto out;
1416          nchars = 2;          cx->free(sharpchars);
 #endif  
1417          goto make_string;          goto make_string;
1418      }      }
1419    #endif
1420    
1421      if (op == TO_SOURCE) {      if (!cb.append('['))
1422          /*          goto out;
          * Always allocate 2 extra chars for closing ']' and terminating 0  
          * and then preallocate 1 + extratail to include starting '['.  
          */  
         extratail = 2;  
         growth = (1 + extratail) * sizeof(jschar);  
         if (!chars) {  
             nchars = 0;  
             chars = (jschar *) malloc(growth);  
             if (!chars)  
                 goto done;  
         } else {  
             MAKE_SHARP(he);  
             nchars = js_strlen(chars);  
             growth += nchars * sizeof(jschar);  
             chars = (jschar *)realloc((ochars = chars), growth);  
             if (!chars) {  
                 free(ochars);  
                 goto done;  
             }  
         }  
         chars[nchars++] = '[';  
         JS_ASSERT(sep == NULL);  
         sepstr = NULL;  /* indicates to use ", " as separator */  
         seplen = 2;  
     } else {  
         /*  
          * Free any sharp variable definition in chars.  Normally, we would  
          * MAKE_SHARP(he) so that only the first sharp variable annotation is  
          * a definition, and all the rest are references, but in the current  
          * case of (op != TO_SOURCE), we don't need chars at all.  
          */  
         if (chars)  
             JS_free(cx, chars);  
         chars = NULL;  
         nchars = 0;  
         extratail = 1;  /* allocate extra char for terminating 0 */  
   
         /* Return the empty string on a cycle as well as on empty join. */  
         if (IS_BUSY(he) || length == 0) {  
             js_LeaveSharpObject(cx, NULL);  
             *rval = JS_GetEmptyStringValue(cx);  
             return ok;  
         }  
1423    
1424          /* Flag he as BUSY so we can distinguish a cycle from a join-point. */      jsuint length;
1425          MAKE_BUSY(he);      if (!js_GetLengthProperty(cx, obj, &length))
1426            goto out;
1427    
1428          if (sep) {      for (jsuint index = 0; index < length; index++) {
1429              JSSTRING_CHARS_AND_LENGTH(sep, sepstr, seplen);          /* Use vp to locally root each element value. */
1430          } else {          JSBool hole;
1431              sepstr = NULL;      /* indicates to use "," as separator */          if (!JS_CHECK_OPERATION_LIMIT(cx) ||
1432              seplen = 1;              !GetArrayElement(cx, obj, index, &hole, vp)) {
1433                goto out;
1434          }          }
     }  
1435    
1436      /* Use rval to locally root each element value as we loop and convert. */          /* Get element's character string. */
1437      for (index = 0; index < length; index++) {          JSString *str;
1438          ok = (JS_CHECK_OPERATION_LIMIT(cx) &&          if (hole) {
               GetArrayElement(cx, obj, index, &hole, rval));  
         if (!ok)  
             goto done;  
         if (hole ||  
             (op != TO_SOURCE &&  
              (JSVAL_IS_VOID(*rval) || JSVAL_IS_NULL(*rval)))) {  
1439              str = cx->runtime->emptyString;              str = cx->runtime->emptyString;
1440          } else {          } else {
1441              if (op == TO_LOCALE_STRING) {              str = js_ValueToSource(cx, *vp);
1442                  JSObject *robj;              if (!str)
1443                    goto out;
1444            }
1445            *vp = STRING_TO_JSVAL(str);
1446            const jschar *chars;
1447            size_t charlen;
1448            str->getCharsAndLength(chars, charlen);
1449    
1450                  atom = cx->runtime->atomState.toLocaleStringAtom;          /* Append element to buffer. */
1451                  ok = js_ValueToObject(cx, *rval, &robj);          if (!cb.append(chars, charlen))
1452                  if (ok) {              goto out;
1453                      /* Re-use *rval to protect robj temporarily. */          if (index + 1 != length) {
1454                      *rval = OBJECT_TO_JSVAL(robj);              if (!js_AppendLiteral(cb, ", "))
1455                      ok = js_TryMethod(cx, robj, atom, 0, NULL, rval);                  goto out;
1456                  }          } else if (hole) {
1457                  if (!ok)              if (!cb.append(','))
1458                      goto done;                  goto out;
                 str = js_ValueToString(cx, *rval);  
             } else if (op == TO_STRING) {  
                 str = js_ValueToString(cx, *rval);  
             } else {  
                 JS_ASSERT(op == TO_SOURCE);  
                 str = js_ValueToSource(cx, *rval);  
             }  
             if (!str) {  
                 ok = JS_FALSE;  
                 goto done;  
             }  
1459          }          }
1460        }
1461    
1462          /*      /* Finalize the buffer. */
1463           * Do not append separator after the last element unless it is a hole      if (!cb.append(']'))
1464           * and we are in toSource. In that case we append single ",".          goto out;
          */  
         if (index + 1 == length)  
             seplen = (hole && op == TO_SOURCE) ? 1 : 0;  
1465    
1466          /* Allocate 1 at end for closing bracket and zero. */    make_string:
1467          tmplen = JSSTRING_LENGTH(str);      if (!BufferToString(cx, cb, vp))
1468          growth = nchars + tmplen + seplen + extratail;          goto out;
         if (nchars > growth || tmplen > growth ||  
             growth > (size_t)-1 / sizeof(jschar)) {  
             if (chars) {  
                 free(chars);  
                 chars = NULL;  
             }  
             goto done;  
         }  
         growth *= sizeof(jschar);  
         if (!chars) {  
             chars = (jschar *) malloc(growth);  
             if (!chars)  
                 goto done;  
         } else {  
             chars = (jschar *) realloc((ochars = chars), growth);  
             if (!chars) {  
                 free(ochars);  
                 goto done;  
             }  
         }  
1469    
1470          js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen);      ok = true;
         nchars += tmplen;  
1471    
1472          if (seplen) {    out:
1473              if (sepstr) {      if (!initiallySharp)
1474                  js_strncpy(&chars[nchars], sepstr, seplen);          js_LeaveSharpObject(cx, NULL);
1475              } else {      return ok;
1476                  JS_ASSERT(seplen == 1 || seplen == 2);  }
1477                  chars[nchars] = ',';  #endif
1478                  if (seplen == 2)  
1479                      chars[nchars + 1] = ' ';  static JSHashNumber
1480              }  js_hash_array(const void *key)
1481              nchars += seplen;  {
1482        return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS;
1483    }
1484    
1485    bool
1486    js_InitContextBusyArrayTable(JSContext *cx)
1487    {
1488        cx->busyArrayTable = JS_NewHashTable(4, js_hash_array, JS_CompareValues,
1489                                             JS_CompareValues, NULL, NULL);
1490        return cx->busyArrayTable != NULL;
1491    }
1492    
1493    static JSBool
1494    array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
1495                       JSString *sepstr, jsval *rval)
1496    {
1497        JS_CHECK_RECURSION(cx, return false);
1498    
1499        /*
1500         * This hash table is shared between toString invocations and must be empty
1501         * after the root invocation completes.
1502         */
1503        JSHashTable *table = cx->busyArrayTable;
1504    
1505        /*
1506         * Use HashTable entry as the cycle indicator. On first visit, create the
1507         * entry, and, when leaving, remove the entry.
1508         */
1509        JSHashNumber hash = js_hash_array(obj);
1510        JSHashEntry **hep = JS_HashTableRawLookup(table, hash, obj);
1511        JSHashEntry *he = *hep;
1512        if (!he) {
1513            /* Not in hash table, so not a cycle. */
1514            he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
1515            if (!he) {
1516                JS_ReportOutOfMemory(cx);
1517                return false;
1518          }          }
1519        } else {
1520            /* Cycle, so return empty string. */
1521            *rval = ATOM_KEY(cx->runtime->atomState.emptyAtom);
1522            return true;
1523      }      }
1524    
1525    done:      JSAutoTempValueRooter tvr(cx, obj);
1526      if (op == TO_SOURCE) {  
1527          if (chars)      /* After this point, all paths exit through the 'out' label. */
1528              chars[nchars++] = ']';      MUST_FLOW_THROUGH("out");
1529        bool ok = false;
1530    
1531        /* Get characters to use for the separator. */
1532        static const jschar comma = ',';
1533        const jschar *sep;
1534        size_t seplen;
1535        if (sepstr) {
1536            sepstr->getCharsAndLength(sep, seplen);
1537      } else {      } else {
1538          CLEAR_BUSY(he);          sep = &comma;
1539      }          seplen = 1;
     js_LeaveSharpObject(cx, NULL);  
     if (!ok) {  
         if (chars)  
             free(chars);  
         return ok;  
1540      }      }
1541    
1542    make_string:      /*
1543      if (!chars) {       * This object will take responsibility for the jschar buffer until the
1544          JS_ReportOutOfMemory(cx);       * buffer is transferred to the returned JSString.
1545          return JS_FALSE;       */
1546      }      JSCharBuffer cb(cx);
     chars[nchars] = 0;  
     JS_ASSERT(growth == (size_t)-1 || (nchars + 1) * sizeof(jschar) == growth);  
     str = js_NewString(cx, chars, nchars);  
     if (!str) {  
         free(chars);  
         return JS_FALSE;  
     }  
     *rval = STRING_TO_JSVAL(str);  
     return JS_TRUE;  
 }  
1547    
1548  #if JS_HAS_TOSOURCE      jsuint length;
1549  static JSBool      if (!js_GetLengthProperty(cx, obj, &length))
1550  array_toSource(JSContext *cx, uintN argc, jsval *vp)          goto out;
 {  
     JSObject *obj;  
1551    
1552      obj = JS_THIS_OBJECT(cx, vp);      for (jsuint index = 0; index < length; index++) {
1553      if (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass &&          /* Use rval to locally root each element value. */
1554          !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2)) {          JSBool hole;
1555          return JS_FALSE;          if (!JS_CHECK_OPERATION_LIMIT(cx) ||
1556                !GetArrayElement(cx, obj, index, &hole, rval)) {
1557                goto out;
1558            }
1559    
1560            /* Get element's character string. */
1561            if (!(hole || JSVAL_IS_VOID(*rval) || JSVAL_IS_NULL(*rval))) {
1562                if (locale) {
1563                    /* Work on obj.toLocalString() instead. */
1564                    JSObject *robj;
1565    
1566                    if (!js_ValueToObject(cx, *rval, &robj))
1567                        goto out;
1568                    *rval = OBJECT_TO_JSVAL(robj);
1569                    JSAtom *atom = cx->runtime->atomState.toLocaleStringAtom;
1570                    if (!js_TryMethod(cx, robj, atom, 0, NULL, rval))
1571                        goto out;
1572                }
1573    
1574                if (!js_ValueToCharBuffer(cx, *rval, cb))
1575                    goto out;
1576            }
1577    
1578            /* Append the separator. */
1579            if (index + 1 != length) {
1580                if (!cb.append(sep, seplen))
1581                    goto out;
1582            }
1583      }      }
1584      return array_join_sub(cx, obj, TO_SOURCE, NULL, vp);  
1585        /* Finalize the buffer. */
1586        if (!BufferToString(cx, cb, rval))
1587            goto out;
1588    
1589        ok = true;
1590    
1591      out:
1592        /*
1593         * It is possible that 'hep' may have been invalidated by subsequent
1594         * RawAdd/Remove. Hence, 'RawRemove' must not be used.
1595         */
1596        JS_HashTableRemove(table, obj);
1597        return ok;
1598  }  }
 #endif  
1599    
1600  static JSBool  static JSBool
1601  array_toString(JSContext *cx, uintN argc, jsval *vp)  array_toString(JSContext *cx, uintN argc, jsval *vp)
# Line 1554  Line 1603 
1603      JSObject *obj;      JSObject *obj;
1604    
1605      obj = JS_THIS_OBJECT(cx, vp);      obj = JS_THIS_OBJECT(cx, vp);
1606      if (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass &&      if (!obj ||
1607          !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2)) {          (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass &&
1608             !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) {
1609          return JS_FALSE;          return JS_FALSE;
1610      }      }
1611      return array_join_sub(cx, obj, TO_STRING, NULL, vp);  
1612        return array_toString_sub(cx, obj, JS_FALSE, NULL, vp);
1613  }  }
1614    
1615  static JSBool  static JSBool
# Line 1567  Line 1618 
1618      JSObject *obj;      JSObject *obj;
1619    
1620      obj = JS_THIS_OBJECT(cx, vp);      obj = JS_THIS_OBJECT(cx, vp);
1621      if (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass &&      if (!obj ||
1622          !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2)) {          (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass &&
1623             !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) {
1624          return JS_FALSE;          return JS_FALSE;
1625      }      }
1626    
# Line 1576  Line 1628 
1628       *  Passing comma here as the separator. Need a way to get a       *  Passing comma here as the separator. Need a way to get a
1629       *  locale-specific version.       *  locale-specific version.
1630       */       */
1631      return array_join_sub(cx, obj, TO_LOCALE_STRING, NULL, vp);      return array_toString_sub(cx, obj, JS_TRUE, NULL, vp);
1632  }  }
1633    
1634  enum TargetElementsType {  enum TargetElementsType {
# Line 1614  Line 1666 
1666                      JS_ASSERT(ReallyBigIndexToId(cx, index, idr.addr()));                      JS_ASSERT(ReallyBigIndexToId(cx, index, idr.addr()));
1667                      JSObject* obj2;                      JSObject* obj2;
1668                      JSProperty* prop;                      JSProperty* prop;
1669                      JS_ASSERT(OBJ_LOOKUP_PROPERTY(cx, obj, idr.id(), &obj2, &prop));                      JS_ASSERT(obj->lookupProperty(cx, idr.id(), &obj2, &prop));
1670                      JS_ASSERT(!prop);                      JS_ASSERT(!prop);
1671                  }                  }
1672              }              }
# Line 1675  Line 1727 
1727      if (!dp)      if (!dp)
1728          return JS_FALSE;          return JS_FALSE;
1729      tmp[0] = DOUBLE_TO_JSVAL(dp);      tmp[0] = DOUBLE_TO_JSVAL(dp);
1730      JSAutoTempValueRooter(cx, JS_ARRAY_LENGTH(tmp), tmp);      JSAutoTempValueRooter tvr(cx, JS_ARRAY_LENGTH(tmp), tmp);
1731      JSAutoTempIdRooter idr(cx);      JSAutoTempIdRooter idr(cx);
1732      do {      do {
1733          tmp[1] = *vector++;          tmp[1] = *vector++;
1734          if (!js_ValueToStringId(cx, tmp[0], idr.addr()) ||          if (!js_ValueToStringId(cx, tmp[0], idr.addr()) ||
1735              !OBJ_SET_PROPERTY(cx, obj, idr.id(), &tmp[1])) {              !obj->setProperty(cx, idr.id(), &tmp[1])) {
1736              return JS_FALSE;              return JS_FALSE;
1737          }          }
1738          *dp += 1;          *dp += 1;
# Line 1723  Line 1775 
1775  Array_p_join(JSContext* cx, JSObject* obj, JSString *str)  Array_p_join(JSContext* cx, JSObject* obj, JSString *str)
1776  {  {
1777      JSAutoTempValueRooter tvr(cx);      JSAutoTempValueRooter tvr(cx);
1778      if (!array_join_sub(cx, obj, TO_STRING, str, tvr.addr())) {      if (!array_toString_sub(cx, obj, JS_FALSE, str, tvr.addr())) {
1779          js_SetBuiltinError(cx);          js_SetBuiltinError(cx);
1780          return NULL;          return NULL;
1781      }      }
# Line 1734  Line 1786 
1786  Array_p_toString(JSContext* cx, JSObject* obj)  Array_p_toString(JSContext* cx, JSObject* obj)
1787  {  {
1788      JSAutoTempValueRooter tvr(cx);      JSAutoTempValueRooter tvr(cx);
1789      if (!array_join_sub(cx, obj, TO_STRING, NULL, tvr.addr())) {      if (!array_toString_sub(cx, obj, JS_FALSE, NULL, tvr.addr())) {
1790          js_SetBuiltinError(cx);          js_SetBuiltinError(cx);
1791          return NULL;          return NULL;
1792      }      }
# Line 1760  Line 1812 
1812          vp[2] = STRING_TO_JSVAL(str);          vp[2] = STRING_TO_JSVAL(str);
1813      }      }
1814      obj = JS_THIS_OBJECT(cx, vp);      obj = JS_THIS_OBJECT(cx, vp);
1815      return obj && array_join_sub(cx, obj, TO_STRING, str, vp);      return obj && array_toString_sub(cx, obj, JS_FALSE, str, vp);
1816  }  }
1817    
1818  static JSBool  static JSBool
1819  array_reverse(JSContext *cx, uintN argc, jsval *vp)  array_reverse(JSContext *cx, uintN argc, jsval *vp)
1820  {  {
1821      JSObject *obj;      jsuint len;
1822      JSTempValueRooter tvr;      JSObject *obj = JS_THIS_OBJECT(cx, vp);
     jsuint len, half, i;  
     JSBool ok, hole, hole2;  
   
     obj = JS_THIS_OBJECT(cx, vp);  
1823      if (!obj || !js_GetLengthProperty(cx, obj, &len))      if (!obj || !js_GetLengthProperty(cx, obj, &len))
1824          return JS_FALSE;          return JS_FALSE;
1825      *vp = OBJECT_TO_JSVAL(obj);      *vp = OBJECT_TO_JSVAL(obj);
# Line 1809  Line 1857 
1857          return JS_TRUE;          return JS_TRUE;
1858      }      }
1859    
1860      ok = JS_TRUE;      JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
1861      JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);      for (jsuint i = 0, half = len / 2; i < half; i++) {
1862      half = len / 2;          JSBool hole, hole2;
1863      for (i = 0; i < half; i++) {          if (!JS_CHECK_OPERATION_LIMIT(cx) ||
1864          ok = JS_CHECK_OPERATION_LIMIT(cx) &&              !GetArrayElement(cx, obj, i, &hole, tvr.addr()) ||
1865               GetArrayElement(cx, obj, i, &hole, &tvr.u.value) &&              !GetArrayElement(cx, obj, len - i - 1, &hole2, vp) ||
1866               GetArrayElement(cx, obj, len - i - 1, &hole2, vp) &&              !SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, tvr.value()) ||
1867               SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, tvr.u.value) &&              !SetOrDeleteArrayElement(cx, obj, i, hole2, *vp)) {
1868               SetOrDeleteArrayElement(cx, obj, i, hole2, *vp);              return false;
1869          if (!ok)          }
             break;  
1870      }      }
     JS_POP_TEMP_ROOT(cx, &tvr);  
   
1871      *vp = OBJECT_TO_JSVAL(obj);      *vp = OBJECT_TO_JSVAL(obj);
1872      return ok;      return true;
1873  }  }
1874    
1875  typedef struct MSortArgs {  typedef struct MSortArgs {
# Line 1835  Line 1880 
1880  } MSortArgs;  } MSortArgs;
1881    
1882  /* Helper function for js_MergeSort. */  /* Helper function for js_MergeSort. */
1883  static JS_REQUIRES_STACK JSBool  static JSBool
1884  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)
1885  {  {
1886      void *arg, *a, *b, *c;      void *arg, *a, *b, *c;
# Line 1896  Line 1941 
1941   * This sort is stable, i.e. sequence of equal elements is preserved.   * This sort is stable, i.e. sequence of equal elements is preserved.
1942   * See also bug #224128.   * See also bug #224128.
1943   */   */
1944  JS_REQUIRES_STACK JSBool  JSBool
1945  js_MergeSort(void *src, size_t nel, size_t elsize,  js_MergeSort(void *src, size_t nel, size_t elsize,
1946               JSComparator cmp, void *arg, void *tmp)               JSComparator cmp, void *arg, void *tmp)
1947  {  {
# Line 2029  Line 2074 
2074      return JS_TRUE;      return JS_TRUE;
2075  }  }
2076    
2077    typedef JSBool (JS_REQUIRES_STACK *JSRedComparator)(void*, const void*,
2078                                                        const void*, int *);
2079    
2080    static inline JS_IGNORE_STACK JSComparator
2081    comparator_stack_cast(JSRedComparator func)
2082    {
2083        return func;
2084    }
2085    
2086  static int  static int
2087  sort_compare_strings(void *arg, const void *a, const void *b, int *result)  sort_compare_strings(void *arg, const void *a, const void *b, int *result)
2088  {  {
# Line 2050  Line 2104 
2104   */   */
2105  JS_STATIC_ASSERT(JSVAL_NULL == 0);  JS_STATIC_ASSERT(JSVAL_NULL == 0);
2106    
2107  static JS_REQUIRES_STACK JSBool  static JSBool
2108  array_sort(JSContext *cx, uintN argc, jsval *vp)  array_sort(JSContext *cx, uintN argc, jsval *vp)
2109  {  {
2110      jsval *argv, fval, *vec, *mergesort_tmp, v;      jsval *argv, fval, *vec, *mergesort_tmp, v;
# Line 2101  Line 2155 
2155          return JS_FALSE;          return JS_FALSE;
2156      }      }
2157  #endif  #endif
2158      vec = (jsval *) JS_malloc(cx, 2 * (size_t) len * sizeof(jsval));      vec = (jsval *) cx->malloc(2 * (size_t) len * sizeof(jsval));
2159      if (!vec)      if (!vec)
2160          return JS_FALSE;          return JS_FALSE;
2161    
# Line 2230  Line 2284 
2284              } while (i != 0);              } while (i != 0);
2285    
2286              JS_ASSERT(tvr.u.array == vec);              JS_ASSERT(tvr.u.array == vec);
2287              vec = (jsval *) JS_realloc(cx, vec,              vec = (jsval *) cx->realloc(vec,
2288                                         4 * (size_t) newlen * sizeof(jsval));                                          4 * (size_t) newlen * sizeof(jsval));
2289              if (!vec) {              if (!vec) {
2290                  vec = tvr.u.array;                  vec = tvr.u.array;
2291                  ok = JS_FALSE;                  ok = JS_FALSE;
# Line 2262  Line 2316 
2316      } else {      } else {
2317          void *mark;          void *mark;
2318    
2319            js_LeaveTrace(cx);
2320    
2321          ca.context = cx;          ca.context = cx;
2322          ca.fval = fval;          ca.fval = fval;
2323          ca.elemroot  = js_AllocStack(cx, 2 + 2, &mark);          ca.elemroot  = js_AllocStack(cx, 2 + 2, &mark);
# Line 2270  Line 2326 
2326              goto out;              goto out;
2327          }          }
2328          ok = js_MergeSort(vec, (size_t) newlen, sizeof(jsval),          ok = js_MergeSort(vec, (size_t) newlen, sizeof(jsval),
2329                            sort_compare, &ca, mergesort_tmp);                            comparator_stack_cast(sort_compare),
2330                              &ca, mergesort_tmp);
2331          js_FreeStack(cx, mark);          js_FreeStack(cx, mark);
2332          if (!ok)          if (!ok)
2333              goto out;              goto out;
# Line 2289  Line 2346 
2346    
2347    out:    out:
2348      JS_POP_TEMP_ROOT(cx, &tvr);      JS_POP_TEMP_ROOT(cx, &tvr);
2349      JS_free(cx, vec);      cx->free(vec);
2350      if (!ok)      if (!ok)
2351          return JS_FALSE;          return JS_FALSE;
2352    
# Line 2363  Line 2420 
2420      JS_ASSERT(length <= js_DenseArrayCapacity(obj));      JS_ASSERT(length <= js_DenseArrayCapacity(obj));
2421    
2422      if (length == js_DenseArrayCapacity(obj)) {      if (length == js_DenseArrayCapacity(obj)) {
2423          if (length >= ARRAY_INIT_LIMIT) {          if (length > JS_ARGS_LENGTH_MAX) {
2424              JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,              JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
2425                                     JSMSG_ARRAY_INIT_TOO_BIG);                                     JSMSG_ARRAY_INIT_TOO_BIG);
2426              return JS_FALSE;              return JS_FALSE;
# Line 2377  Line 2434 
2434      obj->dslots[length] = v;      obj->dslots[length] = v;
2435      return JS_TRUE;      return JS_TRUE;
2436  }  }
2437    JS_DEFINE_CALLINFO_3(extern, BOOL, js_ArrayCompPush, CONTEXT, OBJECT, JSVAL, 0, 0)
2438    
2439  #ifdef JS_TRACER  #ifdef JS_TRACER
2440  static jsval FASTCALL  static jsval FASTCALL
# Line 2754  Line 2812 
2812      JSObject *aobj, *nobj;      JSObject *aobj, *nobj;
2813      jsuint length, alength, slot;      jsuint length, alength, slot;
2814      uintN i;      uintN i;
2815      JSBool hole, ok;      JSBool hole;
     JSTempValueRooter tvr;  
2816    
2817      /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */      /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
2818      argv = JS_ARGV(cx, vp) - 1;      argv = JS_ARGV(cx, vp) - 1;
# Line 2793  Line 2850 
2850          length = 0;          length = 0;
2851      }      }
2852    
2853      MUST_FLOW_THROUGH("out");      JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
     JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);  
2854    
2855      /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */      /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */
2856      for (i = 0; i <= argc; i++) {      for (i = 0; i <= argc; i++) {
2857          ok = JS_CHECK_OPERATION_LIMIT(cx);          if (!JS_CHECK_OPERATION_LIMIT(cx))
2858          if (!ok)              return false;
             goto out;  
2859          v = argv[i];          v = argv[i];
2860          if (!JSVAL_IS_PRIMITIVE(v)) {          if (!JSVAL_IS_PRIMITIVE(v)) {
2861              JSObject *wobj;              JSObject *wobj;
# Line 2808  Line 2863 
2863              aobj = JSVAL_TO_OBJECT(v);              aobj = JSVAL_TO_OBJECT(v);
2864              wobj = js_GetWrappedObject(cx, aobj);              wobj = js_GetWrappedObject(cx, aobj);
2865              if (OBJ_IS_ARRAY(cx, wobj)) {              if (OBJ_IS_ARRAY(cx, wobj)) {
2866                  ok = OBJ_GET_PROPERTY(cx, aobj,                  jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
2867                                        ATOM_TO_JSID(cx->runtime->atomState                  if (!aobj->getProperty(cx, id, tvr.addr()))
2868                                                     .lengthAtom),                      return false;
2869                                        &tvr.u.value);                  alength = ValueIsLength(cx, tvr.addr());
2870                  if (!ok)                  if (JSVAL_IS_NULL(tvr.value()))
2871                      goto out;                      return false;
                 alength = ValueIsLength(cx, &tvr.u.value);  
                 ok = !JSVAL_IS_NULL(tvr.u.value);  
                 if (!ok)  
                     goto out;  
2872                  for (slot = 0; slot < alength; slot++) {                  for (slot = 0; slot < alength; slot++) {
2873                      ok = JS_CHECK_OPERATION_LIMIT(cx) &&                      if (!JS_CHECK_OPERATION_LIMIT(cx) ||
2874                           GetArrayElement(cx, aobj, slot, &hole,                          !GetArrayElement(cx, aobj, slot, &hole, tvr.addr())) {
2875                                           &tvr.u.value);                          return false;
2876                      if (!ok)                      }
                         goto out;  
2877    
2878                      /*                      /*
2879                       * Per ECMA 262, 15.4.4.4, step 9, ignore non-existent                       * Per ECMA 262, 15.4.4.4, step 9, ignore non-existent
2880                       * properties.                       * properties.
2881                       */                       */
2882                      if (!hole) {                      if (!hole &&
2883                          ok = SetArrayElement(cx, nobj, length + slot,                          !SetArrayElement(cx, nobj, length+slot, tvr.value())) {
2884                                               tvr.u.value);                          return false;
                         if (!ok)  
                             goto out;  
2885                      }                      }
2886                  }                  }
2887                  length += alength;                  length += alength;
# Line 2841  Line 2889 
2889              }              }
2890          }          }
2891    
2892          ok = SetArrayElement(cx, nobj, length, v);          if (!SetArrayElement(cx, nobj, length, v))
2893          if (!ok)              return false;
             goto out;  
2894          length++;          length++;
2895      }      }
2896    
2897      ok = js_SetLengthProperty(cx, nobj, length);      return js_SetLengthProperty(cx, nobj, length);
   
 out:  
     JS_POP_TEMP_ROOT(cx, &tvr);  
     return ok;  
2898  }  }
2899    
2900  static JSBool  static JSBool
# Line 3030  Line 3073 
3073    
3074  #define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT)  #define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT)
3075    
3076  static JS_REQUIRES_STACK JSBool  static JSBool
3077  array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, jsval *vp)  array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, jsval *vp)
3078  {  {
3079      JSObject *obj;      JSObject *obj;
# Line 3128  Line 3171 
3171       * For all but REDUCE, we call with 3 args (value, index, array). REDUCE       * For all but REDUCE, we call with 3 args (value, index, array). REDUCE
3172       * requires 4 args (accum, value, index, array).       * requires 4 args (accum, value, index, array).
3173       */       */
3174        js_LeaveTrace(cx);
3175      argc = 3 + REDUCE_MODE(mode);      argc = 3 + REDUCE_MODE(mode);
3176      elemroot = js_AllocStack(cx, 1 + 2 + argc, &mark);      elemroot = js_AllocStack(cx, 1 + 2 + argc, &mark);
3177      if (!elemroot)      if (!elemroot)
# Line 3214  Line 3258 
3258      return ok;      return ok;
3259  }  }
3260    
3261  static JS_REQUIRES_STACK JSBool  static JSBool
3262  array_forEach(JSContext *cx, uintN argc, jsval *vp)  array_forEach(JSContext *cx, uintN argc, jsval *vp)
3263  {  {
3264      return array_extra(cx, FOREACH, argc, vp);      return array_extra(cx, FOREACH, argc, vp);
3265  }  }
3266    
3267  static JS_REQUIRES_STACK JSBool  static JSBool
3268  array_map(JSContext *cx, uintN argc, jsval *vp)  array_map(JSContext *cx, uintN argc, jsval *vp)
3269  {  {
3270      return array_extra(cx, MAP, argc, vp);      return array_extra(cx, MAP, argc, vp);
3271  }  }
3272    
3273  static JS_REQUIRES_STACK JSBool  static JSBool
3274  array_reduce(JSContext *cx, uintN argc, jsval *vp)  array_reduce(JSContext *cx, uintN argc, jsval *vp)
3275  {  {
3276      return array_extra(cx, REDUCE, argc, vp);      return array_extra(cx, REDUCE, argc, vp);
3277  }  }
3278    
3279  static JS_REQUIRES_STACK JSBool  static JSBool
3280  array_reduceRight(JSContext *cx, uintN argc, jsval *vp)  array_reduceRight(JSContext *cx, uintN argc, jsval *vp)
3281  {  {
3282      return array_extra(cx, REDUCE_RIGHT, argc, vp);      return array_extra(cx, REDUCE_RIGHT, argc, vp);
3283  }  }
3284    
3285  static JS_REQUIRES_STACK JSBool  static JSBool
3286  array_filter(JSContext *cx, uintN argc, jsval *vp)  array_filter(JSContext *cx, uintN argc, jsval *vp)
3287  {  {
3288      return array_extra(cx, FILTER, argc, vp);      return array_extra(cx, FILTER, argc, vp);
3289  }  }
3290    
3291  static JS_REQUIRES_STACK JSBool  static JSBool
3292  array_some(JSContext *cx, uintN argc, jsval *vp)  array_some(JSContext *cx, uintN argc, jsval *vp)
3293  {  {
3294      return array_extra(cx, SOME, argc, vp);      return array_extra(cx, SOME, argc, vp);
3295  }  }
3296    
3297  static JS_REQUIRES_STACK JSBool  static JSBool
3298  array_every(JSContext *cx, uintN argc, jsval *vp)  array_every(JSContext *cx, uintN argc, jsval *vp)
3299  {  {
3300      return array_extra(cx, EVERY, argc, vp);      return array_extra(cx, EVERY, argc, vp);
# Line 3276  Line 3320 
3320  #if JS_HAS_TOSOURCE  #if JS_HAS_TOSOURCE
3321      JS_FN(js_toSource_str,      array_toSource,     0,0),      JS_FN(js_toSource_str,      array_toSource,     0,0),
3322  #endif  #endif
3323      JS_TN(js_toString_str,      array_toString,     0,0, array_toString_trcinfo),      JS_TN(js_toString_str,      array_toString,     0,0, &array_toString_trcinfo),
3324      JS_FN(js_toLocaleString_str,array_toLocaleString,0,0),      JS_FN(js_toLocaleString_str,array_toLocaleString,0,0),
3325    
3326      /* Perl-ish methods. */      /* Perl-ish methods. */
3327      JS_TN("join",               array_join,         1,JSFUN_GENERIC_NATIVE, array_join_trcinfo),      JS_TN("join",               array_join,         1,JSFUN_GENERIC_NATIVE, &array_join_trcinfo),
3328      JS_FN("reverse",            array_reverse,      0,JSFUN_GENERIC_NATIVE),      JS_FN("reverse",            array_reverse,      0,JSFUN_GENERIC_NATIVE),
3329      JS_FN("sort",               array_sort,         1,JSFUN_GENERIC_NATIVE),      JS_FN("sort",               array_sort,         1,JSFUN_GENERIC_NATIVE),
3330      JS_TN("push",               array_push,         1,JSFUN_GENERIC_NATIVE, array_push_trcinfo),      JS_TN("push",               array_push,         1,JSFUN_GENERIC_NATIVE, &array_push_trcinfo),
3331      JS_TN("pop",                array_pop,          0,JSFUN_GENERIC_NATIVE, array_pop_trcinfo),      JS_TN("pop",                array_pop,          0,JSFUN_GENERIC_NATIVE, &array_pop_trcinfo),
3332      JS_FN("shift",              array_shift,        0,JSFUN_GENERIC_NATIVE),      JS_FN("shift",              array_shift,        0,JSFUN_GENERIC_NATIVE),
3333      JS_FN("unshift",            array_unshift,      1,JSFUN_GENERIC_NATIVE),      JS_FN("unshift",            array_unshift,      1,JSFUN_GENERIC_NATIVE),
3334      JS_FN("splice",             array_splice,       2,JSFUN_GENERIC_NATIVE),      JS_FN("splice",             array_splice,       2,JSFUN_GENERIC_NATIVE),
# Line 3316  Line 3360 
3360    
3361      /* If called without new, replace obj with a new Array object. */      /* If called without new, replace obj with a new Array object. */
3362      if (!JS_IsConstructing(cx)) {      if (!JS_IsConstructing(cx)) {
3363          obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL, 0);          obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL);
3364          if (!obj)          if (!obj)
3365              return JS_FALSE;              return JS_FALSE;
3366          *rval = OBJECT_TO_JSVAL(obj);          *rval = OBJECT_TO_JSVAL(obj);
# Line 3351  Line 3395 
3395      JS_ASSERT(OBJ_IS_ARRAY(cx, proto));      JS_ASSERT(OBJ_IS_ARRAY(cx, proto));
3396    
3397      JS_ASSERT(JS_ON_TRACE(cx));      JS_ASSERT(JS_ON_TRACE(cx));
3398      JSObject* obj = (JSObject*) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject));      JSObject* obj = js_NewGCObject(cx, GCX_OBJECT);
3399      if (!obj)      if (!obj)
3400          return NULL;          return NULL;
3401    
3402      /* Initialize all fields of JSObject. */      /* Initialize all fields of JSObject. */
3403      obj->map = const_cast<JSObjectMap *>(&SharedArrayMap);      obj->map = const_cast<JSObjectMap *>(&SharedArrayMap);
3404      obj->classword = jsuword(&js_ArrayClass);      obj->classword = jsuword(&js_ArrayClass);
3405      obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);      obj->setProto(proto);
3406      obj->fslots[JSSLOT_PARENT] = proto->fslots[JSSLOT_PARENT];      obj->setParent(proto->getParent());
3407    
3408      obj->fslots[JSSLOT_ARRAY_LENGTH] = 0;      obj->fslots[JSSLOT_ARRAY_LENGTH] = 0;
3409      obj->fslots[JSSLOT_ARRAY_COUNT] = 0;      obj->fslots[JSSLOT_ARRAY_COUNT] = 0;
# Line 3368  Line 3412 
3412      obj->dslots = NULL;      obj->dslots = NULL;
3413      return obj;      return obj;
3414  }  }
3415    JS_DEFINE_CALLINFO_2(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, 0, 0)
3416    
3417    JSObject* JS_FASTCALL
3418    js_NewEmptyArrayWithLength(JSContext* cx, JSObject* proto, int32 len)
3419    {
3420        if (len < 0)
3421            return NULL;
3422        JSObject *obj = js_NewEmptyArray(cx, proto);
3423        if (!obj)
3424            return NULL;
3425        obj->fslots[JSSLOT_ARRAY_LENGTH] = len;
3426        return obj;
3427    }
3428    JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewEmptyArrayWithLength, CONTEXT, OBJECT, INT32, 0, 0)
3429    
3430  JSObject* FASTCALL  JSObject* FASTCALL
3431  js_NewUninitializedArray(JSContext* cx, JSObject* proto, uint32 len)  js_NewUninitializedArray(JSContext* cx, JSObject* proto, uint32 len)
# Line 3381  Line 3439 
3439          return NULL;          return NULL;
3440      return obj;      return obj;
3441  }  }
3442    JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewUninitializedArray, CONTEXT, OBJECT, UINT32, 0, 0)
3443    
3444  #endif /* JS_TRACER */  #endif /* JS_TRACER */
3445    
# Line 3410  Line 3469 
3469      JSTempValueRooter tvr;      JSTempValueRooter tvr;
3470      JSObject *obj;      JSObject *obj;
3471    
3472      obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL, 0);      obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL);
3473      if (!obj)      if (!obj)
3474          return NULL;          return NULL;
3475    
3476        /*
3477         * If this fails, the global object was not initialized and its class does
3478         * not have JSCLASS_IS_GLOBAL.
3479         */
3480        JS_ASSERT(obj->getProto());
3481    
3482      JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);      JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
3483      if (!InitArrayObject(cx, obj, length, vector, holey))      if (!InitArrayObject(cx, obj, length, vector, holey))
3484          obj = NULL;          obj = NULL;
# Line 3427  Line 3492 
3492  JSObject *  JSObject *
3493  js_NewSlowArrayObject(JSContext *cx)  js_NewSlowArrayObject(JSContext *cx)
3494  {  {
3495      JSObject *obj = js_NewObject(cx, &js_SlowArrayClass, NULL, NULL, 0);      JSObject *obj = js_NewObject(cx, &js_SlowArrayClass, NULL, NULL);
3496      if (obj)      if (obj)
3497          obj->fslots[JSSLOT_ARRAY_LENGTH] = 0;          obj->fslots[JSSLOT_ARRAY_LENGTH] = 0;
3498      return obj;      return obj;
# Line 3450  Line 3515 
3515          if (JSVAL_IS_PRIMITIVE(argv[i]) ||          if (JSVAL_IS_PRIMITIVE(argv[i]) ||
3516              !OBJ_IS_ARRAY(cx, (array = JSVAL_TO_OBJECT(argv[i])))) {              !OBJ_IS_ARRAY(cx, (array = JSVAL_TO_OBJECT(argv[i])))) {
3517              fprintf(stderr, "%s: not array\n", bytes);              fprintf(stderr, "%s: not array\n", bytes);
3518              JS_free(cx, bytes);              cx->free(bytes);
3519              continue;              continue;
3520          }          }
3521          fprintf(stderr, "%s: %s (len %lu", bytes,          fprintf(stderr, "%s: %s (len %lu", bytes,
# Line 3462  Line 3527 
3527                      js_DenseArrayCapacity(array));                      js_DenseArrayCapacity(array));
3528          }          }
3529          fputs(")\n", stderr);          fputs(")\n", stderr);
3530          JS_free(cx, bytes);          cx->free(bytes);
3531      }      }
3532      return JS_TRUE;      return JS_TRUE;
3533  }  }
3534  #endif  #endif
3535    
3536  JS_FRIEND_API(JSBool)  JS_FRIEND_API(JSBool)
3537  js_ArrayToJSUint8Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count,  js_CoerceArrayToCanvasImageData(JSObject *obj, jsuint offset, jsuint count,
3538                          JSUint8 *dest)                                  JSUint8 *dest)
3539  {  {
3540      uint32 length;      uint32 length;
3541    
3542      if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj))      if (!obj || !js_IsDenseArray(obj))
3543          return JS_FALSE;          return JS_FALSE;
3544    
3545      length = obj->fslots[JSSLOT_ARRAY_LENGTH];      length = obj->fslots[JSSLOT_ARRAY_LENGTH];
3546      if (length < offset + count)      if (length < offset + count)
3547          return JS_FALSE;          return JS_FALSE;
3548    
     jsval v;  
     jsint vi;  
   
3549      JSUint8 *dp = dest;      JSUint8 *dp = dest;
3550      for (uintN i = offset; i < offset+count; i++) {      for (uintN i = offset; i < offset+count; i++) {
3551          v = obj->dslots[i];          jsval v = obj->dslots[i];
3552          if (!JSVAL_IS_INT(v) || (vi = JSVAL_TO_INT(v)) < 0)          if (JSVAL_IS_INT(v)) {
3553              return JS_FALSE;              jsint vi = JSVAL_TO_INT(v);
3554                if (jsuint(vi) > 255)
3555          *dp++ = (JSUint8) vi;                  vi = (vi < 0) ? 0 : 255;
3556      }              *dp++ = JSUint8(vi);
3557            } else if (JSVAL_IS_DOUBLE(v)) {
3558      return JS_TRUE;              jsdouble vd = *JSVAL_TO_DOUBLE(v);
3559  }              if (!(vd >= 0)) /* Not < so that NaN coerces to 0 */
3560                    *dp++ = 0;
3561  JS_FRIEND_API(JSBool)              else if (vd > 255)
3562  js_ArrayToJSUint16Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count,                  *dp++ = 255;
3563                           JSUint16 *dest)              else {
3564  {                  jsdouble toTruncate = vd + 0.5;
3565      uint32 length;                  JSUint8 val = JSUint8(toTruncate);
   
     if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj))  
         return JS_FALSE;  
   
     length = obj->fslots[JSSLOT_ARRAY_LENGTH];  
     if (length < offset + count)  
         return JS_FALSE;  
   
     jsval v;  
     jsint vi;  
   
     JSUint16 *dp = dest;  
     for (uintN i = offset; i < offset+count; i++) {  
         v = obj->dslots[i];  
         if (!JSVAL_IS_INT(v) || (vi = JSVAL_TO_INT(v)) < 0)  
             return JS_FALSE;  
   
         *dp++ = (JSUint16) vi;  
     }  
   
     return JS_TRUE;  
 }  
   
 JS_FRIEND_API(JSBool)  
 js_ArrayToJSUint32Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count,  
                          JSUint32 *dest)  
 {  
     uint32 length;  
   
     if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj))  
         return JS_FALSE;  
   
     length = obj->fslots[JSSLOT_ARRAY_LENGTH];  
     if (length < offset + count)  
         return JS_FALSE;  
   
     jsval v;  
     jsint vi;  
   
     JSUint32 *dp = dest;  
     for (uintN i = offset; i < offset+count; i++) {  
         v = obj->dslots[i];  
         if (!JSVAL_IS_INT(v) || (vi = JSVAL_TO_INT(v)) < 0)  
             return JS_FALSE;  
   
         *dp++ = (JSUint32) vi;  
     }  
   
     return JS_TRUE;  
 }  
   
 JS_FRIEND_API(JSBool)  
 js_ArrayToJSInt8Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count,  
                        JSInt8 *dest)  
 {  
     uint32 length;  
   
     if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj))  
         return JS_FALSE;  
   
     length = obj->fslots[JSSLOT_ARRAY_LENGTH];  
     if (length < offset + count)  
         return JS_FALSE;  
   
     jsval v;  
     JSInt8 *dp = dest;  
     for (uintN i = offset; i < offset+count; i++) {  
         v = obj->dslots[i];  
         if (!JSVAL_IS_INT(v))  
             return JS_FALSE;  
   
         *dp++ = (JSInt8) JSVAL_TO_INT(v);  
     }  
   
     return JS_TRUE;  
 }  
   
 JS_FRIEND_API(JSBool)  
 js_ArrayToJSInt16Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count,  
                         JSInt16 *dest)  
 {  
     uint32 length;  
   
     if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj))  
         return JS_FALSE;  
   
     length = obj->fslots[JSSLOT_ARRAY_LENGTH];  
     if (length < offset + count)  
         return JS_FALSE;  
   
     jsval v;  
     JSInt16 *dp = dest;  
     for (uintN i = offset; i < offset+count; i++) {  
         v = obj->dslots[i];  
         if (!JSVAL_IS_INT(v))  
             return JS_FALSE;  
   
         *dp++ = (JSInt16) JSVAL_TO_INT(v);  
     }  
   
     return JS_TRUE;  
 }  
   
 JS_FRIEND_API(JSBool)  
 js_ArrayToJSInt32Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count,  
                         JSInt32 *dest)  
 {  
     uint32 length;  
3566    
3567      if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj))                  /*
3568          return JS_FALSE;                   * now val is rounded to nearest, ties rounded up.  We want
3569                     * rounded to nearest ties to even, so check whether we had a
3570      length = obj->fslots[JSSLOT_ARRAY_LENGTH];                   * tie.
3571      if (length < offset + count)                   */
3572          return JS_FALSE;                  if (val == toTruncate) {
3573                      /*
3574      jsval v;                     * It was a tie (since adding 0.5 gave us the exact integer
3575      JSInt32 *dp = dest;                     * we want).  Since we rounded up, we either already have an
3576      for (uintN i = offset; i < offset+count; i++) {                     * even number or we have an odd number but the number we
3577          v = obj->dslots[i];                     * want is one less.  So just unconditionally masking out the
3578          if (!JSVAL_IS_INT(v))                     * ones bit should do the trick to get us the value we
3579                       * want.
3580                       */
3581                      *dp++ = (val & ~1);
3582                    } else {
3583                      *dp++ = val;
3584                    }
3585                }
3586            } else {
3587              return JS_FALSE;              return JS_FALSE;
3588            }
         *dp++ = (JSInt32) JSVAL_TO_INT(v);  
3589      }      }
3590    
3591      return JS_TRUE;      return JS_TRUE;
3592  }  }
3593    
3594  JS_FRIEND_API(JSBool)  JS_FRIEND_API(JSObject *)
3595  js_ArrayToJSDoubleBuffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count,  js_NewArrayObjectWithCapacity(JSContext *cx, jsuint capacity, jsval **vector)
                          jsdouble *dest)  
3596  {  {
3597      uint32 length;      JSObject *obj = js_NewArrayObject(cx, capacity, NULL);
3598        if (!obj)
3599      if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj))          return NULL;
         return JS_FALSE;  
3600    
3601      length = obj->fslots[JSSLOT_ARRAY_LENGTH];      JSAutoTempValueRooter tvr(cx, obj);
3602      if (length < offset + count)      if (!EnsureCapacity(cx, obj, capacity, JS_FALSE))
3603          return JS_FALSE;          obj = NULL;
3604    
3605      jsval v;      /* Set/clear newborn root, in case we lost it.  */
3606      jsdouble *dp = dest;      cx->weakRoots.newborn[GCX_OBJECT] = obj;
3607      for (uintN i = offset; i < offset+count; i++) {      if (!obj)
3608          v = obj->dslots[i];          return NULL;
         if (JSVAL_IS_INT(v))  
             *dp++ = (jsdouble) JSVAL_TO_INT(v);  
         else if (JSVAL_IS_DOUBLE(v))  
             *dp++ = *(JSVAL_TO_DOUBLE(v));  
         else  
             return JS_FALSE;  
     }  
3609    
3610      return JS_TRUE;      obj->fslots[JSSLOT_ARRAY_COUNT] = capacity;
3611        *vector = obj->dslots;
3612        return obj;
3613  }  }
   
 JS_DEFINE_CALLINFO_4(extern, BOOL,   js_Array_dense_setelem, CONTEXT, OBJECT, INT32, JSVAL,   0, 0)  
 JS_DEFINE_CALLINFO_2(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT,                       0, 0)  
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewUninitializedArray, CONTEXT, OBJECT, UINT32,       0, 0)  
 JS_DEFINE_CALLINFO_3(extern, BOOL,   js_ArrayCompPush, CONTEXT, OBJECT, JSVAL,                0, 0)  

Legend:
Removed from v.506  
changed lines
  Added in v.507

  ViewVC Help
Powered by ViewVC 1.1.24