1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set sw=4 ts=8 et tw=78:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is Mozilla Communicator client code, released
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
44 * 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
46 * methods optimize for denseness by testing that the object's class is
47 * &js_ArrayClass, and can then directly manipulate the slots for efficiency.
49 * We track these pieces of metadata for arrays in dense mode:
50 * - 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,
52 * - the net number of slots starting at dslots (capacity), in dslots[-1] if
55 * In dense mode, holes in the array are represented by JSVAL_HOLE. The final
56 * slot in fslots is unused.
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
63 * Arrays are converted to use js_SlowArrayClass when any of these conditions
65 * - the load factor (COUNT / capacity) is less than 0.25, and there are
66 * more than MIN_SPARSE_INDEX slots total
67 * - a property is set that is not indexed (and not "length"); or
68 * - a property is defined that has non-default property attributes.
70 * Dense arrays do not track property creation order, so unlike other native
71 * objects and slow arrays, enumerating an array does not necessarily visit the
72 * properties in the order they were created. We could instead maintain the
73 * scope to track property enumeration order, but still use the fast slot
74 * 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.
82 #include "jsutil.h" /* Added by JSIFY */
89 #include "jsbuiltins.h"
91 #include "jsversion.h"
92 #include "jsdbgapi.h" /* for js_TraceWatchPoints */
102 #include "jsstaticcheck.h"
103 #include "jsvector.h"
105 #include "jsatominlines.h"
109 /* 2^32 - 1 as a number and a string */
110 #define MAXINDEX 4294967295u
111 #define MAXSTR "4294967295"
113 /* Small arrays are dense, no matter what. */
114 #define MIN_SPARSE_INDEX 256
117 INDEX_TOO_BIG(jsuint index
)
119 return index
> JS_BIT(29) - 1;
122 #define INDEX_TOO_SPARSE(array, index) \
123 (INDEX_TOO_BIG(index) || \
124 ((index) > js_DenseArrayCapacity(array) && (index) >= MIN_SPARSE_INDEX && \
125 (index) > (uint32)((array)->fslots[JSSLOT_ARRAY_COUNT] + 1) * 4))
127 JS_STATIC_ASSERT(sizeof(JSScopeProperty
) > 4 * sizeof(jsval
));
129 #define ENSURE_SLOW_ARRAY(cx, obj) \
130 (OBJ_GET_CLASS(cx, obj) == &js_SlowArrayClass || js_MakeArraySlow(cx, obj))
133 * Determine if the id represents an array index or an XML property index.
135 * An id is an array index according to ECMA by (15.4):
137 * "Array objects give special treatment to a certain class of property names.
138 * A property name P (in the form of a string value) is an array index if and
139 * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
142 * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id)
143 * except that by using signed 32-bit integers we miss the top half of the
144 * valid range. This function checks the string representation itself; note
145 * that calling a standard conversion routine might allow strings such as
146 * "08" or "4.0" as array indices, which they are not.
149 js_IdIsIndex(jsval id
, jsuint
*indexp
)
154 if (JSVAL_IS_INT(id
)) {
156 i
= JSVAL_TO_INT(id
);
163 /* NB: id should be a string, but jsxml.c may call us with an object id. */
164 if (!JSVAL_IS_STRING(id
))
167 str
= JSVAL_TO_STRING(id
);
169 if (JS7_ISDEC(*cp
) && str
->length() < sizeof(MAXSTR
)) {
170 jsuint index
= JS7_UNDEC(*cp
++);
174 while (JS7_ISDEC(*cp
)) {
177 index
= 10*index
+ c
;
182 /* Ensure that all characters were consumed and we didn't overflow. */
184 (oldIndex
< (MAXINDEX
/ 10) ||
185 (oldIndex
== (MAXINDEX
/ 10) && c
< (MAXINDEX
% 10))))
195 ValueIsLength(JSContext
*cx
, jsval
* vp
)
201 if (JSVAL_IS_INT(*vp
)) {
202 i
= JSVAL_TO_INT(*vp
);
208 d
= js_ValueToNumber(cx
, vp
);
209 if (JSVAL_IS_NULL(*vp
))
212 if (JSDOUBLE_IS_NaN(d
))
215 if (d
!= (jsdouble
) length
)
220 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
221 JSMSG_BAD_ARRAY_LENGTH
);
227 js_GetLengthProperty(JSContext
*cx
, JSObject
*obj
, jsuint
*lengthp
)
229 if (OBJ_IS_ARRAY(cx
, obj
)) {
230 *lengthp
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
234 JSAutoTempValueRooter
tvr(cx
, JSVAL_NULL
);
235 if (!obj
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
), tvr
.addr()))
238 if (JSVAL_IS_INT(tvr
.value())) {
239 *lengthp
= jsuint(jsint(JSVAL_TO_INT(tvr
.value()))); /* jsuint cast does ToUint32 */
243 *lengthp
= js_ValueToECMAUint32(cx
, tvr
.addr());
244 return !JSVAL_IS_NULL(tvr
.value());
248 IndexToValue(JSContext
*cx
, jsdouble index
, jsval
*vp
)
250 return js_NewWeaklyRootedNumber(cx
, index
, vp
);
254 js_IndexToId(JSContext
*cx
, jsuint index
, jsid
*idp
)
258 if (index
<= JSVAL_INT_MAX
) {
259 *idp
= INT_TO_JSID(index
);
262 str
= js_NumberToString(cx
, index
);
265 return js_ValueToStringId(cx
, STRING_TO_JSVAL(str
), idp
);
269 BigIndexToId(JSContext
*cx
, JSObject
*obj
, jsuint index
, JSBool createAtom
,
272 jschar buf
[10], *start
;
275 JS_STATIC_ASSERT((jsuint
)-1 == 4294967295U);
277 JS_ASSERT(index
> JSVAL_INT_MAX
);
279 start
= JS_ARRAY_END(buf
);
282 *start
= (jschar
)('0' + index
% 10);
284 } while (index
!= 0);
287 * Skip the atomization if the class is known to store atoms corresponding
288 * to big indexes together with elements. In such case we know that the
289 * array does not have an element at the given index if its atom does not
290 * exist. Fast arrays (clasp == &js_ArrayClass) don't use atoms for
291 * any indexes, though it would be rare to see them have a big index
295 ((clasp
= OBJ_GET_CLASS(cx
, obj
)) == &js_SlowArrayClass
||
296 clasp
== &js_ArgumentsClass
||
297 clasp
== &js_ObjectClass
)) {
298 atom
= js_GetExistingStringAtom(cx
, start
, JS_ARRAY_END(buf
) - start
);
304 atom
= js_AtomizeChars(cx
, start
, JS_ARRAY_END(buf
) - start
, 0);
309 *idp
= ATOM_TO_JSID(atom
);
314 ResizeSlots(JSContext
*cx
, JSObject
*obj
, uint32 oldlen
, uint32 newlen
,
315 bool initializeAllSlots
= true)
317 jsval
*slots
, *newslots
;
321 cx
->free(obj
->dslots
- 1);
327 if (newlen
> MAX_DSLOTS_LENGTH
) {
328 js_ReportAllocationOverflow(cx
);
332 slots
= obj
->dslots
? obj
->dslots
- 1 : NULL
;
333 newslots
= (jsval
*) cx
->realloc(slots
, (size_t(newlen
) + 1) * sizeof(jsval
));
337 obj
->dslots
= newslots
+ 1;
338 js_SetDenseArrayCapacity(obj
, newlen
);
340 if (initializeAllSlots
) {
341 for (slots
= obj
->dslots
+ oldlen
; slots
< obj
->dslots
+ newlen
; slots
++)
349 * When a dense array with CAPACITY_DOUBLING_MAX or fewer slots needs to grow,
350 * double its capacity, to push() N elements in amortized O(N) time.
352 * Above this limit, grow by 12.5% each time. Speed is still amortized O(N),
353 * with a higher constant factor, and we waste less space.
355 #define CAPACITY_DOUBLING_MAX (1024 * 1024)
358 * Round up all large allocations to a multiple of this (1MB), so as not to
359 * waste space if malloc gives us 1MB-sized chunks (as jemalloc does).
361 #define CAPACITY_CHUNK (1024 * 1024 / sizeof(jsval))
364 EnsureCapacity(JSContext
*cx
, JSObject
*obj
, uint32 newcap
,
365 bool initializeAllSlots
= true)
367 uint32 oldcap
= js_DenseArrayCapacity(obj
);
369 if (newcap
> oldcap
) {
371 * If this overflows uint32, newcap is very large. nextsize will end
372 * up being less than newcap, the code below will thus disregard it,
373 * and ResizeSlots will fail.
375 * The way we use dslots[-1] forces a few +1s and -1s here. For
376 * example, (oldcap * 2 + 1) produces the sequence 7, 15, 31, 63, ...
377 * which makes the total allocation size (with dslots[-1]) a power
380 uint32 nextsize
= (oldcap
<= CAPACITY_DOUBLING_MAX
)
382 : oldcap
+ (oldcap
>> 3);
384 uint32 actualCapacity
= JS_MAX(newcap
, nextsize
);
385 if (actualCapacity
>= CAPACITY_CHUNK
)
386 actualCapacity
= JS_ROUNDUP(actualCapacity
+ 1, CAPACITY_CHUNK
) - 1; /* -1 for dslots[-1] */
387 else if (actualCapacity
< ARRAY_CAPACITY_MIN
)
388 actualCapacity
= ARRAY_CAPACITY_MIN
;
389 if (!ResizeSlots(cx
, obj
, oldcap
, actualCapacity
, initializeAllSlots
))
391 if (!initializeAllSlots
) {
393 * Initialize the slots caller didn't actually ask for.
395 for (jsval
*slots
= obj
->dslots
+ newcap
;
396 slots
< obj
->dslots
+ actualCapacity
;
406 ReallyBigIndexToId(JSContext
* cx
, jsdouble index
, jsid
* idp
)
408 JSAutoTempValueRooter
dval(cx
);
409 if (!js_NewDoubleInRootedValue(cx
, index
, dval
.addr()) ||
410 !js_ValueToStringId(cx
, dval
.value(), idp
)) {
417 IndexToId(JSContext
* cx
, JSObject
* obj
, jsdouble index
, JSBool
* hole
, jsid
* idp
,
418 JSBool createAtom
= JS_FALSE
)
420 if (index
<= JSVAL_INT_MAX
) {
421 *idp
= INT_TO_JSID(int(index
));
425 if (index
<= jsuint(-1)) {
426 if (!BigIndexToId(cx
, obj
, jsuint(index
), createAtom
, idp
))
428 if (hole
&& JSVAL_IS_VOID(*idp
))
433 return ReallyBigIndexToId(cx
, index
, idp
);
437 * If the property at the given index exists, get its value into location
438 * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp
439 * to JSVAL_VOID. This function assumes that the location pointed by vp is
440 * properly rooted and can be used as GC-protected storage for temporaries.
443 GetArrayElement(JSContext
*cx
, JSObject
*obj
, jsdouble index
, JSBool
*hole
,
446 JS_ASSERT(index
>= 0);
447 if (OBJ_IS_DENSE_ARRAY(cx
, obj
) && index
< js_DenseArrayCapacity(obj
) &&
448 (*vp
= obj
->dslots
[jsuint(index
)]) != JSVAL_HOLE
) {
453 JSAutoTempIdRooter
idr(cx
);
456 if (!IndexToId(cx
, obj
, index
, hole
, idr
.addr()))
465 if (!obj
->lookupProperty(cx
, idr
.id(), &obj2
, &prop
))
471 obj2
->dropProperty(cx
, prop
);
472 if (!obj
->getProperty(cx
, idr
.id(), vp
))
480 * Set the value of the property at the given index to v assuming v is rooted.
483 SetArrayElement(JSContext
*cx
, JSObject
*obj
, jsdouble index
, jsval v
)
485 JS_ASSERT(index
>= 0);
487 if (OBJ_IS_DENSE_ARRAY(cx
, obj
)) {
488 /* Predicted/prefetched code should favor the remains-dense case. */
489 if (index
<= jsuint(-1)) {
490 jsuint idx
= jsuint(index
);
491 if (!INDEX_TOO_SPARSE(obj
, idx
)) {
492 JS_ASSERT(idx
+ 1 > idx
);
493 if (!EnsureCapacity(cx
, obj
, idx
+ 1))
495 if (idx
>= uint32(obj
->fslots
[JSSLOT_ARRAY_LENGTH
]))
496 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = idx
+ 1;
497 if (obj
->dslots
[idx
] == JSVAL_HOLE
)
498 obj
->fslots
[JSSLOT_ARRAY_COUNT
]++;
499 obj
->dslots
[idx
] = v
;
504 if (!js_MakeArraySlow(cx
, obj
))
508 JSAutoTempIdRooter
idr(cx
);
510 if (!IndexToId(cx
, obj
, index
, NULL
, idr
.addr(), JS_TRUE
))
512 JS_ASSERT(!JSVAL_IS_VOID(idr
.id()));
514 return obj
->setProperty(cx
, idr
.id(), &v
);
518 DeleteArrayElement(JSContext
*cx
, JSObject
*obj
, jsdouble index
)
520 JS_ASSERT(index
>= 0);
521 if (OBJ_IS_DENSE_ARRAY(cx
, obj
)) {
522 if (index
<= jsuint(-1)) {
523 jsuint idx
= jsuint(index
);
524 if (!INDEX_TOO_SPARSE(obj
, idx
) && idx
< js_DenseArrayCapacity(obj
)) {
525 if (obj
->dslots
[idx
] != JSVAL_HOLE
)
526 obj
->fslots
[JSSLOT_ARRAY_COUNT
]--;
527 obj
->dslots
[idx
] = JSVAL_HOLE
;
534 JSAutoTempIdRooter
idr(cx
);
536 if (!IndexToId(cx
, obj
, index
, NULL
, idr
.addr()))
538 if (JSVAL_IS_VOID(idr
.id()))
542 return obj
->deleteProperty(cx
, idr
.id(), &junk
);
546 * When hole is true, delete the property at the given index. Otherwise set
547 * its value to v assuming v is rooted.
550 SetOrDeleteArrayElement(JSContext
*cx
, JSObject
*obj
, jsdouble index
,
551 JSBool hole
, jsval v
)
554 JS_ASSERT(JSVAL_IS_VOID(v
));
555 return DeleteArrayElement(cx
, obj
, index
);
557 return SetArrayElement(cx
, obj
, index
, v
);
561 js_SetLengthProperty(JSContext
*cx
, JSObject
*obj
, jsdouble length
)
566 if (!IndexToValue(cx
, length
, &v
))
568 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
);
569 return obj
->setProperty(cx
, id
, &v
);
573 js_HasLengthProperty(JSContext
*cx
, JSObject
*obj
, jsuint
*lengthp
)
575 JSErrorReporter older
= JS_SetErrorReporter(cx
, NULL
);
576 JSAutoTempValueRooter
tvr(cx
, JSVAL_NULL
);
577 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
);
578 JSBool ok
= obj
->getProperty(cx
, id
, tvr
.addr());
579 JS_SetErrorReporter(cx
, older
);
583 *lengthp
= ValueIsLength(cx
, tvr
.addr());
584 return !JSVAL_IS_NULL(tvr
.value());
588 js_IsArrayLike(JSContext
*cx
, JSObject
*obj
, JSBool
*answerp
, jsuint
*lengthp
)
592 clasp
= OBJ_GET_CLASS(cx
, js_GetWrappedObject(cx
, obj
));
593 *answerp
= (clasp
== &js_ArgumentsClass
|| clasp
== &js_ArrayClass
||
594 clasp
== &js_SlowArrayClass
);
599 return js_GetLengthProperty(cx
, obj
, lengthp
);
603 * The 'length' property of all native Array instances is a shared permanent
604 * property of Array.prototype, so it appears to be a direct property of each
605 * array instance delegating to that Array.prototype. It accesses the private
606 * slot reserved by js_ArrayClass.
608 * Since SpiderMonkey supports cross-class prototype-based delegation, we have
609 * to be careful about the length getter and setter being called on an object
610 * not of Array class. For the getter, we search obj's prototype chain for the
611 * array that caused this getter to be invoked. In the setter case to overcome
612 * the JSPROP_SHARED attribute, we must define a shadowing length property.
615 array_length_getter(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
618 if (OBJ_IS_ARRAY(cx
, obj
))
619 return IndexToValue(cx
, obj
->fslots
[JSSLOT_ARRAY_LENGTH
], vp
);
620 } while ((obj
= OBJ_GET_PROTO(cx
, obj
)) != NULL
);
625 array_length_setter(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
627 jsuint newlen
, oldlen
, gap
, index
;
630 JSTempValueRooter tvr
;
633 if (!OBJ_IS_ARRAY(cx
, obj
)) {
634 jsid lengthId
= ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
);
636 return obj
->defineProperty(cx
, lengthId
, *vp
, NULL
, NULL
, JSPROP_ENUMERATE
);
639 newlen
= ValueIsLength(cx
, vp
);
640 if (JSVAL_IS_NULL(*vp
))
642 oldlen
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
644 if (oldlen
== newlen
)
647 if (!IndexToValue(cx
, newlen
, vp
))
650 if (oldlen
< newlen
) {
651 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = newlen
;
655 if (OBJ_IS_DENSE_ARRAY(cx
, obj
)) {
656 /* Don't reallocate if we're not actually shrinking our slots. */
657 jsuint capacity
= js_DenseArrayCapacity(obj
);
658 if (capacity
> newlen
&& !ResizeSlots(cx
, obj
, capacity
, newlen
))
660 } else if (oldlen
- newlen
< (1 << 24)) {
663 if (!JS_CHECK_OPERATION_LIMIT(cx
) ||
664 !DeleteArrayElement(cx
, obj
, oldlen
)) {
667 } while (oldlen
!= newlen
);
670 * We are going to remove a lot of indexes in a presumably sparse
671 * array. So instead of looping through indexes between newlen and
672 * oldlen, we iterate through all properties and remove those that
673 * correspond to indexes in the half-open range [newlen, oldlen). See
676 iter
= JS_NewPropertyIterator(cx
, obj
);
680 /* Protect iter against GC under JSObject::deleteProperty. */
681 JS_PUSH_TEMP_ROOT_OBJECT(cx
, iter
, &tvr
);
682 gap
= oldlen
- newlen
;
684 ok
= (JS_CHECK_OPERATION_LIMIT(cx
) &&
685 JS_NextProperty(cx
, iter
, &id
));
688 if (JSVAL_IS_VOID(id
))
690 if (js_IdIsIndex(id
, &index
) && index
- newlen
< gap
) {
691 ok
= obj
->deleteProperty(cx
, id
, &junk
);
696 JS_POP_TEMP_ROOT(cx
, &tvr
);
701 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = newlen
;
706 * We have only indexed properties up to capacity (excepting holes), plus the
707 * length property. For all else, we delegate to the prototype.
710 IsDenseArrayId(JSContext
*cx
, JSObject
*obj
, jsid id
)
712 JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx
, obj
));
715 return id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
) ||
716 (js_IdIsIndex(id
, &i
) &&
717 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] != 0 &&
718 i
< js_DenseArrayCapacity(obj
) &&
719 obj
->dslots
[i
] != JSVAL_HOLE
);
723 array_lookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
726 if (!OBJ_IS_DENSE_ARRAY(cx
, obj
))
727 return js_LookupProperty(cx
, obj
, id
, objp
, propp
);
729 if (IsDenseArrayId(cx
, obj
, id
)) {
730 *propp
= (JSProperty
*) id
;
735 JSObject
*proto
= STOBJ_GET_PROTO(obj
);
741 return proto
->lookupProperty(cx
, id
, objp
, propp
);
745 array_dropProperty(JSContext
*cx
, JSObject
*obj
, JSProperty
*prop
)
747 JS_ASSERT(IsDenseArrayId(cx
, obj
, (jsid
) prop
));
751 js_GetDenseArrayElementValue(JSContext
*cx
, JSObject
*obj
, JSProperty
*prop
,
754 jsid id
= (jsid
) prop
;
755 JS_ASSERT(IsDenseArrayId(cx
, obj
, id
));
758 if (!js_IdIsIndex(id
, &i
)) {
759 JS_ASSERT(id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
));
760 return IndexToValue(cx
, obj
->fslots
[JSSLOT_ARRAY_LENGTH
], vp
);
762 *vp
= obj
->dslots
[i
];
767 array_getProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
771 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
))
772 return IndexToValue(cx
, obj
->fslots
[JSSLOT_ARRAY_LENGTH
], vp
);
774 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.protoAtom
)) {
775 *vp
= OBJECT_TO_JSVAL(obj
->getProto());
779 if (!OBJ_IS_DENSE_ARRAY(cx
, obj
))
780 return js_GetProperty(cx
, obj
, id
, vp
);
782 if (!js_IdIsIndex(ID_TO_VALUE(id
), &i
) || i
>= js_DenseArrayCapacity(obj
) ||
783 obj
->dslots
[i
] == JSVAL_HOLE
) {
786 JSScopeProperty
*sprop
;
788 JSObject
*proto
= STOBJ_GET_PROTO(obj
);
795 if (js_LookupPropertyWithFlags(cx
, proto
, id
, cx
->resolveFlags
,
800 if (OBJ_IS_NATIVE(obj2
)) {
801 sprop
= (JSScopeProperty
*) prop
;
802 if (!js_NativeGet(cx
, obj
, obj2
, sprop
, JSGET_METHOD_BARRIER
, vp
))
805 obj2
->dropProperty(cx
, prop
);
810 *vp
= obj
->dslots
[i
];
815 slowarray_addProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
817 jsuint index
, length
;
819 if (!js_IdIsIndex(id
, &index
))
821 length
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
823 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = index
+ 1;
828 slowarray_enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
829 jsval
*statep
, jsid
*idp
);
832 array_typeOf(JSContext
*cx
, JSObject
*obj
)
834 return JSTYPE_OBJECT
;
837 /* The same as js_ObjectOps except for the .enumerate and .call hooks. */
838 static JSObjectOps js_SlowArrayObjectOps
= {
840 js_LookupProperty
, js_DefineProperty
,
841 js_GetProperty
, js_SetProperty
,
842 js_GetAttributes
, js_SetAttributes
,
843 js_DeleteProperty
, js_DefaultValue
,
844 slowarray_enumerate
, js_CheckAccess
,
845 array_typeOf
, js_TraceObject
,
846 NULL
, NATIVE_DROP_PROPERTY
,
848 js_HasInstance
, js_Clear
852 slowarray_getObjectOps(JSContext
*cx
, JSClass
*clasp
)
854 return &js_SlowArrayObjectOps
;
858 array_setProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
862 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
))
863 return array_length_setter(cx
, obj
, id
, vp
);
865 if (!OBJ_IS_DENSE_ARRAY(cx
, obj
))
866 return js_SetProperty(cx
, obj
, id
, vp
);
868 if (!js_IdIsIndex(id
, &i
) || INDEX_TOO_SPARSE(obj
, i
)) {
869 if (!js_MakeArraySlow(cx
, obj
))
871 return js_SetProperty(cx
, obj
, id
, vp
);
874 if (!EnsureCapacity(cx
, obj
, i
+ 1))
877 if (i
>= (uint32
)obj
->fslots
[JSSLOT_ARRAY_LENGTH
])
878 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = i
+ 1;
879 if (obj
->dslots
[i
] == JSVAL_HOLE
)
880 obj
->fslots
[JSSLOT_ARRAY_COUNT
]++;
881 obj
->dslots
[i
] = *vp
;
886 js_PrototypeHasIndexedProperties(JSContext
*cx
, JSObject
*obj
)
889 * Walk up the prototype chain and see if this indexed element already
890 * exists. If we hit the end of the prototype chain, it's safe to set the
891 * element on the original object.
893 while ((obj
= obj
->getProto()) != NULL
) {
895 * If the prototype is a non-native object (possibly a dense array), or
896 * a native object (possibly a slow array) that has indexed properties,
899 if (!OBJ_IS_NATIVE(obj
))
901 if (OBJ_SCOPE(obj
)->hadIndexedProperties())
909 static inline JSBool FASTCALL
910 dense_grow(JSContext
* cx
, JSObject
* obj
, jsint i
, jsval v
)
913 * Let the interpreter worry about negative array indexes.
915 JS_ASSERT((MAX_DSLOTS_LENGTH
> MAX_DSLOTS_LENGTH32
) == (sizeof(jsval
) != sizeof(uint32
)));
916 if (MAX_DSLOTS_LENGTH
> MAX_DSLOTS_LENGTH32
) {
918 * Have to check for negative values bleeding through on 64-bit machines only,
919 * since we can't allocate large enough arrays for this on 32-bit machines.
926 * If needed, grow the array as long it remains dense, otherwise fall off trace.
928 jsuint u
= jsuint(i
);
929 jsuint capacity
= js_DenseArrayCapacity(obj
);
930 if ((u
>= capacity
) && (INDEX_TOO_SPARSE(obj
, u
) || !EnsureCapacity(cx
, obj
, u
+ 1)))
933 if (obj
->dslots
[u
] == JSVAL_HOLE
) {
934 if (js_PrototypeHasIndexedProperties(cx
, obj
))
937 if (u
>= jsuint(obj
->fslots
[JSSLOT_ARRAY_LENGTH
]))
938 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = u
+ 1;
939 ++obj
->fslots
[JSSLOT_ARRAY_COUNT
];
948 js_Array_dense_setelem(JSContext
* cx
, JSObject
* obj
, jsint i
, jsval v
)
950 JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx
, obj
));
951 return dense_grow(cx
, obj
, i
, v
);
953 JS_DEFINE_CALLINFO_4(extern, BOOL
, js_Array_dense_setelem
, CONTEXT
, OBJECT
, INT32
, JSVAL
, 0, 0)
956 js_Array_dense_setelem_int(JSContext
* cx
, JSObject
* obj
, jsint i
, int32 j
)
958 JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx
, obj
));
961 if (JS_LIKELY(INT_FITS_IN_JSVAL(j
))) {
964 jsdouble d
= (jsdouble
)j
;
965 if (!js_NewDoubleInRootedValue(cx
, d
, &v
))
969 return dense_grow(cx
, obj
, i
, v
);
971 JS_DEFINE_CALLINFO_4(extern, BOOL
, js_Array_dense_setelem_int
, CONTEXT
, OBJECT
, INT32
, INT32
, 0, 0)
974 js_Array_dense_setelem_double(JSContext
* cx
, JSObject
* obj
, jsint i
, jsdouble d
)
976 JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx
, obj
));
981 if (JS_LIKELY(JSDOUBLE_IS_INT(d
, j
) && INT_FITS_IN_JSVAL(j
))) {
984 if (!js_NewDoubleInRootedValue(cx
, d
, &v
))
988 return dense_grow(cx
, obj
, i
, v
);
990 JS_DEFINE_CALLINFO_4(extern, BOOL
, js_Array_dense_setelem_double
, CONTEXT
, OBJECT
, INT32
, DOUBLE
, 0, 0)
994 array_defineProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval value
,
995 JSPropertyOp getter
, JSPropertyOp setter
, uintN attrs
)
997 uint32 i
= 0; // init to shut GCC up
1000 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
))
1003 isIndex
= js_IdIsIndex(ID_TO_VALUE(id
), &i
);
1004 if (!isIndex
|| attrs
!= JSPROP_ENUMERATE
|| !OBJ_IS_DENSE_ARRAY(cx
, obj
) || INDEX_TOO_SPARSE(obj
, i
)) {
1005 if (!ENSURE_SLOW_ARRAY(cx
, obj
))
1007 return js_DefineProperty(cx
, obj
, id
, value
, getter
, setter
, attrs
);
1010 return array_setProperty(cx
, obj
, id
, &value
);
1014 array_getAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, JSProperty
*prop
,
1017 *attrsp
= id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
)
1018 ? JSPROP_PERMANENT
: JSPROP_ENUMERATE
;
1023 array_setAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, JSProperty
*prop
,
1026 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1027 JSMSG_CANT_SET_ARRAY_ATTRS
);
1032 array_deleteProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*rval
)
1036 if (!OBJ_IS_DENSE_ARRAY(cx
, obj
))
1037 return js_DeleteProperty(cx
, obj
, id
, rval
);
1039 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
)) {
1040 *rval
= JSVAL_FALSE
;
1044 if (js_IdIsIndex(id
, &i
) && i
< js_DenseArrayCapacity(obj
) &&
1045 obj
->dslots
[i
] != JSVAL_HOLE
) {
1046 obj
->fslots
[JSSLOT_ARRAY_COUNT
]--;
1047 obj
->dslots
[i
] = JSVAL_HOLE
;
1055 * JSObjectOps.enumerate implementation.
1057 * For a fast array, JSENUMERATE_INIT captures in the enumeration state both
1058 * the length of the array and the bitmap indicating the positions of holes in
1059 * the array. This ensures that adding or deleting array elements does not
1060 * affect the sequence of indexes JSENUMERATE_NEXT returns.
1062 * For a common case of an array without holes, to represent the state we pack
1063 * the (nextEnumerationIndex, arrayLength) pair as a pseudo-boolean jsval.
1064 * This is possible when length <= PACKED_UINT_PAIR_BITS. For arrays with
1065 * greater length or holes we allocate the JSIndexIterState structure and
1066 * store it as an int-tagged private pointer jsval. For a slow array we
1067 * delegate the enumeration implementation to js_Enumerate in
1068 * slowarray_enumerate.
1070 * Array mutations can turn a fast array into a slow one after the enumeration
1071 * starts. When this happens, slowarray_enumerate receives a state created
1072 * when the array was fast. To distinguish such fast state from a slow state,
1073 * which is an int-tagged pointer that js_Enumerate creates, we set not one
1074 * but two lowest bits when tagging a JSIndexIterState pointer -- see
1075 * INDEX_ITER_TAG usage below. Thus, when slowarray_enumerate receives a state
1076 * tagged with JSVAL_SPECIAL or with two lowest bits set, it knows that this
1077 * is a fast state so it calls array_enumerate to continue enumerating the
1078 * indexes present in the original fast array.
1081 #define PACKED_UINT_PAIR_BITS 14
1082 #define PACKED_UINT_PAIR_MASK JS_BITMASK(PACKED_UINT_PAIR_BITS)
1084 #define UINT_PAIR_TO_SPECIAL_JSVAL(i,j) \
1085 (JS_ASSERT((uint32) (i) <= PACKED_UINT_PAIR_MASK), \
1086 JS_ASSERT((uint32) (j) <= PACKED_UINT_PAIR_MASK), \
1087 ((jsval) (i) << (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)) | \
1088 ((jsval) (j) << (JSVAL_TAGBITS)) | \
1089 (jsval) JSVAL_SPECIAL)
1091 #define SPECIAL_JSVAL_TO_UINT_PAIR(v,i,j) \
1092 (JS_ASSERT(JSVAL_IS_SPECIAL(v)), \
1093 (i) = (uint32) ((v) >> (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)), \
1094 (j) = (uint32) ((v) >> JSVAL_TAGBITS) & PACKED_UINT_PAIR_MASK, \
1095 JS_ASSERT((i) <= PACKED_UINT_PAIR_MASK))
1097 JS_STATIC_ASSERT(PACKED_UINT_PAIR_BITS
* 2 + JSVAL_TAGBITS
<= JS_BITS_PER_WORD
);
1099 typedef struct JSIndexIterState
{
1105 * Variable-length bitmap representing array's holes. It must not be
1106 * accessed when hasHoles is false.
1111 #define INDEX_ITER_TAG 3
1113 JS_STATIC_ASSERT(JSVAL_INT
== 1);
1116 array_enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
1117 jsval
*statep
, jsid
*idp
)
1120 JSIndexIterState
*ii
;
1123 case JSENUMERATE_INIT
:
1124 JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx
, obj
));
1125 capacity
= js_DenseArrayCapacity(obj
);
1127 *idp
= INT_TO_JSVAL(obj
->fslots
[JSSLOT_ARRAY_COUNT
]);
1129 for (i
= 0; i
!= capacity
; ++i
) {
1130 if (obj
->dslots
[i
] == JSVAL_HOLE
) {
1132 ii
= (JSIndexIterState
*)
1133 cx
->malloc(offsetof(JSIndexIterState
, holes
) +
1134 JS_BITMAP_SIZE(capacity
));
1137 ii
->hasHoles
= JS_TRUE
;
1138 memset(ii
->holes
, 0, JS_BITMAP_SIZE(capacity
));
1140 JS_SET_BIT(ii
->holes
, i
);
1144 /* Array has no holes. */
1145 if (capacity
<= PACKED_UINT_PAIR_MASK
) {
1146 *statep
= UINT_PAIR_TO_SPECIAL_JSVAL(0, capacity
);
1149 ii
= (JSIndexIterState
*)
1150 cx
->malloc(offsetof(JSIndexIterState
, holes
));
1153 ii
->hasHoles
= JS_FALSE
;
1156 ii
->length
= capacity
;
1157 *statep
= (jsval
) ii
| INDEX_ITER_TAG
;
1158 JS_ASSERT(*statep
& JSVAL_INT
);
1161 case JSENUMERATE_NEXT
:
1162 if (JSVAL_IS_SPECIAL(*statep
)) {
1163 SPECIAL_JSVAL_TO_UINT_PAIR(*statep
, i
, capacity
);
1164 if (i
!= capacity
) {
1165 *idp
= INT_TO_JSID(i
);
1166 *statep
= UINT_PAIR_TO_SPECIAL_JSVAL(i
+ 1, capacity
);
1170 JS_ASSERT((*statep
& INDEX_ITER_TAG
) == INDEX_ITER_TAG
);
1171 ii
= (JSIndexIterState
*) (*statep
& ~INDEX_ITER_TAG
);
1173 if (i
!= ii
->length
) {
1174 /* Skip holes if any. */
1176 while (JS_TEST_BIT(ii
->holes
, i
) && ++i
!= ii
->length
)
1179 if (i
!= ii
->length
) {
1181 return js_IndexToId(cx
, i
, idp
);
1187 case JSENUMERATE_DESTROY
:
1188 if (!JSVAL_IS_SPECIAL(*statep
)) {
1189 JS_ASSERT((*statep
& INDEX_ITER_TAG
) == INDEX_ITER_TAG
);
1190 ii
= (JSIndexIterState
*) (*statep
& ~INDEX_ITER_TAG
);
1193 *statep
= JSVAL_NULL
;
1200 slowarray_enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
1201 jsval
*statep
, jsid
*idp
)
1205 /* Are we continuing an enumeration that started when we were dense? */
1206 if (enum_op
!= JSENUMERATE_INIT
) {
1207 if (JSVAL_IS_SPECIAL(*statep
) ||
1208 (*statep
& INDEX_ITER_TAG
) == INDEX_ITER_TAG
) {
1209 return array_enumerate(cx
, obj
, enum_op
, statep
, idp
);
1211 JS_ASSERT((*statep
& INDEX_ITER_TAG
) == JSVAL_INT
);
1213 ok
= js_Enumerate(cx
, obj
, enum_op
, statep
, idp
);
1214 JS_ASSERT(*statep
== JSVAL_NULL
|| (*statep
& INDEX_ITER_TAG
) == JSVAL_INT
);
1219 array_finalize(JSContext
*cx
, JSObject
*obj
)
1222 cx
->free(obj
->dslots
- 1);
1227 array_trace(JSTracer
*trc
, JSObject
*obj
)
1233 JS_ASSERT(obj
->isDenseArray());
1234 obj
->traceProtoAndParent(trc
);
1236 capacity
= js_DenseArrayCapacity(obj
);
1237 for (i
= 0; i
< capacity
; i
++) {
1239 if (JSVAL_IS_TRACEABLE(v
)) {
1240 JS_SET_TRACING_INDEX(trc
, "array_dslots", i
);
1241 js_CallGCMarker(trc
, JSVAL_TO_TRACEABLE(v
), JSVAL_TRACE_KIND(v
));
1246 extern JSObjectOps js_ArrayObjectOps
;
1248 static const JSObjectMap
SharedArrayMap(&js_ArrayObjectOps
, JSObjectMap::SHAPELESS
);
1250 JSObjectOps js_ArrayObjectOps
= {
1252 array_lookupProperty
, array_defineProperty
,
1253 array_getProperty
, array_setProperty
,
1254 array_getAttributes
, array_setAttributes
,
1255 array_deleteProperty
, js_DefaultValue
,
1256 array_enumerate
, js_CheckAccess
,
1257 array_typeOf
, array_trace
,
1258 NULL
, array_dropProperty
,
1260 js_HasInstance
, NULL
1263 static JSObjectOps
*
1264 array_getObjectOps(JSContext
*cx
, JSClass
*clasp
)
1266 return &js_ArrayObjectOps
;
1269 JSClass js_ArrayClass
= {
1271 JSCLASS_HAS_RESERVED_SLOTS(2) |
1272 JSCLASS_HAS_CACHED_PROTO(JSProto_Array
) |
1273 JSCLASS_NEW_ENUMERATE
,
1274 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
1275 JS_EnumerateStub
, JS_ResolveStub
, js_TryValueOf
, array_finalize
,
1276 array_getObjectOps
, NULL
, NULL
, NULL
,
1277 NULL
, NULL
, NULL
, NULL
1280 JSClass js_SlowArrayClass
= {
1282 JSCLASS_HAS_PRIVATE
|
1283 JSCLASS_HAS_CACHED_PROTO(JSProto_Array
),
1284 slowarray_addProperty
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
1285 JS_EnumerateStub
, JS_ResolveStub
, js_TryValueOf
, NULL
,
1286 slowarray_getObjectOps
, NULL
, NULL
, NULL
,
1287 NULL
, NULL
, NULL
, NULL
1291 * Convert an array object from fast-and-dense to slow-and-flexible.
1294 js_MakeArraySlow(JSContext
*cx
, JSObject
*obj
)
1296 JS_ASSERT(obj
->getClass() == &js_ArrayClass
);
1299 * Create a native scope. All slow arrays other than Array.prototype get
1300 * the same initial shape.
1303 JSObject
*arrayProto
= obj
->getProto();
1304 if (arrayProto
->getClass() == &js_ObjectClass
) {
1305 /* obj is Array.prototype. */
1306 emptyShape
= js_GenerateShape(cx
, false);
1308 /* arrayProto is Array.prototype. */
1309 JS_ASSERT(arrayProto
->getClass() == &js_SlowArrayClass
);
1310 emptyShape
= OBJ_SCOPE(arrayProto
)->emptyScope
->shape
;
1312 JSScope
*scope
= JSScope::create(cx
, &js_SlowArrayObjectOps
, &js_SlowArrayClass
, obj
,
1317 uint32 capacity
= js_DenseArrayCapacity(obj
);
1319 scope
->freeslot
= STOBJ_NSLOTS(obj
) + JS_INITIAL_NSLOTS
;
1320 obj
->dslots
[-1] = JS_INITIAL_NSLOTS
+ capacity
;
1322 scope
->freeslot
= STOBJ_NSLOTS(obj
);
1325 /* Create new properties pointing to existing values in dslots */
1326 for (uint32 i
= 0; i
< capacity
; i
++) {
1328 JSScopeProperty
*sprop
;
1330 if (!JS_ValueToId(cx
, INT_TO_JSVAL(i
), &id
))
1333 if (obj
->dslots
[i
] == JSVAL_HOLE
) {
1334 obj
->dslots
[i
] = JSVAL_VOID
;
1338 sprop
= scope
->addDataProperty(cx
, id
, JS_INITIAL_NSLOTS
+ i
,
1345 * Render our formerly-reserved count property GC-safe. If length fits in
1346 * a jsval, set our slow/sparse COUNT to the current length as a jsval, so
1347 * we can tell when only named properties have been added to a dense array
1348 * to make it slow-but-not-sparse.
1350 * We do not need to make the length slot GC-safe as this slot is private
1351 * where the implementation can store an arbitrary value.
1354 JS_STATIC_ASSERT(JSSLOT_ARRAY_LENGTH
== JSSLOT_PRIVATE
);
1355 JS_ASSERT(js_SlowArrayClass
.flags
& JSCLASS_HAS_PRIVATE
);
1356 uint32 length
= uint32(obj
->fslots
[JSSLOT_ARRAY_LENGTH
]);
1357 obj
->fslots
[JSSLOT_ARRAY_COUNT
] = INT_FITS_IN_JSVAL(length
)
1358 ? INT_TO_JSVAL(length
)
1362 /* Make sure we preserve any flags borrowing bits in classword. */
1363 obj
->classword
^= (jsuword
) &js_ArrayClass
;
1364 obj
->classword
|= (jsuword
) &js_SlowArrayClass
;
1374 /* Transfer ownership of buffer to returned string. */
1375 static inline JSBool
1376 BufferToString(JSContext
*cx
, JSCharBuffer
&cb
, jsval
*rval
)
1378 JSString
*str
= js_NewStringFromCharBuffer(cx
, cb
);
1381 *rval
= STRING_TO_JSVAL(str
);
1387 array_toSource(JSContext
*cx
, uintN argc
, jsval
*vp
)
1389 JS_CHECK_RECURSION(cx
, return false);
1391 JSObject
*obj
= JS_THIS_OBJECT(cx
, vp
);
1393 (OBJ_GET_CLASS(cx
, obj
) != &js_SlowArrayClass
&&
1394 !JS_InstanceOf(cx
, obj
, &js_ArrayClass
, vp
+ 2))) {
1398 /* Find joins or cycles in the reachable object graph. */
1400 JSHashEntry
*he
= js_EnterSharpObject(cx
, obj
, NULL
, &sharpchars
);
1403 bool initiallySharp
= IS_SHARP(he
);
1405 /* After this point, all paths exit through the 'out' label. */
1406 MUST_FLOW_THROUGH("out");
1410 * This object will take responsibility for the jschar buffer until the
1411 * buffer is transferred to the returned JSString.
1413 JSCharBuffer
cb(cx
);
1415 /* Cycles/joins are indicated by sharp objects. */
1416 #if JS_HAS_SHARP_VARS
1418 JS_ASSERT(sharpchars
!= 0);
1419 cb
.replaceRawBuffer(sharpchars
, js_strlen(sharpchars
));
1421 } else if (sharpchars
) {
1423 cb
.replaceRawBuffer(sharpchars
, js_strlen(sharpchars
));
1427 if (!js_AppendLiteral(cb
, "[]"))
1429 cx
->free(sharpchars
);
1434 if (!cb
.append('['))
1438 if (!js_GetLengthProperty(cx
, obj
, &length
))
1441 for (jsuint index
= 0; index
< length
; index
++) {
1442 /* Use vp to locally root each element value. */
1444 if (!JS_CHECK_OPERATION_LIMIT(cx
) ||
1445 !GetArrayElement(cx
, obj
, index
, &hole
, vp
)) {
1449 /* Get element's character string. */
1452 str
= cx
->runtime
->emptyString
;
1454 str
= js_ValueToSource(cx
, *vp
);
1458 *vp
= STRING_TO_JSVAL(str
);
1459 const jschar
*chars
;
1461 str
->getCharsAndLength(chars
, charlen
);
1463 /* Append element to buffer. */
1464 if (!cb
.append(chars
, charlen
))
1466 if (index
+ 1 != length
) {
1467 if (!js_AppendLiteral(cb
, ", "))
1470 if (!cb
.append(','))
1475 /* Finalize the buffer. */
1476 if (!cb
.append(']'))
1480 if (!BufferToString(cx
, cb
, vp
))
1486 if (!initiallySharp
)
1487 js_LeaveSharpObject(cx
, NULL
);
1493 array_toString_sub(JSContext
*cx
, JSObject
*obj
, JSBool locale
,
1494 JSString
*sepstr
, jsval
*rval
)
1496 JS_CHECK_RECURSION(cx
, return false);
1499 * Use HashTable entry as the cycle indicator. On first visit, create the
1500 * entry, and, when leaving, remove the entry.
1502 typedef js::HashSet
<JSObject
*> ObjSet
;
1503 ObjSet::AddPtr hashp
= cx
->busyArrays
.lookupForAdd(obj
);
1506 /* Not in hash table, so not a cycle. */
1507 if (!cx
->busyArrays
.add(hashp
, obj
)) {
1508 JS_ReportOutOfMemory(cx
);
1511 genBefore
= cx
->busyArrays
.generation();
1513 /* Cycle, so return empty string. */
1514 *rval
= ATOM_KEY(cx
->runtime
->atomState
.emptyAtom
);
1518 JSAutoTempValueRooter
tvr(cx
, obj
);
1520 /* After this point, all paths exit through the 'out' label. */
1521 MUST_FLOW_THROUGH("out");
1524 /* Get characters to use for the separator. */
1525 static const jschar comma
= ',';
1529 sepstr
->getCharsAndLength(sep
, seplen
);
1536 * This object will take responsibility for the jschar buffer until the
1537 * buffer is transferred to the returned JSString.
1539 JSCharBuffer
cb(cx
);
1542 if (!js_GetLengthProperty(cx
, obj
, &length
))
1545 for (jsuint index
= 0; index
< length
; index
++) {
1546 /* Use rval to locally root each element value. */
1548 if (!JS_CHECK_OPERATION_LIMIT(cx
) ||
1549 !GetArrayElement(cx
, obj
, index
, &hole
, rval
)) {
1553 /* Get element's character string. */
1554 if (!(hole
|| JSVAL_IS_VOID(*rval
) || JSVAL_IS_NULL(*rval
))) {
1556 /* Work on obj.toLocalString() instead. */
1559 if (!js_ValueToObject(cx
, *rval
, &robj
))
1561 *rval
= OBJECT_TO_JSVAL(robj
);
1562 JSAtom
*atom
= cx
->runtime
->atomState
.toLocaleStringAtom
;
1563 if (!js_TryMethod(cx
, robj
, atom
, 0, NULL
, rval
))
1567 if (!js_ValueToCharBuffer(cx
, *rval
, cb
))
1571 /* Append the separator. */
1572 if (index
+ 1 != length
) {
1573 if (!cb
.append(sep
, seplen
))
1578 /* Finalize the buffer. */
1579 if (!BufferToString(cx
, cb
, rval
))
1585 if (genBefore
== cx
->busyArrays
.generation())
1586 cx
->busyArrays
.remove(hashp
);
1588 cx
->busyArrays
.remove(obj
);
1593 array_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1597 obj
= JS_THIS_OBJECT(cx
, vp
);
1599 (OBJ_GET_CLASS(cx
, obj
) != &js_SlowArrayClass
&&
1600 !JS_InstanceOf(cx
, obj
, &js_ArrayClass
, vp
+ 2))) {
1604 return array_toString_sub(cx
, obj
, JS_FALSE
, NULL
, vp
);
1608 array_toLocaleString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1612 obj
= JS_THIS_OBJECT(cx
, vp
);
1614 (OBJ_GET_CLASS(cx
, obj
) != &js_SlowArrayClass
&&
1615 !JS_InstanceOf(cx
, obj
, &js_ArrayClass
, vp
+ 2))) {
1620 * Passing comma here as the separator. Need a way to get a
1621 * locale-specific version.
1623 return array_toString_sub(cx
, obj
, JS_TRUE
, NULL
, vp
);
1626 enum TargetElementsType
{
1627 TargetElementsAllHoles
,
1628 TargetElementsMayContainValues
1631 enum SourceVectorType
{
1632 SourceVectorAllValues
,
1633 SourceVectorMayContainHoles
1637 InitArrayElements(JSContext
*cx
, JSObject
*obj
, jsuint start
, jsuint count
, jsval
*vector
,
1638 TargetElementsType targetType
, SourceVectorType vectorType
)
1640 JS_ASSERT(count
< MAXINDEX
);
1643 * Optimize for dense arrays so long as adding the given set of elements
1644 * wouldn't otherwise make the array slow.
1646 if (OBJ_IS_DENSE_ARRAY(cx
, obj
) && !js_PrototypeHasIndexedProperties(cx
, obj
) &&
1647 start
<= MAXINDEX
- count
&& !INDEX_TOO_BIG(start
+ count
)) {
1649 #ifdef DEBUG_jwalden
1651 /* Verify that overwriteType and writeType were accurate. */
1652 JSAutoTempIdRooter
idr(cx
, JSVAL_ZERO
);
1653 for (jsuint i
= 0; i
< count
; i
++) {
1654 JS_ASSERT_IF(vectorType
== SourceVectorAllValues
, vector
[i
] != JSVAL_HOLE
);
1656 jsdouble index
= jsdouble(start
) + i
;
1657 if (targetType
== TargetElementsAllHoles
&& index
< jsuint(-1)) {
1658 JS_ASSERT(ReallyBigIndexToId(cx
, index
, idr
.addr()));
1661 JS_ASSERT(obj
->lookupProperty(cx
, idr
.id(), &obj2
, &prop
));
1668 jsuint newlen
= start
+ count
;
1669 JS_ASSERT(jsdouble(start
) + count
== jsdouble(newlen
));
1670 if (!EnsureCapacity(cx
, obj
, newlen
))
1673 if (newlen
> uint32(obj
->fslots
[JSSLOT_ARRAY_LENGTH
]))
1674 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = newlen
;
1676 JS_ASSERT(count
< size_t(-1) / sizeof(jsval
));
1677 if (targetType
== TargetElementsMayContainValues
) {
1678 jsuint valueCount
= 0;
1679 for (jsuint i
= 0; i
< count
; i
++) {
1680 if (obj
->dslots
[start
+ i
] != JSVAL_HOLE
)
1683 JS_ASSERT(uint32(obj
->fslots
[JSSLOT_ARRAY_COUNT
]) >= valueCount
);
1684 obj
->fslots
[JSSLOT_ARRAY_COUNT
] -= valueCount
;
1686 memcpy(obj
->dslots
+ start
, vector
, sizeof(jsval
) * count
);
1687 if (vectorType
== SourceVectorAllValues
) {
1688 obj
->fslots
[JSSLOT_ARRAY_COUNT
] += count
;
1690 jsuint valueCount
= 0;
1691 for (jsuint i
= 0; i
< count
; i
++) {
1692 if (obj
->dslots
[start
+ i
] != JSVAL_HOLE
)
1695 obj
->fslots
[JSSLOT_ARRAY_COUNT
] += valueCount
;
1697 JS_ASSERT_IF(count
!= 0, obj
->dslots
[newlen
- 1] != JSVAL_HOLE
);
1701 jsval
* end
= vector
+ count
;
1702 while (vector
!= end
&& start
< MAXINDEX
) {
1703 if (!JS_CHECK_OPERATION_LIMIT(cx
) ||
1704 !SetArrayElement(cx
, obj
, start
++, *vector
++)) {
1712 /* Finish out any remaining elements past the max array index. */
1713 if (OBJ_IS_DENSE_ARRAY(cx
, obj
) && !ENSURE_SLOW_ARRAY(cx
, obj
))
1716 JS_ASSERT(start
== MAXINDEX
);
1717 jsval tmp
[2] = {JSVAL_NULL
, JSVAL_NULL
};
1718 JSAutoTempValueRooter
tvr(cx
, JS_ARRAY_LENGTH(tmp
), tmp
);
1719 if (!js_NewDoubleInRootedValue(cx
, MAXINDEX
, &tmp
[0]))
1721 jsdouble
*dp
= JSVAL_TO_DOUBLE(tmp
[0]);
1722 JS_ASSERT(*dp
== MAXINDEX
);
1723 JSAutoTempIdRooter
idr(cx
);
1726 if (!js_ValueToStringId(cx
, tmp
[0], idr
.addr()) ||
1727 !obj
->setProperty(cx
, idr
.id(), &tmp
[1])) {
1731 } while (vector
!= end
);
1737 InitArrayObject(JSContext
*cx
, JSObject
*obj
, jsuint length
, jsval
*vector
,
1738 JSBool holey
= JS_FALSE
)
1740 JS_ASSERT(OBJ_IS_ARRAY(cx
, obj
));
1742 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = length
;
1745 if (!EnsureCapacity(cx
, obj
, length
))
1748 jsuint count
= length
;
1750 memcpy(obj
->dslots
, vector
, length
* sizeof (jsval
));
1752 for (jsuint i
= 0; i
< length
; i
++) {
1753 if (vector
[i
] == JSVAL_HOLE
)
1755 obj
->dslots
[i
] = vector
[i
];
1758 obj
->fslots
[JSSLOT_ARRAY_COUNT
] = count
;
1760 obj
->fslots
[JSSLOT_ARRAY_COUNT
] = 0;
1766 static JSString
* FASTCALL
1767 Array_p_join(JSContext
* cx
, JSObject
* obj
, JSString
*str
)
1769 JSAutoTempValueRooter
tvr(cx
);
1770 if (!array_toString_sub(cx
, obj
, JS_FALSE
, str
, tvr
.addr())) {
1771 SetBuiltinError(cx
);
1774 return JSVAL_TO_STRING(tvr
.value());
1777 static JSString
* FASTCALL
1778 Array_p_toString(JSContext
* cx
, JSObject
* obj
)
1780 JSAutoTempValueRooter
tvr(cx
);
1781 if (!array_toString_sub(cx
, obj
, JS_FALSE
, NULL
, tvr
.addr())) {
1782 SetBuiltinError(cx
);
1785 return JSVAL_TO_STRING(tvr
.value());
1790 * Perl-inspired join, reverse, and sort.
1793 array_join(JSContext
*cx
, uintN argc
, jsval
*vp
)
1798 if (argc
== 0 || JSVAL_IS_VOID(vp
[2])) {
1801 str
= js_ValueToString(cx
, vp
[2]);
1804 vp
[2] = STRING_TO_JSVAL(str
);
1806 obj
= JS_THIS_OBJECT(cx
, vp
);
1807 return obj
&& array_toString_sub(cx
, obj
, JS_FALSE
, str
, vp
);
1811 array_reverse(JSContext
*cx
, uintN argc
, jsval
*vp
)
1814 JSObject
*obj
= JS_THIS_OBJECT(cx
, vp
);
1815 if (!obj
|| !js_GetLengthProperty(cx
, obj
, &len
))
1817 *vp
= OBJECT_TO_JSVAL(obj
);
1819 if (OBJ_IS_DENSE_ARRAY(cx
, obj
) && !js_PrototypeHasIndexedProperties(cx
, obj
)) {
1820 /* An empty array or an array with no elements is already reversed. */
1821 if (len
== 0 || !obj
->dslots
)
1825 * It's actually surprisingly complicated to reverse an array due to the
1826 * orthogonality of array length and array capacity while handling
1827 * leading and trailing holes correctly. Reversing seems less likely to
1828 * be a common operation than other array mass-mutation methods, so for
1829 * now just take a probably-small memory hit (in the absence of too many
1830 * holes in the array at its start) and ensure that the capacity is
1831 * sufficient to hold all the elements in the array if it were full.
1833 if (!EnsureCapacity(cx
, obj
, len
))
1836 jsval
* lo
= &obj
->dslots
[0];
1837 jsval
* hi
= &obj
->dslots
[len
- 1];
1838 for (; lo
< hi
; lo
++, hi
--) {
1845 * Per ECMA-262, don't update the length of the array, even if the new
1846 * array has trailing holes (and thus the original array began with
1852 JSAutoTempValueRooter
tvr(cx
, JSVAL_NULL
);
1853 for (jsuint i
= 0, half
= len
/ 2; i
< half
; i
++) {
1855 if (!JS_CHECK_OPERATION_LIMIT(cx
) ||
1856 !GetArrayElement(cx
, obj
, i
, &hole
, tvr
.addr()) ||
1857 !GetArrayElement(cx
, obj
, len
- i
- 1, &hole2
, vp
) ||
1858 !SetOrDeleteArrayElement(cx
, obj
, len
- i
- 1, hole
, tvr
.value()) ||
1859 !SetOrDeleteArrayElement(cx
, obj
, i
, hole2
, *vp
)) {
1863 *vp
= OBJECT_TO_JSVAL(obj
);
1867 typedef struct MSortArgs
{
1874 /* Helper function for js_MergeSort. */
1876 MergeArrays(MSortArgs
*msa
, void *src
, void *dest
, size_t run1
, size_t run2
)
1878 void *arg
, *a
, *b
, *c
;
1879 size_t elsize
, runtotal
;
1884 runtotal
= run1
+ run2
;
1886 elsize
= msa
->elsize
;
1889 fastcopy
= msa
->fastcopy
;
1891 #define CALL_CMP(a, b) \
1892 if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE;
1894 /* Copy runs already in sorted order. */
1895 b
= (char *)src
+ run1
* elsize
;
1896 a
= (char *)b
- elsize
;
1898 if (cmp_result
<= 0) {
1899 memcpy(dest
, src
, runtotal
* elsize
);
1903 #define COPY_ONE(p,q,n) \
1904 (fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n))
1908 for (; runtotal
!= 0; runtotal
--) {
1909 JSBool from_a
= run2
== 0;
1910 if (!from_a
&& run1
!= 0) {
1912 from_a
= cmp_result
<= 0;
1916 COPY_ONE(c
, a
, elsize
);
1918 a
= (char *)a
+ elsize
;
1920 COPY_ONE(c
, b
, elsize
);
1922 b
= (char *)b
+ elsize
;
1924 c
= (char *)c
+ elsize
;
1933 * This sort is stable, i.e. sequence of equal elements is preserved.
1934 * See also bug #224128.
1937 js_MergeSort(void *src
, size_t nel
, size_t elsize
,
1938 JSComparator cmp
, void *arg
, void *tmp
)
1940 void *swap
, *vec1
, *vec2
;
1942 size_t i
, j
, lo
, hi
, run
;
1946 /* Avoid memcpy overhead for word-sized and word-aligned elements. */
1947 fastcopy
= (elsize
== sizeof(jsval
) &&
1948 (((jsuword
) src
| (jsuword
) tmp
) & JSVAL_ALIGN
) == 0);
1949 #define COPY_ONE(p,q,n) \
1950 (fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n))
1951 #define CALL_CMP(a, b) \
1952 if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE;
1953 #define INS_SORT_INT 4
1956 * Apply insertion sort to small chunks to reduce the number of merge
1959 for (lo
= 0; lo
< nel
; lo
+= INS_SORT_INT
) {
1960 hi
= lo
+ INS_SORT_INT
;
1963 for (i
= lo
+ 1; i
< hi
; i
++) {
1964 vec1
= (char *)src
+ i
* elsize
;
1965 vec2
= (char *)vec1
- elsize
;
1966 for (j
= i
; j
> lo
; j
--) {
1967 CALL_CMP(vec2
, vec1
);
1968 /* "<=" instead of "<" insures the sort is stable */
1969 if (cmp_result
<= 0) {
1973 /* Swap elements, using "tmp" as tmp storage */
1974 COPY_ONE(tmp
, vec2
, elsize
);
1975 COPY_ONE(vec2
, vec1
, elsize
);
1976 COPY_ONE(vec1
, tmp
, elsize
);
1978 vec2
= (char *)vec1
- elsize
;
1985 msa
.elsize
= elsize
;
1988 msa
.fastcopy
= fastcopy
;
1992 for (run
= INS_SORT_INT
; run
< nel
; run
*= 2) {
1993 for (lo
= 0; lo
< nel
; lo
+= 2 * run
) {
1996 memcpy((char *)vec2
+ lo
* elsize
, (char *)vec1
+ lo
* elsize
,
1997 (nel
- lo
) * elsize
);
2000 if (!MergeArrays(&msa
, (char *)vec1
+ lo
* elsize
,
2001 (char *)vec2
+ lo
* elsize
, run
,
2002 hi
+ run
> nel
? nel
- hi
: run
)) {
2011 memcpy(src
, tmp
, nel
* elsize
);
2016 typedef struct CompareArgs
{
2019 jsval
*elemroot
; /* stack needed for js_Invoke */
2022 static JS_REQUIRES_STACK JSBool
2023 sort_compare(void *arg
, const void *a
, const void *b
, int *result
)
2025 jsval av
= *(const jsval
*)a
, bv
= *(const jsval
*)b
;
2026 CompareArgs
*ca
= (CompareArgs
*) arg
;
2027 JSContext
*cx
= ca
->context
;
2028 jsval
*invokevp
, *sp
;
2032 * array_sort deals with holes and undefs on its own and they should not
2035 JS_ASSERT(!JSVAL_IS_VOID(av
));
2036 JS_ASSERT(!JSVAL_IS_VOID(bv
));
2038 if (!JS_CHECK_OPERATION_LIMIT(cx
))
2041 invokevp
= ca
->elemroot
;
2048 if (!js_Invoke(cx
, 2, invokevp
, 0))
2051 cmp
= js_ValueToNumber(cx
, invokevp
);
2052 if (JSVAL_IS_NULL(*invokevp
))
2055 /* Clamp cmp to -1, 0, 1. */
2057 if (!JSDOUBLE_IS_NaN(cmp
) && cmp
!= 0)
2058 *result
= cmp
> 0 ? 1 : -1;
2061 * XXX else report some kind of error here? ECMA talks about 'consistent
2062 * compare functions' that don't return NaN, but is silent about what the
2063 * result should be. So we currently ignore it.
2069 typedef JSBool (JS_REQUIRES_STACK
*JSRedComparator
)(void*, const void*,
2070 const void*, int *);
2072 static inline JS_IGNORE_STACK JSComparator
2073 comparator_stack_cast(JSRedComparator func
)
2079 sort_compare_strings(void *arg
, const void *a
, const void *b
, int *result
)
2081 jsval av
= *(const jsval
*)a
, bv
= *(const jsval
*)b
;
2083 JS_ASSERT(JSVAL_IS_STRING(av
));
2084 JS_ASSERT(JSVAL_IS_STRING(bv
));
2085 if (!JS_CHECK_OPERATION_LIMIT((JSContext
*)arg
))
2088 *result
= (int) js_CompareStrings(JSVAL_TO_STRING(av
), JSVAL_TO_STRING(bv
));
2093 * The array_sort function below assumes JSVAL_NULL is zero in order to
2094 * perform initialization using memset. Other parts of SpiderMonkey likewise
2095 * "know" that JSVAL_NULL is zero; this static assertion covers all cases.
2097 JS_STATIC_ASSERT(JSVAL_NULL
== 0);
2100 array_sort(JSContext
*cx
, uintN argc
, jsval
*vp
)
2102 jsval
*argv
, fval
, *vec
, *mergesort_tmp
, v
;
2105 jsuint len
, newlen
, i
, undefs
;
2106 JSTempValueRooter tvr
;
2113 * Optimize the default compare function case if all of obj's elements
2114 * have values of type string.
2118 argv
= JS_ARGV(cx
, vp
);
2120 if (JSVAL_IS_PRIMITIVE(argv
[0])) {
2121 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2122 JSMSG_BAD_SORT_ARG
);
2125 fval
= argv
[0]; /* non-default compare function */
2130 obj
= JS_THIS_OBJECT(cx
, vp
);
2131 if (!obj
|| !js_GetLengthProperty(cx
, obj
, &len
))
2134 *vp
= OBJECT_TO_JSVAL(obj
);
2139 * We need a temporary array of 2 * len jsvals to hold the array elements
2140 * and the scratch space for merge sort. Check that its size does not
2141 * overflow size_t, which would allow for indexing beyond the end of the
2144 #if JS_BITS_PER_WORD == 32
2145 if ((size_t)len
> ~(size_t)0 / (2 * sizeof(jsval
))) {
2146 js_ReportAllocationOverflow(cx
);
2150 vec
= (jsval
*) cx
->malloc(2 * (size_t) len
* sizeof(jsval
));
2155 * Initialize vec as a root. We will clear elements of vec one by
2156 * one while increasing tvr.count when we know that the property at
2157 * the corresponding index exists and its value must be rooted.
2159 * In this way when sorting a huge mostly sparse array we will not
2160 * access the tail of vec corresponding to properties that do not
2161 * exist, allowing OS to avoiding committing RAM. See bug 330812.
2163 * After this point control must flow through label out: to exit.
2165 JS_PUSH_TEMP_ROOT(cx
, 0, vec
, &tvr
);
2168 * By ECMA 262, 15.4.4.11, a property that does not exist (which we
2169 * call a "hole") is always greater than an existing property with
2170 * value undefined and that is always greater than any other property.
2171 * Thus to sort holes and undefs we simply count them, sort the rest
2172 * of elements, append undefs after them and then make holes after
2177 all_strings
= JS_TRUE
;
2178 for (i
= 0; i
< len
; i
++) {
2179 ok
= JS_CHECK_OPERATION_LIMIT(cx
);
2183 /* Clear vec[newlen] before including it in the rooted set. */
2184 vec
[newlen
] = JSVAL_NULL
;
2185 tvr
.count
= newlen
+ 1;
2186 ok
= GetArrayElement(cx
, obj
, i
, &hole
, &vec
[newlen
]);
2193 if (JSVAL_IS_VOID(vec
[newlen
])) {
2198 /* We know JSVAL_IS_STRING yields 0 or 1, so avoid a branch via &=. */
2199 all_strings
&= JSVAL_IS_STRING(vec
[newlen
]);
2205 /* The array has only holes and undefs. */
2211 * The first newlen elements of vec are copied from the array object
2212 * (above). The remaining newlen positions are used as GC-rooted scratch
2213 * space for mergesort. We must clear the space before including it to
2214 * the root set covered by tvr.count. We assume JSVAL_NULL==0 to optimize
2215 * initialization using memset.
2217 mergesort_tmp
= vec
+ newlen
;
2218 memset(mergesort_tmp
, 0, newlen
* sizeof(jsval
));
2219 tvr
.count
= newlen
* 2;
2221 /* Here len == 2 * (newlen + undefs + number_of_holes). */
2222 if (fval
== JSVAL_NULL
) {
2224 * Sort using the default comparator converting all elements to
2228 elemsize
= sizeof(jsval
);
2231 * To avoid string conversion on each compare we do it only once
2232 * prior to sorting. But we also need the space for the original
2233 * values to recover the sorting result. To reuse
2234 * sort_compare_strings we move the original values to the odd
2235 * indexes in vec, put the string conversion results in the even
2236 * indexes and pass 2 * sizeof(jsval) as an element size to the
2237 * sorting function. In this way sort_compare_strings will only
2238 * see the string values when it casts the compare arguments as
2239 * pointers to jsval.
2241 * This requires doubling the temporary storage including the
2242 * scratch space for the merge sort. Since vec already contains
2243 * the rooted scratch space for newlen elements at the tail, we
2244 * can use it to rearrange and convert to strings first and try
2245 * realloc only when we know that we successfully converted all
2248 #if JS_BITS_PER_WORD == 32
2249 if ((size_t)newlen
> ~(size_t)0 / (4 * sizeof(jsval
))) {
2250 js_ReportAllocationOverflow(cx
);
2257 * Rearrange and string-convert the elements of the vector from
2258 * the tail here and, after sorting, move the results back
2259 * starting from the start to prevent overwrite the existing
2265 ok
= JS_CHECK_OPERATION_LIMIT(cx
);
2269 str
= js_ValueToString(cx
, v
);
2274 vec
[2 * i
] = STRING_TO_JSVAL(str
);
2278 JS_ASSERT(tvr
.u
.array
== vec
);
2279 vec
= (jsval
*) cx
->realloc(vec
,
2280 4 * (size_t) newlen
* sizeof(jsval
));
2287 mergesort_tmp
= vec
+ 2 * newlen
;
2288 memset(mergesort_tmp
, 0, newlen
* 2 * sizeof(jsval
));
2289 tvr
.count
= newlen
* 4;
2290 elemsize
= 2 * sizeof(jsval
);
2292 ok
= js_MergeSort(vec
, (size_t) newlen
, elemsize
,
2293 sort_compare_strings
, cx
, mergesort_tmp
);
2298 * We want to make the following loop fast and to unroot the
2299 * cached results of toString invocations before the operation
2300 * callback has a chance to run the GC. For this reason we do
2301 * not call JS_CHECK_OPERATION_LIMIT in the loop.
2305 vec
[i
] = vec
[2 * i
+ 1];
2306 } while (++i
!= newlen
);
2315 ca
.elemroot
= js_AllocStack(cx
, 2 + 2, &mark
);
2320 ok
= js_MergeSort(vec
, (size_t) newlen
, sizeof(jsval
),
2321 comparator_stack_cast(sort_compare
),
2322 &ca
, mergesort_tmp
);
2323 js_FreeStack(cx
, mark
);
2329 * We no longer need to root the scratch space for the merge sort, so
2330 * unroot it now to make the job of a potential GC under InitArrayElements
2334 ok
= InitArrayElements(cx
, obj
, 0, newlen
, vec
, TargetElementsMayContainValues
,
2335 SourceVectorAllValues
);
2340 JS_POP_TEMP_ROOT(cx
, &tvr
);
2345 /* Set undefs that sorted after the rest of elements. */
2346 while (undefs
!= 0) {
2348 if (!JS_CHECK_OPERATION_LIMIT(cx
) ||
2349 !SetArrayElement(cx
, obj
, newlen
++, JSVAL_VOID
)) {
2354 /* Re-create any holes that sorted to the end of the array. */
2355 while (len
> newlen
) {
2356 if (!JS_CHECK_OPERATION_LIMIT(cx
) ||
2357 !DeleteArrayElement(cx
, obj
, --len
)) {
2361 *vp
= OBJECT_TO_JSVAL(obj
);
2366 * Perl-inspired push, pop, shift, unshift, and splice methods.
2369 array_push_slowly(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2373 if (!js_GetLengthProperty(cx
, obj
, &length
))
2375 if (!InitArrayElements(cx
, obj
, length
, argc
, argv
, TargetElementsMayContainValues
,
2376 SourceVectorAllValues
)) {
2380 /* Per ECMA-262, return the new array length. */
2381 jsdouble newlength
= length
+ jsdouble(argc
);
2382 if (!IndexToValue(cx
, newlength
, rval
))
2384 return js_SetLengthProperty(cx
, obj
, newlength
);
2388 array_push1_dense(JSContext
* cx
, JSObject
* obj
, jsval v
, jsval
*rval
)
2390 uint32 length
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
2391 if (INDEX_TOO_SPARSE(obj
, length
)) {
2392 if (!js_MakeArraySlow(cx
, obj
))
2394 return array_push_slowly(cx
, obj
, 1, &v
, rval
);
2397 if (!EnsureCapacity(cx
, obj
, length
+ 1))
2399 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = length
+ 1;
2401 JS_ASSERT(obj
->dslots
[length
] == JSVAL_HOLE
);
2402 obj
->fslots
[JSSLOT_ARRAY_COUNT
]++;
2403 obj
->dslots
[length
] = v
;
2404 return IndexToValue(cx
, obj
->fslots
[JSSLOT_ARRAY_LENGTH
], rval
);
2408 js_ArrayCompPush(JSContext
*cx
, JSObject
*obj
, jsval v
)
2410 JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx
, obj
));
2411 uint32_t length
= (uint32_t) obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
2412 JS_ASSERT(length
<= js_DenseArrayCapacity(obj
));
2414 if (length
== js_DenseArrayCapacity(obj
)) {
2415 if (length
> JS_ARGS_LENGTH_MAX
) {
2416 JS_ReportErrorNumberUC(cx
, js_GetErrorMessage
, NULL
,
2417 JSMSG_ARRAY_INIT_TOO_BIG
);
2421 if (!EnsureCapacity(cx
, obj
, length
+ 1))
2424 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = length
+ 1;
2425 obj
->fslots
[JSSLOT_ARRAY_COUNT
]++;
2426 obj
->dslots
[length
] = v
;
2429 JS_DEFINE_CALLINFO_3(extern, BOOL
, js_ArrayCompPush
, CONTEXT
, OBJECT
, JSVAL
, 0, 0)
2432 static jsval FASTCALL
2433 Array_p_push1(JSContext
* cx
, JSObject
* obj
, jsval v
)
2435 JSAutoTempValueRooter
tvr(cx
, v
);
2436 if (OBJ_IS_DENSE_ARRAY(cx
, obj
)
2437 ? array_push1_dense(cx
, obj
, v
, tvr
.addr())
2438 : array_push_slowly(cx
, obj
, 1, tvr
.addr(), tvr
.addr())) {
2441 SetBuiltinError(cx
);
2447 array_push(JSContext
*cx
, uintN argc
, jsval
*vp
)
2451 /* Insist on one argument and obj of the expected class. */
2452 obj
= JS_THIS_OBJECT(cx
, vp
);
2455 if (argc
!= 1 || !OBJ_IS_DENSE_ARRAY(cx
, obj
))
2456 return array_push_slowly(cx
, obj
, argc
, vp
+ 2, vp
);
2458 return array_push1_dense(cx
, obj
, vp
[2], vp
);
2462 array_pop_slowly(JSContext
*cx
, JSObject
* obj
, jsval
*vp
)
2467 if (!js_GetLengthProperty(cx
, obj
, &index
))
2474 /* Get the to-be-deleted property's value into vp. */
2475 if (!GetArrayElement(cx
, obj
, index
, &hole
, vp
))
2477 if (!hole
&& !DeleteArrayElement(cx
, obj
, index
))
2480 return js_SetLengthProperty(cx
, obj
, index
);
2484 array_pop_dense(JSContext
*cx
, JSObject
* obj
, jsval
*vp
)
2489 index
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
2495 if (!GetArrayElement(cx
, obj
, index
, &hole
, vp
))
2497 if (!hole
&& !DeleteArrayElement(cx
, obj
, index
))
2499 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = index
;
2504 static jsval FASTCALL
2505 Array_p_pop(JSContext
* cx
, JSObject
* obj
)
2507 JSAutoTempValueRooter
tvr(cx
);
2508 if (OBJ_IS_DENSE_ARRAY(cx
, obj
)
2509 ? array_pop_dense(cx
, obj
, tvr
.addr())
2510 : array_pop_slowly(cx
, obj
, tvr
.addr())) {
2513 SetBuiltinError(cx
);
2519 array_pop(JSContext
*cx
, uintN argc
, jsval
*vp
)
2523 obj
= JS_THIS_OBJECT(cx
, vp
);
2526 if (OBJ_IS_DENSE_ARRAY(cx
, obj
))
2527 return array_pop_dense(cx
, obj
, vp
);
2528 return array_pop_slowly(cx
, obj
, vp
);
2532 array_shift(JSContext
*cx
, uintN argc
, jsval
*vp
)
2538 obj
= JS_THIS_OBJECT(cx
, vp
);
2539 if (!obj
|| !js_GetLengthProperty(cx
, obj
, &length
))
2546 if (OBJ_IS_DENSE_ARRAY(cx
, obj
) && !js_PrototypeHasIndexedProperties(cx
, obj
) &&
2547 length
< js_DenseArrayCapacity(obj
)) {
2548 if (JS_LIKELY(obj
->dslots
!= NULL
)) {
2549 *vp
= obj
->dslots
[0];
2550 if (*vp
== JSVAL_HOLE
)
2553 obj
->fslots
[JSSLOT_ARRAY_COUNT
]--;
2554 memmove(obj
->dslots
, obj
->dslots
+ 1, length
* sizeof(jsval
));
2555 obj
->dslots
[length
] = JSVAL_HOLE
;
2558 * We don't need to modify the indexed properties of an empty array
2559 * with an explicitly set non-zero length when shift() is called on
2560 * it, but note fallthrough to reduce the length by one.
2562 JS_ASSERT(obj
->fslots
[JSSLOT_ARRAY_COUNT
] == 0);
2566 /* Get the to-be-deleted property's value into vp ASAP. */
2567 if (!GetArrayElement(cx
, obj
, 0, &hole
, vp
))
2570 /* Slide down the array above the first element. */
2571 JSAutoTempValueRooter
tvr(cx
, JSVAL_NULL
);
2572 for (i
= 0; i
!= length
; i
++) {
2573 if (!JS_CHECK_OPERATION_LIMIT(cx
) ||
2574 !GetArrayElement(cx
, obj
, i
+ 1, &hole
, tvr
.addr()) ||
2575 !SetOrDeleteArrayElement(cx
, obj
, i
, hole
, tvr
.value())) {
2580 /* Delete the only or last element when it exists. */
2581 if (!hole
&& !DeleteArrayElement(cx
, obj
, length
))
2585 return js_SetLengthProperty(cx
, obj
, length
);
2589 array_unshift(JSContext
*cx
, uintN argc
, jsval
*vp
)
2595 jsdouble last
, newlen
;
2597 obj
= JS_THIS_OBJECT(cx
, vp
);
2598 if (!obj
|| !js_GetLengthProperty(cx
, obj
, &length
))
2602 /* Slide up the array to make room for argc at the bottom. */
2603 argv
= JS_ARGV(cx
, vp
);
2605 if (OBJ_IS_DENSE_ARRAY(cx
, obj
) && !js_PrototypeHasIndexedProperties(cx
, obj
) &&
2606 !INDEX_TOO_SPARSE(obj
, unsigned(newlen
+ argc
))) {
2607 JS_ASSERT(newlen
+ argc
== length
+ argc
);
2608 if (!EnsureCapacity(cx
, obj
, length
+ argc
))
2610 memmove(obj
->dslots
+ argc
, obj
->dslots
, length
* sizeof(jsval
));
2611 for (uint32 i
= 0; i
< argc
; i
++)
2612 obj
->dslots
[i
] = JSVAL_HOLE
;
2615 jsdouble upperIndex
= last
+ argc
;
2616 JSAutoTempValueRooter
tvr(cx
, JSVAL_NULL
);
2618 --last
, --upperIndex
;
2619 if (!JS_CHECK_OPERATION_LIMIT(cx
) ||
2620 !GetArrayElement(cx
, obj
, last
, &hole
, tvr
.addr()) ||
2621 !SetOrDeleteArrayElement(cx
, obj
, upperIndex
, hole
, tvr
.value())) {
2624 } while (last
!= 0);
2628 /* Copy from argv to the bottom of the array. */
2629 if (!InitArrayElements(cx
, obj
, 0, argc
, argv
, TargetElementsAllHoles
, SourceVectorAllValues
))
2633 if (!js_SetLengthProperty(cx
, obj
, newlen
))
2637 /* Follow Perl by returning the new array length. */
2638 return IndexToValue(cx
, newlen
, vp
);
2642 array_splice(JSContext
*cx
, uintN argc
, jsval
*vp
)
2646 jsuint length
, begin
, end
, count
, delta
, last
;
2652 * Create a new array value to return. Our ECMA v2 proposal specs
2653 * that splice always returns an array value, even when given no
2654 * arguments. We think this is best because it eliminates the need
2655 * for callers to do an extra test to handle the empty splice case.
2657 obj2
= js_NewArrayObject(cx
, 0, NULL
);
2660 *vp
= OBJECT_TO_JSVAL(obj2
);
2662 /* Nothing to do if no args. Otherwise get length. */
2665 argv
= JS_ARGV(cx
, vp
);
2666 obj
= JS_THIS_OBJECT(cx
, vp
);
2667 if (!obj
|| !js_GetLengthProperty(cx
, obj
, &length
))
2670 /* Convert the first argument into a starting index. */
2671 d
= js_ValueToNumber(cx
, argv
);
2672 if (JSVAL_IS_NULL(*argv
))
2674 d
= js_DoubleToInteger(d
);
2679 } else if (d
> length
) {
2682 begin
= (jsuint
)d
; /* d has been clamped to uint32 */
2686 /* Convert the second argument from a count into a fencepost index. */
2687 delta
= length
- begin
;
2692 d
= js_ValueToNumber(cx
, argv
);
2693 if (JSVAL_IS_NULL(*argv
))
2695 d
= js_DoubleToInteger(d
);
2701 end
= begin
+ count
;
2706 JSAutoTempValueRooter
tvr(cx
, JSVAL_NULL
);
2708 /* If there are elements to remove, put them into the return value. */
2710 if (OBJ_IS_DENSE_ARRAY(cx
, obj
) && !js_PrototypeHasIndexedProperties(cx
, obj
) &&
2711 !js_PrototypeHasIndexedProperties(cx
, obj2
) &&
2712 end
<= js_DenseArrayCapacity(obj
)) {
2713 if (!InitArrayObject(cx
, obj2
, count
, obj
->dslots
+ begin
,
2714 obj
->fslots
[JSSLOT_ARRAY_COUNT
] !=
2715 obj
->fslots
[JSSLOT_ARRAY_LENGTH
])) {
2719 for (last
= begin
; last
< end
; last
++) {
2720 if (!JS_CHECK_OPERATION_LIMIT(cx
) ||
2721 !GetArrayElement(cx
, obj
, last
, &hole
, tvr
.addr())) {
2725 /* Copy tvr.value() to the new array unless it's a hole. */
2726 if (!hole
&& !SetArrayElement(cx
, obj2
, last
- begin
, tvr
.value()))
2730 if (!js_SetLengthProperty(cx
, obj2
, count
))
2735 /* Find the direction (up or down) to copy and make way for argv. */
2737 delta
= (jsuint
)argc
- count
;
2739 if (OBJ_IS_DENSE_ARRAY(cx
, obj
) && !js_PrototypeHasIndexedProperties(cx
, obj
) &&
2740 length
<= js_DenseArrayCapacity(obj
) &&
2741 (length
== 0 || obj
->dslots
[length
- 1] != JSVAL_HOLE
)) {
2742 if (!EnsureCapacity(cx
, obj
, length
+ delta
))
2744 /* (uint) end could be 0, so we can't use a vanilla >= test. */
2745 while (last
-- > end
) {
2746 jsval srcval
= obj
->dslots
[last
];
2747 jsval
* dest
= &obj
->dslots
[last
+ delta
];
2748 if (*dest
== JSVAL_HOLE
&& srcval
!= JSVAL_HOLE
)
2749 obj
->fslots
[JSSLOT_ARRAY_COUNT
]++;
2752 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] += delta
;
2754 /* (uint) end could be 0, so we can't use a vanilla >= test. */
2755 while (last
-- > end
) {
2756 if (!JS_CHECK_OPERATION_LIMIT(cx
) ||
2757 !GetArrayElement(cx
, obj
, last
, &hole
, tvr
.addr()) ||
2758 !SetOrDeleteArrayElement(cx
, obj
, last
+ delta
, hole
, tvr
.value())) {
2764 } else if (argc
< count
) {
2765 delta
= count
- (jsuint
)argc
;
2766 if (OBJ_IS_DENSE_ARRAY(cx
, obj
) && !js_PrototypeHasIndexedProperties(cx
, obj
) &&
2767 length
<= js_DenseArrayCapacity(obj
)) {
2768 /* (uint) end could be 0, so we can't use a vanilla >= test. */
2769 for (last
= end
; last
< length
; last
++) {
2770 jsval srcval
= obj
->dslots
[last
];
2771 jsval
* dest
= &obj
->dslots
[last
- delta
];
2772 if (*dest
== JSVAL_HOLE
&& srcval
!= JSVAL_HOLE
)
2773 obj
->fslots
[JSSLOT_ARRAY_COUNT
]++;
2777 for (last
= end
; last
< length
; last
++) {
2778 if (!JS_CHECK_OPERATION_LIMIT(cx
) ||
2779 !GetArrayElement(cx
, obj
, last
, &hole
, tvr
.addr()) ||
2780 !SetOrDeleteArrayElement(cx
, obj
, last
- delta
, hole
, tvr
.value())) {
2789 * Copy from argv into the hole to complete the splice, and update length in
2790 * case we deleted elements from the end.
2792 return InitArrayElements(cx
, obj
, begin
, argc
, argv
, TargetElementsMayContainValues
,
2793 SourceVectorAllValues
) &&
2794 js_SetLengthProperty(cx
, obj
, length
);
2798 * Python-esque sequence operations.
2801 array_concat(JSContext
*cx
, uintN argc
, jsval
*vp
)
2804 JSObject
*aobj
, *nobj
;
2805 jsuint length
, alength
, slot
;
2809 /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
2810 argv
= JS_ARGV(cx
, vp
) - 1;
2811 JS_ASSERT(JS_THIS_OBJECT(cx
, vp
) == JSVAL_TO_OBJECT(argv
[0]));
2813 /* Create a new Array object and root it using *vp. */
2814 aobj
= JS_THIS_OBJECT(cx
, vp
);
2815 if (OBJ_IS_DENSE_ARRAY(cx
, aobj
)) {
2817 * Clone aobj but pass the minimum of its length and capacity, to
2818 * handle a = [1,2,3]; a.length = 10000 "dense" cases efficiently. In
2819 * such a case we'll pass 8 (not 3) due to ARRAY_CAPACITY_MIN, which
2820 * will cause nobj to be over-allocated to 16. But in the normal case
2821 * where length is <= capacity, nobj and aobj will have the same
2824 length
= aobj
->fslots
[JSSLOT_ARRAY_LENGTH
];
2825 jsuint capacity
= js_DenseArrayCapacity(aobj
);
2826 nobj
= js_NewArrayObject(cx
, JS_MIN(length
, capacity
), aobj
->dslots
,
2827 aobj
->fslots
[JSSLOT_ARRAY_COUNT
] !=
2831 nobj
->fslots
[JSSLOT_ARRAY_LENGTH
] = length
;
2832 *vp
= OBJECT_TO_JSVAL(nobj
);
2838 nobj
= js_NewArrayObject(cx
, 0, NULL
);
2841 *vp
= OBJECT_TO_JSVAL(nobj
);
2845 JSAutoTempValueRooter
tvr(cx
, JSVAL_NULL
);
2847 /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */
2848 for (i
= 0; i
<= argc
; i
++) {
2849 if (!JS_CHECK_OPERATION_LIMIT(cx
))
2852 if (!JSVAL_IS_PRIMITIVE(v
)) {
2855 aobj
= JSVAL_TO_OBJECT(v
);
2856 wobj
= js_GetWrappedObject(cx
, aobj
);
2857 if (OBJ_IS_ARRAY(cx
, wobj
)) {
2858 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
);
2859 if (!aobj
->getProperty(cx
, id
, tvr
.addr()))
2861 alength
= ValueIsLength(cx
, tvr
.addr());
2862 if (JSVAL_IS_NULL(tvr
.value()))
2864 for (slot
= 0; slot
< alength
; slot
++) {
2865 if (!JS_CHECK_OPERATION_LIMIT(cx
) ||
2866 !GetArrayElement(cx
, aobj
, slot
, &hole
, tvr
.addr())) {
2871 * Per ECMA 262, 15.4.4.4, step 9, ignore non-existent
2875 !SetArrayElement(cx
, nobj
, length
+slot
, tvr
.value())) {
2884 if (!SetArrayElement(cx
, nobj
, length
, v
))
2889 return js_SetLengthProperty(cx
, nobj
, length
);
2893 array_slice(JSContext
*cx
, uintN argc
, jsval
*vp
)
2896 JSObject
*nobj
, *obj
;
2897 jsuint length
, begin
, end
, slot
;
2901 argv
= JS_ARGV(cx
, vp
);
2903 obj
= JS_THIS_OBJECT(cx
, vp
);
2904 if (!obj
|| !js_GetLengthProperty(cx
, obj
, &length
))
2910 d
= js_ValueToNumber(cx
, &argv
[0]);
2911 if (JSVAL_IS_NULL(argv
[0]))
2913 d
= js_DoubleToInteger(d
);
2918 } else if (d
> length
) {
2924 d
= js_ValueToNumber(cx
, &argv
[1]);
2925 if (JSVAL_IS_NULL(argv
[1]))
2927 d
= js_DoubleToInteger(d
);
2932 } else if (d
> length
) {
2942 if (OBJ_IS_DENSE_ARRAY(cx
, obj
) && end
<= js_DenseArrayCapacity(obj
) &&
2943 !js_PrototypeHasIndexedProperties(cx
, obj
)) {
2944 nobj
= js_NewArrayObject(cx
, end
- begin
, obj
->dslots
+ begin
,
2945 obj
->fslots
[JSSLOT_ARRAY_COUNT
] !=
2946 obj
->fslots
[JSSLOT_ARRAY_LENGTH
]);
2949 *vp
= OBJECT_TO_JSVAL(nobj
);
2953 /* Create a new Array object and root it using *vp. */
2954 nobj
= js_NewArrayObject(cx
, 0, NULL
);
2957 *vp
= OBJECT_TO_JSVAL(nobj
);
2959 JSAutoTempValueRooter
tvr(cx
, JSVAL_NULL
);
2960 for (slot
= begin
; slot
< end
; slot
++) {
2961 if (!JS_CHECK_OPERATION_LIMIT(cx
) ||
2962 !GetArrayElement(cx
, obj
, slot
, &hole
, tvr
.addr())) {
2965 if (!hole
&& !SetArrayElement(cx
, nobj
, slot
- begin
, tvr
.value()))
2969 return js_SetLengthProperty(cx
, nobj
, end
- begin
);
2972 #if JS_HAS_ARRAY_EXTRAS
2975 array_indexOfHelper(JSContext
*cx
, JSBool isLast
, uintN argc
, jsval
*vp
)
2978 jsuint length
, i
, stop
;
2983 obj
= JS_THIS_OBJECT(cx
, vp
);
2984 if (!obj
|| !js_GetLengthProperty(cx
, obj
, &length
))
2990 i
= isLast
? length
- 1 : 0;
2991 tosearch
= (argc
!= 0) ? vp
[2] : JSVAL_VOID
;
2996 start
= js_ValueToNumber(cx
, &vp
[3]);
2997 if (JSVAL_IS_NULL(vp
[3]))
2999 start
= js_DoubleToInteger(start
);
3009 } else if (start
>= length
) {
3027 if (!JS_CHECK_OPERATION_LIMIT(cx
) ||
3028 !GetArrayElement(cx
, obj
, (jsuint
)i
, &hole
, vp
)) {
3031 if (!hole
&& js_StrictlyEqual(cx
, *vp
, tosearch
))
3032 return js_NewNumberInRootedValue(cx
, i
, vp
);
3039 *vp
= INT_TO_JSVAL(-1);
3044 array_indexOf(JSContext
*cx
, uintN argc
, jsval
*vp
)
3046 return array_indexOfHelper(cx
, JS_FALSE
, argc
, vp
);
3050 array_lastIndexOf(JSContext
*cx
, uintN argc
, jsval
*vp
)
3052 return array_indexOfHelper(cx
, JS_TRUE
, argc
, vp
);
3055 /* Order is important; extras that take a predicate funarg must follow MAP. */
3056 typedef enum ArrayExtraMode
{
3066 #define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT)
3069 array_extra(JSContext
*cx
, ArrayExtraMode mode
, uintN argc
, jsval
*vp
)
3072 jsuint length
, newlen
;
3073 jsval
*argv
, *elemroot
, *invokevp
, *sp
;
3074 JSBool ok
, cond
, hole
;
3075 JSObject
*callable
, *thisp
, *newarr
;
3076 jsint start
, end
, step
, i
;
3079 obj
= JS_THIS_OBJECT(cx
, vp
);
3080 if (!obj
|| !js_GetLengthProperty(cx
, obj
, &length
))
3084 * First, get or compute our callee, so that we error out consistently
3085 * when passed a non-callable object.
3088 js_ReportMissingArg(cx
, vp
, 0);
3092 callable
= js_ValueToCallableObject(cx
, &argv
[0], JSV2F_SEARCH_STACK
);
3097 * Set our initial return condition, used for zero-length array cases
3098 * (and pre-size our map return to match our known length, for all cases).
3100 #ifdef __GNUC__ /* quell GCC overwarning */
3104 start
= 0, end
= length
, step
= 1;
3108 start
= length
- 1, end
= -1, step
= -1;
3111 if (length
== 0 && argc
== 1) {
3112 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3113 JSMSG_EMPTY_ARRAY_REDUCE
);
3120 if (!GetArrayElement(cx
, obj
, start
, &hole
, vp
))
3123 } while (hole
&& start
!= end
);
3125 if (hole
&& start
== end
) {
3126 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3127 JSMSG_EMPTY_ARRAY_REDUCE
);
3134 newlen
= (mode
== MAP
) ? length
: 0;
3135 newarr
= js_NewArrayObject(cx
, newlen
, NULL
);
3138 *vp
= OBJECT_TO_JSVAL(newarr
);
3154 if (argc
> 1 && !REDUCE_MODE(mode
)) {
3155 if (!js_ValueToObject(cx
, argv
[1], &thisp
))
3157 argv
[1] = OBJECT_TO_JSVAL(thisp
);
3163 * For all but REDUCE, we call with 3 args (value, index, array). REDUCE
3164 * requires 4 args (accum, value, index, array).
3167 argc
= 3 + REDUCE_MODE(mode
);
3168 elemroot
= js_AllocStack(cx
, 1 + 2 + argc
, &mark
);
3172 MUST_FLOW_THROUGH("out");
3174 invokevp
= elemroot
+ 1;
3176 for (i
= start
; i
!= end
; i
+= step
) {
3177 ok
= JS_CHECK_OPERATION_LIMIT(cx
) &&
3178 GetArrayElement(cx
, obj
, i
, &hole
, elemroot
);
3185 * Push callable and 'this', then args. We must do this for every
3186 * iteration around the loop since js_Invoke uses spbase[0] for return
3187 * value storage, while some native functions use spbase[1] for local
3191 *sp
++ = OBJECT_TO_JSVAL(callable
);
3192 *sp
++ = OBJECT_TO_JSVAL(thisp
);
3193 if (REDUCE_MODE(mode
))
3196 *sp
++ = INT_TO_JSVAL(i
);
3197 *sp
++ = OBJECT_TO_JSVAL(obj
);
3200 ok
= js_Invoke(cx
, argc
, invokevp
, 0);
3205 cond
= js_ValueToBoolean(*invokevp
);
3206 #ifdef __GNUC__ /* quell GCC overwarning */
3219 ok
= SetArrayElement(cx
, newarr
, i
, *invokevp
);
3226 /* The filter passed *elemroot, so push it onto our result. */
3227 ok
= SetArrayElement(cx
, newarr
, newlen
++, *elemroot
);
3247 js_FreeStack(cx
, mark
);
3248 if (ok
&& mode
== FILTER
)
3249 ok
= js_SetLengthProperty(cx
, newarr
, newlen
);
3254 array_forEach(JSContext
*cx
, uintN argc
, jsval
*vp
)
3256 return array_extra(cx
, FOREACH
, argc
, vp
);
3260 array_map(JSContext
*cx
, uintN argc
, jsval
*vp
)
3262 return array_extra(cx
, MAP
, argc
, vp
);
3266 array_reduce(JSContext
*cx
, uintN argc
, jsval
*vp
)
3268 return array_extra(cx
, REDUCE
, argc
, vp
);
3272 array_reduceRight(JSContext
*cx
, uintN argc
, jsval
*vp
)
3274 return array_extra(cx
, REDUCE_RIGHT
, argc
, vp
);
3278 array_filter(JSContext
*cx
, uintN argc
, jsval
*vp
)
3280 return array_extra(cx
, FILTER
, argc
, vp
);
3284 array_some(JSContext
*cx
, uintN argc
, jsval
*vp
)
3286 return array_extra(cx
, SOME
, argc
, vp
);
3290 array_every(JSContext
*cx
, uintN argc
, jsval
*vp
)
3292 return array_extra(cx
, EVERY
, argc
, vp
);
3297 array_isArray(JSContext
*cx
, uintN argc
, jsval
*vp
)
3299 *vp
= BOOLEAN_TO_JSVAL(argc
> 0 &&
3300 !JSVAL_IS_PRIMITIVE(vp
[2]) &&
3301 OBJ_IS_ARRAY(cx
, js_GetWrappedObject(cx
, JSVAL_TO_OBJECT(vp
[2]))));
3305 static JSPropertySpec array_props
[] = {
3306 {js_length_str
, -1, JSPROP_SHARED
| JSPROP_PERMANENT
,
3307 array_length_getter
, array_length_setter
},
3311 JS_DEFINE_TRCINFO_1(array_toString
,
3312 (2, (static, STRING_FAIL
, Array_p_toString
, CONTEXT
, THIS
, 0, 0)))
3313 JS_DEFINE_TRCINFO_1(array_join
,
3314 (3, (static, STRING_FAIL
, Array_p_join
, CONTEXT
, THIS
, STRING
, 0, 0)))
3315 JS_DEFINE_TRCINFO_1(array_push
,
3316 (3, (static, JSVAL_FAIL
, Array_p_push1
, CONTEXT
, THIS
, JSVAL
, 0, 0)))
3317 JS_DEFINE_TRCINFO_1(array_pop
,
3318 (2, (static, JSVAL_FAIL
, Array_p_pop
, CONTEXT
, THIS
, 0, 0)))
3320 static JSFunctionSpec array_methods
[] = {
3322 JS_FN(js_toSource_str
, array_toSource
, 0,0),
3324 JS_TN(js_toString_str
, array_toString
, 0,0, &array_toString_trcinfo
),
3325 JS_FN(js_toLocaleString_str
,array_toLocaleString
,0,0),
3327 /* Perl-ish methods. */
3328 JS_TN("join", array_join
, 1,JSFUN_GENERIC_NATIVE
, &array_join_trcinfo
),
3329 JS_FN("reverse", array_reverse
, 0,JSFUN_GENERIC_NATIVE
),
3330 JS_FN("sort", array_sort
, 1,JSFUN_GENERIC_NATIVE
),
3331 JS_TN("push", array_push
, 1,JSFUN_GENERIC_NATIVE
, &array_push_trcinfo
),
3332 JS_TN("pop", array_pop
, 0,JSFUN_GENERIC_NATIVE
, &array_pop_trcinfo
),
3333 JS_FN("shift", array_shift
, 0,JSFUN_GENERIC_NATIVE
),
3334 JS_FN("unshift", array_unshift
, 1,JSFUN_GENERIC_NATIVE
),
3335 JS_FN("splice", array_splice
, 2,JSFUN_GENERIC_NATIVE
),
3337 /* Pythonic sequence methods. */
3338 JS_FN("concat", array_concat
, 1,JSFUN_GENERIC_NATIVE
),
3339 JS_FN("slice", array_slice
, 2,JSFUN_GENERIC_NATIVE
),
3341 #if JS_HAS_ARRAY_EXTRAS
3342 JS_FN("indexOf", array_indexOf
, 1,JSFUN_GENERIC_NATIVE
),
3343 JS_FN("lastIndexOf", array_lastIndexOf
, 1,JSFUN_GENERIC_NATIVE
),
3344 JS_FN("forEach", array_forEach
, 1,JSFUN_GENERIC_NATIVE
),
3345 JS_FN("map", array_map
, 1,JSFUN_GENERIC_NATIVE
),
3346 JS_FN("reduce", array_reduce
, 1,JSFUN_GENERIC_NATIVE
),
3347 JS_FN("reduceRight", array_reduceRight
, 1,JSFUN_GENERIC_NATIVE
),
3348 JS_FN("filter", array_filter
, 1,JSFUN_GENERIC_NATIVE
),
3349 JS_FN("some", array_some
, 1,JSFUN_GENERIC_NATIVE
),
3350 JS_FN("every", array_every
, 1,JSFUN_GENERIC_NATIVE
),
3356 static JSFunctionSpec array_static_methods
[] = {
3357 JS_FN("isArray", array_isArray
, 1,0),
3362 js_Array(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
3367 /* If called without new, replace obj with a new Array object. */
3368 if (!JS_IsConstructing(cx
)) {
3369 obj
= js_NewObject(cx
, &js_ArrayClass
, NULL
, NULL
);
3372 *rval
= OBJECT_TO_JSVAL(obj
);
3378 } else if (argc
> 1) {
3379 length
= (jsuint
) argc
;
3381 } else if (!JSVAL_IS_NUMBER(argv
[0])) {
3385 length
= ValueIsLength(cx
, &argv
[0]);
3386 if (JSVAL_IS_NULL(argv
[0]))
3390 return InitArrayObject(cx
, obj
, length
, vector
);
3393 JS_STATIC_ASSERT(JSSLOT_PRIVATE
== JSSLOT_ARRAY_LENGTH
);
3394 JS_STATIC_ASSERT(JSSLOT_ARRAY_LENGTH
+ 1 == JSSLOT_ARRAY_COUNT
);
3396 JSObject
* JS_FASTCALL
3397 js_NewEmptyArray(JSContext
* cx
, JSObject
* proto
)
3399 JS_ASSERT(OBJ_IS_ARRAY(cx
, proto
));
3401 JSObject
* obj
= js_NewGCObject(cx
);
3405 /* Initialize all fields of JSObject. */
3406 obj
->map
= const_cast<JSObjectMap
*>(&SharedArrayMap
);
3407 obj
->classword
= jsuword(&js_ArrayClass
);
3408 obj
->setProto(proto
);
3409 obj
->setParent(proto
->getParent());
3411 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = 0;
3412 obj
->fslots
[JSSLOT_ARRAY_COUNT
] = 0;
3413 for (unsigned i
= JSSLOT_ARRAY_COUNT
+ 1; i
!= JS_INITIAL_NSLOTS
; ++i
)
3414 obj
->fslots
[i
] = JSVAL_VOID
;
3419 JS_DEFINE_CALLINFO_2(extern, OBJECT
, js_NewEmptyArray
, CONTEXT
, OBJECT
, 0, 0)
3422 JSObject
* JS_FASTCALL
3423 js_NewEmptyArrayWithLength(JSContext
* cx
, JSObject
* proto
, int32 len
)
3427 JSObject
*obj
= js_NewEmptyArray(cx
, proto
);
3430 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = len
;
3434 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_NewEmptyArrayWithLength
, CONTEXT
, OBJECT
, INT32
, 0, 0)
3437 JSObject
* JS_FASTCALL
3438 js_NewArrayWithSlots(JSContext
* cx
, JSObject
* proto
, uint32 len
)
3440 JSObject
* obj
= js_NewEmptyArray(cx
, proto
);
3443 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = len
;
3444 if (!ResizeSlots(cx
, obj
, 0, JS_MAX(len
, ARRAY_CAPACITY_MIN
)))
3449 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_NewArrayWithSlots
, CONTEXT
, OBJECT
, UINT32
, 0, 0)
3453 js_InitArrayClass(JSContext
*cx
, JSObject
*obj
)
3455 JSObject
*proto
= JS_InitClass(cx
, obj
, NULL
, &js_ArrayClass
, js_Array
, 1,
3456 array_props
, array_methods
, NULL
, array_static_methods
);
3458 /* Initialize the Array prototype object so it gets a length property. */
3459 if (!proto
|| !InitArrayObject(cx
, proto
, 0, NULL
))
3465 js_NewArrayObject(JSContext
*cx
, jsuint length
, jsval
*vector
, JSBool holey
)
3467 JSTempValueRooter tvr
;
3470 obj
= js_NewObject(cx
, &js_ArrayClass
, NULL
, NULL
);
3475 * If this fails, the global object was not initialized and its class does
3476 * not have JSCLASS_IS_GLOBAL.
3478 JS_ASSERT(obj
->getProto());
3480 JS_PUSH_TEMP_ROOT_OBJECT(cx
, obj
, &tvr
);
3481 if (!InitArrayObject(cx
, obj
, length
, vector
, holey
))
3483 JS_POP_TEMP_ROOT(cx
, &tvr
);
3485 /* Set/clear newborn root, in case we lost it. */
3486 cx
->weakRoots
.finalizableNewborns
[FINALIZE_OBJECT
] = obj
;
3491 js_NewSlowArrayObject(JSContext
*cx
)
3493 JSObject
*obj
= js_NewObject(cx
, &js_SlowArrayClass
, NULL
, NULL
);
3495 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = 0;
3501 js_ArrayInfo(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
3506 for (i
= 0; i
< argc
; i
++) {
3509 bytes
= js_DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, argv
[i
],
3513 if (JSVAL_IS_PRIMITIVE(argv
[i
]) ||
3514 !OBJ_IS_ARRAY(cx
, (array
= JSVAL_TO_OBJECT(argv
[i
])))) {
3515 fprintf(stderr
, "%s: not array\n", bytes
);
3519 fprintf(stderr
, "%s: %s (len %lu", bytes
,
3520 OBJ_IS_DENSE_ARRAY(cx
, array
) ? "dense" : "sparse",
3521 array
->fslots
[JSSLOT_ARRAY_LENGTH
]);
3522 if (OBJ_IS_DENSE_ARRAY(cx
, array
)) {
3523 fprintf(stderr
, ", count %lu, capacity %lu",
3524 array
->fslots
[JSSLOT_ARRAY_COUNT
],
3525 js_DenseArrayCapacity(array
));
3527 fputs(")\n", stderr
);
3534 JS_FRIEND_API(JSBool
)
3535 js_CoerceArrayToCanvasImageData(JSObject
*obj
, jsuint offset
, jsuint count
,
3540 if (!obj
|| !obj
->isDenseArray())
3543 length
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
3544 if (length
< offset
+ count
)
3548 for (uintN i
= offset
; i
< offset
+count
; i
++) {
3549 jsval v
= obj
->dslots
[i
];
3550 if (JSVAL_IS_INT(v
)) {
3551 jsint vi
= JSVAL_TO_INT(v
);
3552 if (jsuint(vi
) > 255)
3553 vi
= (vi
< 0) ? 0 : 255;
3554 *dp
++ = JSUint8(vi
);
3555 } else if (JSVAL_IS_DOUBLE(v
)) {
3556 jsdouble vd
= *JSVAL_TO_DOUBLE(v
);
3557 if (!(vd
>= 0)) /* Not < so that NaN coerces to 0 */
3562 jsdouble toTruncate
= vd
+ 0.5;
3563 JSUint8 val
= JSUint8(toTruncate
);
3566 * now val is rounded to nearest, ties rounded up. We want
3567 * rounded to nearest ties to even, so check whether we had a
3570 if (val
== toTruncate
) {
3572 * It was a tie (since adding 0.5 gave us the exact integer
3573 * we want). Since we rounded up, we either already have an
3574 * even number or we have an odd number but the number we
3575 * want is one less. So just unconditionally masking out the
3576 * ones bit should do the trick to get us the value we
3592 JS_FRIEND_API(JSObject
*)
3593 js_NewArrayObjectWithCapacity(JSContext
*cx
, jsuint capacity
, jsval
**vector
)
3595 JSObject
*obj
= js_NewArrayObject(cx
, capacity
, NULL
);
3599 JSAutoTempValueRooter
tvr(cx
, obj
);
3600 if (!EnsureCapacity(cx
, obj
, capacity
, JS_FALSE
))
3603 /* Set/clear newborn root, in case we lost it. */
3604 cx
->weakRoots
.finalizableNewborns
[FINALIZE_OBJECT
] = obj
;
3608 obj
->fslots
[JSSLOT_ARRAY_COUNT
] = capacity
;
3609 *vector
= obj
->dslots
;