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 numeric-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 (DENSELEN), in dslots[-1] if
55 * In dense mode, holes in the array are represented by JSVAL_HOLE. The final
56 * slot in fslots (JSSLOT_ARRAY_LOOKUP_HOLDER) is used to store the single jsid
57 * "in use" by a lookupProperty caller.
59 * Arrays are converted to use js_SlowArrayClass when any of these conditions
61 * - the load factor (COUNT / DENSELEN) is less than 0.25, and there are
62 * more than MIN_SPARSE_INDEX slots total
63 * - a property is set that is non-numeric (and not "length"); or
64 * - a hole is filled below DENSELEN (possibly implicitly through methods like
65 * |reverse| or |splice|).
67 * In the latter two cases, property creation order is no longer index order,
68 * which necessitates use of a structure that keeps track of property creation
69 * order. (ES4, due to expectations baked into web script, requires that
70 * enumeration order be the order in which properties were created.)
72 * An alternative in the latter case (out-of-order index set) would be to
73 * maintain the scope to track property enumeration order, but still use
74 * the fast slot access. That would have the same memory cost as just using
75 * a js_SlowArrayClass, but have the same performance characteristics as
76 * a dense array for slot accesses, at some cost in code complexity.
82 #include "jsutil.h" /* Added by JSIFY */
88 #include "jsbuiltins.h"
90 #include "jsversion.h"
91 #include "jsdbgapi.h" /* for js_TraceWatchPoints */
101 #include "jsstaticcheck.h"
103 /* 2^32 - 1 as a number and a string */
104 #define MAXINDEX 4294967295u
105 #define MAXSTR "4294967295"
107 /* Small arrays are dense, no matter what. */
108 #define MIN_SPARSE_INDEX 32
110 #define INDEX_TOO_BIG(index) ((index) > JS_BIT(29) - 1)
111 #define INDEX_TOO_SPARSE(array, index) \
112 (INDEX_TOO_BIG(index) || \
113 ((index) > ARRAY_DENSE_LENGTH(array) && (index) >= MIN_SPARSE_INDEX && \
114 (index) > (uint32)((array)->fslots[JSSLOT_ARRAY_COUNT] + 1) * 4))
116 JS_STATIC_ASSERT(sizeof(JSScopeProperty
) > 4 * sizeof(jsval
));
118 #define ENSURE_SLOW_ARRAY(cx, obj) \
119 (OBJ_GET_CLASS(cx, obj) == &js_SlowArrayClass || js_MakeArraySlow(cx, obj))
122 * Determine if the id represents an array index or an XML property index.
124 * An id is an array index according to ECMA by (15.4):
126 * "Array objects give special treatment to a certain class of property names.
127 * A property name P (in the form of a string value) is an array index if and
128 * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
131 * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id)
132 * except that by using signed 32-bit integers we miss the top half of the
133 * valid range. This function checks the string representation itself; note
134 * that calling a standard conversion routine might allow strings such as
135 * "08" or "4.0" as array indices, which they are not.
138 js_IdIsIndex(jsval id
, jsuint
*indexp
)
143 if (JSVAL_IS_INT(id
)) {
145 i
= JSVAL_TO_INT(id
);
152 /* NB: id should be a string, but jsxml.c may call us with an object id. */
153 if (!JSVAL_IS_STRING(id
))
156 str
= JSVAL_TO_STRING(id
);
157 cp
= JSSTRING_CHARS(str
);
158 if (JS7_ISDEC(*cp
) && JSSTRING_LENGTH(str
) < sizeof(MAXSTR
)) {
159 jsuint index
= JS7_UNDEC(*cp
++);
163 while (JS7_ISDEC(*cp
)) {
166 index
= 10*index
+ c
;
171 /* Ensure that all characters were consumed and we didn't overflow. */
173 (oldIndex
< (MAXINDEX
/ 10) ||
174 (oldIndex
== (MAXINDEX
/ 10) && c
< (MAXINDEX
% 10))))
184 ValueIsLength(JSContext
*cx
, jsval
* vp
)
190 if (JSVAL_IS_INT(*vp
)) {
191 i
= JSVAL_TO_INT(*vp
);
197 d
= js_ValueToNumber(cx
, vp
);
198 if (JSVAL_IS_NULL(*vp
))
201 if (JSDOUBLE_IS_NaN(d
))
204 if (d
!= (jsdouble
) length
)
209 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
210 JSMSG_BAD_ARRAY_LENGTH
);
216 js_GetLengthProperty(JSContext
*cx
, JSObject
*obj
, jsuint
*lengthp
)
218 JSTempValueRooter tvr
;
223 if (OBJ_IS_ARRAY(cx
, obj
)) {
224 *lengthp
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
228 JS_PUSH_SINGLE_TEMP_ROOT(cx
, JSVAL_NULL
, &tvr
);
229 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
);
230 ok
= OBJ_GET_PROPERTY(cx
, obj
, id
, &tvr
.u
.value
);
232 if (JSVAL_IS_INT(tvr
.u
.value
)) {
233 i
= JSVAL_TO_INT(tvr
.u
.value
);
234 *lengthp
= (jsuint
)i
; /* jsuint cast does ToUint32 */
236 *lengthp
= js_ValueToECMAUint32(cx
, &tvr
.u
.value
);
237 ok
= !JSVAL_IS_NULL(tvr
.u
.value
);
240 JS_POP_TEMP_ROOT(cx
, &tvr
);
245 IndexToValue(JSContext
*cx
, jsuint index
, jsval
*vp
)
247 if (index
<= JSVAL_INT_MAX
) {
248 *vp
= INT_TO_JSVAL(index
);
251 return JS_NewDoubleValue(cx
, (jsdouble
)index
, vp
);
255 js_IndexToId(JSContext
*cx
, jsuint index
, jsid
*idp
)
259 if (index
<= JSVAL_INT_MAX
) {
260 *idp
= INT_TO_JSID(index
);
263 str
= js_NumberToString(cx
, index
);
266 return js_ValueToStringId(cx
, STRING_TO_JSVAL(str
), idp
);
270 BigIndexToId(JSContext
*cx
, JSObject
*obj
, jsuint index
, JSBool createAtom
,
273 jschar buf
[10], *start
;
276 JS_STATIC_ASSERT((jsuint
)-1 == 4294967295U);
278 JS_ASSERT(index
> JSVAL_INT_MAX
);
280 start
= JS_ARRAY_END(buf
);
283 *start
= (jschar
)('0' + index
% 10);
285 } while (index
!= 0);
288 * Skip the atomization if the class is known to store atoms corresponding
289 * to big indexes together with elements. In such case we know that the
290 * array does not have an element at the given index if its atom does not
291 * exist. Fast arrays (clasp == &js_ArrayClass) don't use atoms for
292 * any indexes, though it would be rare to see them have a big index
296 ((clasp
= OBJ_GET_CLASS(cx
, obj
)) == &js_SlowArrayClass
||
297 clasp
== &js_ArgumentsClass
||
298 clasp
== &js_ObjectClass
)) {
299 atom
= js_GetExistingStringAtom(cx
, start
, JS_ARRAY_END(buf
) - start
);
305 atom
= js_AtomizeChars(cx
, start
, JS_ARRAY_END(buf
) - start
, 0);
310 *idp
= ATOM_TO_JSID(atom
);
315 ResizeSlots(JSContext
*cx
, JSObject
*obj
, uint32 oldlen
, uint32 len
)
317 jsval
*slots
, *newslots
;
321 JS_free(cx
, obj
->dslots
- 1);
327 if (len
> ~(uint32
)0 / sizeof(jsval
)) {
328 js_ReportAllocationOverflow(cx
);
332 slots
= obj
->dslots
? obj
->dslots
- 1 : NULL
;
333 newslots
= (jsval
*) JS_realloc(cx
, slots
, sizeof (jsval
) * (len
+ 1));
337 obj
->dslots
= newslots
+ 1;
338 ARRAY_SET_DENSE_LENGTH(obj
, len
);
340 for (slots
= obj
->dslots
+ oldlen
; slots
< obj
->dslots
+ len
; slots
++)
347 EnsureLength(JSContext
*cx
, JSObject
*obj
, uint32 len
)
349 uint32 oldlen
= ARRAY_DENSE_LENGTH(obj
);
352 return ResizeSlots(cx
, obj
, oldlen
,
353 len
+ ARRAY_GROWBY
- (len
% ARRAY_GROWBY
));
359 * If the property at the given index exists, get its value into location
360 * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp
361 * to JSVAL_VOID. This function assumes that the location pointed by vp is
362 * properly rooted and can be used as GC-protected storage for temporaries.
365 GetArrayElement(JSContext
*cx
, JSObject
*obj
, jsuint index
, JSBool
*hole
,
372 if (OBJ_IS_DENSE_ARRAY(cx
, obj
) && index
< ARRAY_DENSE_LENGTH(obj
) &&
373 (*vp
= obj
->dslots
[index
]) != JSVAL_HOLE
) {
378 if (index
<= JSVAL_INT_MAX
) {
379 id
= INT_TO_JSID(index
);
381 if (!BigIndexToId(cx
, obj
, index
, JS_FALSE
, &id
))
383 if (JSVAL_IS_VOID(id
)) {
390 if (!OBJ_LOOKUP_PROPERTY(cx
, obj
, id
, &obj2
, &prop
))
396 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
397 if (!OBJ_GET_PROPERTY(cx
, obj
, id
, vp
))
405 * Set the value of the property at the given index to v assuming v is rooted.
408 SetArrayElement(JSContext
*cx
, JSObject
*obj
, jsuint index
, jsval v
)
412 if (OBJ_IS_DENSE_ARRAY(cx
, obj
)) {
413 /* Predicted/prefeched code should favor the remains-dense case. */
414 if (!INDEX_TOO_SPARSE(obj
, index
)) {
415 if (!EnsureLength(cx
, obj
, index
+ 1))
417 if (index
>= (uint32
)obj
->fslots
[JSSLOT_ARRAY_LENGTH
])
418 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = index
+ 1;
419 if (obj
->dslots
[index
] == JSVAL_HOLE
)
420 obj
->fslots
[JSSLOT_ARRAY_COUNT
]++;
421 obj
->dslots
[index
] = v
;
425 if (!js_MakeArraySlow(cx
, obj
))
429 if (index
<= JSVAL_INT_MAX
) {
430 id
= INT_TO_JSID(index
);
432 if (!BigIndexToId(cx
, obj
, index
, JS_TRUE
, &id
))
434 JS_ASSERT(!JSVAL_IS_VOID(id
));
436 return OBJ_SET_PROPERTY(cx
, obj
, id
, &v
);
440 DeleteArrayElement(JSContext
*cx
, JSObject
*obj
, jsuint index
)
445 if (OBJ_IS_DENSE_ARRAY(cx
, obj
)) {
446 if (index
< ARRAY_DENSE_LENGTH(obj
)) {
447 if (obj
->dslots
[index
] != JSVAL_HOLE
)
448 obj
->fslots
[JSSLOT_ARRAY_COUNT
]--;
449 obj
->dslots
[index
] = JSVAL_HOLE
;
454 if (index
<= JSVAL_INT_MAX
) {
455 id
= INT_TO_JSID(index
);
457 if (!BigIndexToId(cx
, obj
, index
, JS_FALSE
, &id
))
459 if (JSVAL_IS_VOID(id
))
462 return OBJ_DELETE_PROPERTY(cx
, obj
, id
, &junk
);
466 * When hole is true, delete the property at the given index. Otherwise set
467 * its value to v assuming v is rooted.
470 SetOrDeleteArrayElement(JSContext
*cx
, JSObject
*obj
, jsuint index
,
471 JSBool hole
, jsval v
)
474 JS_ASSERT(JSVAL_IS_VOID(v
));
475 return DeleteArrayElement(cx
, obj
, index
);
477 return SetArrayElement(cx
, obj
, index
, v
);
481 js_SetLengthProperty(JSContext
*cx
, JSObject
*obj
, jsuint length
)
486 if (!IndexToValue(cx
, length
, &v
))
488 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
);
489 return OBJ_SET_PROPERTY(cx
, obj
, id
, &v
);
493 js_HasLengthProperty(JSContext
*cx
, JSObject
*obj
, jsuint
*lengthp
)
495 JSErrorReporter older
;
496 JSTempValueRooter tvr
;
500 older
= JS_SetErrorReporter(cx
, NULL
);
501 JS_PUSH_SINGLE_TEMP_ROOT(cx
, JSVAL_NULL
, &tvr
);
502 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
);
503 ok
= OBJ_GET_PROPERTY(cx
, obj
, id
, &tvr
.u
.value
);
504 JS_SetErrorReporter(cx
, older
);
506 *lengthp
= ValueIsLength(cx
, &tvr
.u
.value
);
507 ok
= !JSVAL_IS_NULL(tvr
.u
.value
);
509 JS_POP_TEMP_ROOT(cx
, &tvr
);
514 js_IsArrayLike(JSContext
*cx
, JSObject
*obj
, JSBool
*answerp
, jsuint
*lengthp
)
518 clasp
= OBJ_GET_CLASS(cx
, obj
);
519 *answerp
= (clasp
== &js_ArgumentsClass
|| clasp
== &js_ArrayClass
||
520 clasp
== &js_SlowArrayClass
);
525 return js_GetLengthProperty(cx
, obj
, lengthp
);
529 * The 'length' property of all native Array instances is a shared permanent
530 * property of Array.prototype, so it appears to be a direct property of each
531 * array instance delegating to that Array.prototype. It accesses the private
532 * slot reserved by js_ArrayClass.
534 * Since SpiderMonkey supports cross-class prototype-based delegation, we have
535 * to be careful about the length getter and setter being called on an object
536 * not of Array class. For the getter, we search obj's prototype chain for the
537 * array that caused this getter to be invoked. In the setter case to overcome
538 * the JSPROP_SHARED attribute, we must define a shadowing length property.
541 array_length_getter(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
544 if (OBJ_IS_ARRAY(cx
, obj
))
545 return IndexToValue(cx
, obj
->fslots
[JSSLOT_ARRAY_LENGTH
], vp
);
546 } while ((obj
= OBJ_GET_PROTO(cx
, obj
)) != NULL
);
551 array_length_setter(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
553 jsuint newlen
, oldlen
, gap
, index
;
556 JSTempValueRooter tvr
;
559 if (!OBJ_IS_ARRAY(cx
, obj
)) {
560 jsid lengthId
= ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
);
562 return OBJ_DEFINE_PROPERTY(cx
, obj
, lengthId
, *vp
, NULL
, NULL
,
563 JSPROP_ENUMERATE
, NULL
);
566 newlen
= ValueIsLength(cx
, vp
);
567 if (JSVAL_IS_NULL(*vp
))
569 oldlen
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
571 if (oldlen
== newlen
)
574 if (!IndexToValue(cx
, newlen
, vp
))
577 if (oldlen
< newlen
) {
578 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = newlen
;
582 if (OBJ_IS_DENSE_ARRAY(cx
, obj
)) {
583 if (ARRAY_DENSE_LENGTH(obj
) && !ResizeSlots(cx
, obj
, oldlen
, newlen
))
585 } else if (oldlen
- newlen
< (1 << 24)) {
588 if (!JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
) ||
589 !DeleteArrayElement(cx
, obj
, oldlen
)) {
592 } while (oldlen
!= newlen
);
595 * We are going to remove a lot of indexes in a presumably sparse
596 * array. So instead of looping through indexes between newlen and
597 * oldlen, we iterate through all properties and remove those that
598 * correspond to indexes in the half-open range [newlen, oldlen). See
601 iter
= JS_NewPropertyIterator(cx
, obj
);
605 /* Protect iter against GC in OBJ_DELETE_PROPERTY. */
606 JS_PUSH_TEMP_ROOT_OBJECT(cx
, iter
, &tvr
);
607 gap
= oldlen
- newlen
;
609 ok
= (JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
) &&
610 JS_NextProperty(cx
, iter
, &id
));
613 if (JSVAL_IS_VOID(id
))
615 if (js_IdIsIndex(id
, &index
) && index
- newlen
< gap
) {
616 ok
= OBJ_DELETE_PROPERTY(cx
, obj
, id
, &junk
);
621 JS_POP_TEMP_ROOT(cx
, &tvr
);
626 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = newlen
;
631 array_lookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
635 union { JSProperty
*p
; jsval
*v
; } u
;
637 if (!OBJ_IS_DENSE_ARRAY(cx
, obj
))
638 return js_LookupProperty(cx
, obj
, id
, objp
, propp
);
641 * We have only indexed properties up to DENSELEN (excepting holes), plus
642 * the length property. For all else, we delegate to the prototype.
644 if (id
!= ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
) &&
645 (!js_IdIsIndex(id
, &i
) ||
646 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] == 0 ||
647 i
>= ARRAY_DENSE_LENGTH(obj
) ||
648 obj
->dslots
[i
] == JSVAL_HOLE
))
650 JSObject
*proto
= STOBJ_GET_PROTO(obj
);
658 return OBJ_LOOKUP_PROPERTY(cx
, proto
, id
, objp
, propp
);
661 /* FIXME 417501: threadsafety: could race with a lookup on another thread.
662 * If we can only have a single lookup active per context, we could
663 * pigeonhole this on the context instead. */
664 JS_ASSERT(JSVAL_IS_VOID(obj
->fslots
[JSSLOT_ARRAY_LOOKUP_HOLDER
]));
665 obj
->fslots
[JSSLOT_ARRAY_LOOKUP_HOLDER
] = (jsval
) id
;
666 u
.v
= &(obj
->fslots
[JSSLOT_ARRAY_LOOKUP_HOLDER
]);
673 array_dropProperty(JSContext
*cx
, JSObject
*obj
, JSProperty
*prop
)
675 JS_ASSERT_IF(OBJ_IS_DENSE_ARRAY(cx
, obj
),
676 !JSVAL_IS_VOID(obj
->fslots
[JSSLOT_ARRAY_LOOKUP_HOLDER
]));
678 obj
->fslots
[JSSLOT_ARRAY_LOOKUP_HOLDER
] = JSVAL_VOID
;
683 array_getProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
687 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
))
688 return IndexToValue(cx
, obj
->fslots
[JSSLOT_ARRAY_LENGTH
], vp
);
690 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.protoAtom
)) {
691 *vp
= STOBJ_GET_SLOT(obj
, JSSLOT_PROTO
);
695 if (!OBJ_IS_DENSE_ARRAY(cx
, obj
))
696 return js_GetProperty(cx
, obj
, id
, vp
);
698 if (!js_IdIsIndex(ID_TO_VALUE(id
), &i
) || i
>= ARRAY_DENSE_LENGTH(obj
) ||
699 obj
->dslots
[i
] == JSVAL_HOLE
) {
702 JSScopeProperty
*sprop
;
704 JSObject
*proto
= STOBJ_GET_PROTO(obj
);
711 if (js_LookupPropertyWithFlags(cx
, proto
, id
, cx
->resolveFlags
,
716 if (OBJ_IS_NATIVE(obj2
)) {
717 sprop
= (JSScopeProperty
*) prop
;
718 if (!js_NativeGet(cx
, obj
, obj2
, sprop
, vp
))
721 OBJ_DROP_PROPERTY(cx
, obj2
, prop
);
726 *vp
= obj
->dslots
[i
];
731 slowarray_addProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
733 jsuint index
, length
;
735 if (!js_IdIsIndex(id
, &index
))
737 length
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
739 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = index
+ 1;
744 slowarray_trace(JSTracer
*trc
, JSObject
*obj
)
746 uint32 length
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
748 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_SlowArrayClass
);
751 * Move JSSLOT_ARRAY_LENGTH aside to prevent the GC from treating
752 * untagged integer values as objects or strings.
754 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = JSVAL_VOID
;
755 js_TraceObject(trc
, obj
);
756 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = length
;
759 static JSObjectOps js_SlowArrayObjectOps
;
762 slowarray_getObjectOps(JSContext
*cx
, JSClass
*clasp
)
764 return &js_SlowArrayObjectOps
;
768 array_setProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
772 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
))
773 return array_length_setter(cx
, obj
, id
, vp
);
775 if (!OBJ_IS_DENSE_ARRAY(cx
, obj
))
776 return js_SetProperty(cx
, obj
, id
, vp
);
778 if (!js_IdIsIndex(id
, &i
) || INDEX_TOO_SPARSE(obj
, i
)) {
779 if (!js_MakeArraySlow(cx
, obj
))
781 return js_SetProperty(cx
, obj
, id
, vp
);
784 if (!EnsureLength(cx
, obj
, i
+ 1))
787 if (i
>= (uint32
)obj
->fslots
[JSSLOT_ARRAY_LENGTH
])
788 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = i
+ 1;
789 if (obj
->dslots
[i
] == JSVAL_HOLE
)
790 obj
->fslots
[JSSLOT_ARRAY_COUNT
]++;
791 obj
->dslots
[i
] = *vp
;
797 js_Array_dense_setelem(JSContext
* cx
, JSObject
* obj
, jsint i
, jsval v
)
799 JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx
, obj
));
802 jsuint length
= ARRAY_DENSE_LENGTH(obj
);
803 if ((jsuint
)i
< length
) {
804 if (obj
->dslots
[i
] == JSVAL_HOLE
) {
805 if (cx
->runtime
->anyArrayProtoHasElement
)
807 if (i
>= obj
->fslots
[JSSLOT_ARRAY_LENGTH
])
808 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = i
+ 1;
809 obj
->fslots
[JSSLOT_ARRAY_COUNT
]++;
815 return OBJ_SET_PROPERTY(cx
, obj
, INT_TO_JSID(i
), &v
);
820 array_defineProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval value
,
821 JSPropertyOp getter
, JSPropertyOp setter
, uintN attrs
,
827 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
))
830 isIndex
= js_IdIsIndex(ID_TO_VALUE(id
), &i
);
831 if (!isIndex
|| attrs
!= JSPROP_ENUMERATE
) {
832 if (!ENSURE_SLOW_ARRAY(cx
, obj
))
834 if (isIndex
&& STOBJ_IS_DELEGATE(obj
))
835 cx
->runtime
->anyArrayProtoHasElement
= JS_TRUE
;
836 return js_DefineProperty(cx
, obj
, id
, value
, getter
, setter
, attrs
, propp
);
839 return array_setProperty(cx
, obj
, id
, &value
);
843 array_getAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, JSProperty
*prop
,
846 *attrsp
= id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
)
847 ? JSPROP_PERMANENT
: JSPROP_ENUMERATE
;
852 array_setAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, JSProperty
*prop
,
855 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
856 JSMSG_CANT_SET_ARRAY_ATTRS
);
861 array_deleteProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*rval
)
865 if (!OBJ_IS_DENSE_ARRAY(cx
, obj
))
866 return js_DeleteProperty(cx
, obj
, id
, rval
);
868 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
)) {
873 if (js_IdIsIndex(id
, &i
) && i
< ARRAY_DENSE_LENGTH(obj
) &&
874 obj
->dslots
[i
] != JSVAL_HOLE
) {
875 obj
->fslots
[JSSLOT_ARRAY_COUNT
]--;
876 obj
->dslots
[i
] = JSVAL_HOLE
;
884 * JSObjectOps.enumerate implementation.
886 * For a fast array, JSENUMERATE_INIT captures in the enumeration state both
887 * the length of the array and the bitmap indicating the positions of holes in
888 * the array. This ensures that adding or deleting array elements does not
889 * affect the sequence of indexes JSENUMERATE_NEXT returns.
891 * For a common case of an array without holes, to represent the state we pack
892 * the (nextEnumerationIndex, arrayLength) pair as a pseudo-boolean jsval.
893 * This is possible when length <= PACKED_UINT_PAIR_BITS. For arrays with
894 * greater length or holes we allocate the JSIndexIterState structure and
895 * store it as an int-tagged private pointer jsval. For a slow array we
896 * delegate the enumeration implementation to js_Enumerate in
897 * slowarray_enumerate.
899 * Array mutations can turn a fast array into a slow one after the enumeration
900 * starts. When this happens, slowarray_enumerate receives a state created
901 * when the array was fast. To distinguish such fast state from a slow state,
902 * which is an int-tagged pointer that js_Enumerate creates, we set not one
903 * but two lowest bits when tagging a JSIndexIterState pointer -- see
904 * INDEX_ITER_TAG usage below. Thus, when slowarray_enumerate receives a state
905 * tagged with JSVAL_BOOLEAN or with two lowest bits set, it knows that this
906 * is a fast state so it calls array_enumerate to continue enumerating the
907 * indexes present in the original fast array.
910 #define PACKED_UINT_PAIR_BITS 14
911 #define PACKED_UINT_PAIR_MASK JS_BITMASK(PACKED_UINT_PAIR_BITS)
913 #define UINT_PAIR_TO_BOOLEAN_JSVAL(i,j) \
914 (JS_ASSERT((uint32) (i) <= PACKED_UINT_PAIR_MASK), \
915 JS_ASSERT((uint32) (j) <= PACKED_UINT_PAIR_MASK), \
916 ((jsval) (i) << (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)) | \
917 ((jsval) (j) << (JSVAL_TAGBITS)) | \
918 (jsval) JSVAL_BOOLEAN)
920 #define BOOLEAN_JSVAL_TO_UINT_PAIR(v,i,j) \
921 (JS_ASSERT(JSVAL_TAG(v) == JSVAL_BOOLEAN), \
922 (i) = (uint32) ((v) >> (PACKED_UINT_PAIR_BITS + JSVAL_TAGBITS)), \
923 (j) = (uint32) ((v) >> JSVAL_TAGBITS) & PACKED_UINT_PAIR_MASK, \
924 JS_ASSERT((i) <= PACKED_UINT_PAIR_MASK))
926 JS_STATIC_ASSERT(PACKED_UINT_PAIR_BITS
* 2 + JSVAL_TAGBITS
<= JS_BITS_PER_WORD
);
928 typedef struct JSIndexIterState
{
934 * Variable-length bitmap representing array's holes. It must not be
935 * accessed when hasHoles is false.
940 #define INDEX_ITER_TAG 3
942 JS_STATIC_ASSERT(JSVAL_INT
== 1);
945 array_enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
946 jsval
*statep
, jsid
*idp
)
949 JSIndexIterState
*ii
;
952 case JSENUMERATE_INIT
:
953 JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx
, obj
));
954 length
= ARRAY_DENSE_LENGTH(obj
);
956 *idp
= INT_TO_JSVAL(obj
->fslots
[JSSLOT_ARRAY_COUNT
]);
958 for (i
= 0; i
!= length
; ++i
) {
959 if (obj
->dslots
[i
] == JSVAL_HOLE
) {
961 ii
= (JSIndexIterState
*)
962 JS_malloc(cx
, offsetof(JSIndexIterState
, holes
) +
963 JS_BITMAP_SIZE(length
));
966 ii
->hasHoles
= JS_TRUE
;
967 memset(ii
->holes
, 0, JS_BITMAP_SIZE(length
));
969 JS_SET_BIT(ii
->holes
, i
);
973 /* Array has no holes. */
974 if (length
<= PACKED_UINT_PAIR_MASK
) {
975 *statep
= UINT_PAIR_TO_BOOLEAN_JSVAL(0, length
);
978 ii
= (JSIndexIterState
*)
979 JS_malloc(cx
, offsetof(JSIndexIterState
, holes
));
982 ii
->hasHoles
= JS_FALSE
;
986 *statep
= (jsval
) ii
| INDEX_ITER_TAG
;
987 JS_ASSERT(*statep
& JSVAL_INT
);
990 case JSENUMERATE_NEXT
:
991 if (JSVAL_TAG(*statep
) == JSVAL_BOOLEAN
) {
992 BOOLEAN_JSVAL_TO_UINT_PAIR(*statep
, i
, length
);
994 *idp
= INT_TO_JSID(i
);
995 *statep
= UINT_PAIR_TO_BOOLEAN_JSVAL(i
+ 1, length
);
999 JS_ASSERT((*statep
& INDEX_ITER_TAG
) == INDEX_ITER_TAG
);
1000 ii
= (JSIndexIterState
*) (*statep
& ~INDEX_ITER_TAG
);
1002 if (i
!= ii
->length
) {
1003 /* Skip holes if any. */
1005 while (JS_TEST_BIT(ii
->holes
, i
) && ++i
!= ii
->length
)
1008 if (i
!= ii
->length
) {
1010 return js_IndexToId(cx
, i
, idp
);
1016 case JSENUMERATE_DESTROY
:
1017 if (JSVAL_TAG(*statep
) != JSVAL_BOOLEAN
) {
1018 JS_ASSERT((*statep
& INDEX_ITER_TAG
) == INDEX_ITER_TAG
);
1019 ii
= (JSIndexIterState
*) (*statep
& ~INDEX_ITER_TAG
);
1022 *statep
= JSVAL_NULL
;
1029 slowarray_enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
1030 jsval
*statep
, jsid
*idp
)
1034 /* Are we continuing an enumeration that started when we were dense? */
1035 if (enum_op
!= JSENUMERATE_INIT
) {
1036 if (JSVAL_TAG(*statep
) == JSVAL_BOOLEAN
||
1037 (*statep
& INDEX_ITER_TAG
) == INDEX_ITER_TAG
) {
1038 return array_enumerate(cx
, obj
, enum_op
, statep
, idp
);
1040 JS_ASSERT((*statep
& INDEX_ITER_TAG
) == JSVAL_INT
);
1042 ok
= js_Enumerate(cx
, obj
, enum_op
, statep
, idp
);
1043 JS_ASSERT(*statep
== JSVAL_NULL
|| (*statep
& INDEX_ITER_TAG
) == JSVAL_INT
);
1048 array_finalize(JSContext
*cx
, JSObject
*obj
)
1051 JS_free(cx
, obj
->dslots
- 1);
1056 array_trace(JSTracer
*trc
, JSObject
*obj
)
1062 JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx
, obj
));
1064 length
= ARRAY_DENSE_LENGTH(obj
);
1065 for (i
= 0; i
< length
; i
++) {
1067 if (JSVAL_IS_TRACEABLE(v
)) {
1068 JS_SET_TRACING_INDEX(trc
, "array_dslots", i
);
1069 JS_CallTracer(trc
, JSVAL_TO_TRACEABLE(v
), JSVAL_TRACE_KIND(v
));
1073 for (i
= JSSLOT_PROTO
; i
<= JSSLOT_PARENT
; ++i
) {
1074 v
= STOBJ_GET_SLOT(obj
, i
);
1075 if (JSVAL_IS_TRACEABLE(v
)) {
1076 JS_SET_TRACING_DETAILS(trc
, js_PrintObjectSlotName
, obj
, i
);
1077 JS_CallTracer(trc
, JSVAL_TO_TRACEABLE(v
), JSVAL_TRACE_KIND(v
));
1082 static JSObjectMap
*
1083 array_newObjectMap(JSContext
*cx
, jsrefcount nrefs
, JSObjectOps
*ops
,
1084 JSClass
*clasp
, JSObject
*obj
)
1087 extern JSClass js_ArrayClass
;
1088 extern JSObjectOps js_ArrayObjectOps
;
1090 JSObjectMap
*map
= (JSObjectMap
*) JS_malloc(cx
, sizeof(*map
));
1095 JS_ASSERT(ops
== &js_ArrayObjectOps
);
1097 JS_ASSERT(clasp
== &js_ArrayClass
);
1098 map
->freeslot
= JSSLOT_FREE(clasp
);
1104 array_destroyObjectMap(JSContext
*cx
, JSObjectMap
*map
)
1109 JSObjectOps js_ArrayObjectOps
= {
1110 array_newObjectMap
, array_destroyObjectMap
,
1111 array_lookupProperty
, array_defineProperty
,
1112 array_getProperty
, array_setProperty
,
1113 array_getAttributes
, array_setAttributes
,
1114 array_deleteProperty
, js_DefaultValue
,
1115 array_enumerate
, js_CheckAccess
,
1116 NULL
, array_dropProperty
,
1118 NULL
, js_HasInstance
,
1119 js_SetProtoOrParent
, js_SetProtoOrParent
,
1124 static JSObjectOps
*
1125 array_getObjectOps(JSContext
*cx
, JSClass
*clasp
)
1127 return &js_ArrayObjectOps
;
1130 JSClass js_ArrayClass
= {
1132 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Array
) |
1133 JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_NEW_ENUMERATE
,
1134 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
1135 JS_EnumerateStub
, JS_ResolveStub
, js_TryValueOf
, array_finalize
,
1136 array_getObjectOps
, NULL
, NULL
, NULL
,
1137 NULL
, NULL
, NULL
, NULL
1140 JSClass js_SlowArrayClass
= {
1142 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Array
),
1143 slowarray_addProperty
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
1144 JS_EnumerateStub
, JS_ResolveStub
, js_TryValueOf
, JS_FinalizeStub
,
1145 slowarray_getObjectOps
, NULL
, NULL
, NULL
,
1146 NULL
, NULL
, NULL
, NULL
1150 * Convert an array object from fast-and-dense to slow-and-flexible.
1153 js_MakeArraySlow(JSContext
*cx
, JSObject
*obj
)
1155 JSObjectMap
*map
, *oldmap
;
1158 JS_ASSERT(OBJ_GET_CLASS(cx
, obj
) == &js_ArrayClass
);
1160 /* Create a native scope. */
1161 map
= js_NewObjectMap(cx
, obj
->map
->nrefs
, &js_SlowArrayObjectOps
,
1162 &js_SlowArrayClass
, obj
);
1166 length
= ARRAY_DENSE_LENGTH(obj
);
1168 map
->freeslot
= STOBJ_NSLOTS(obj
) + JS_INITIAL_NSLOTS
;
1169 obj
->dslots
[-1] = JS_INITIAL_NSLOTS
+ length
;
1171 map
->freeslot
= STOBJ_NSLOTS(obj
);
1174 /* Create new properties pointing to existing values in dslots */
1175 for (i
= 0; i
< length
; i
++) {
1177 JSScopeProperty
*sprop
;
1179 if (!JS_ValueToId(cx
, INT_TO_JSVAL(i
), &id
))
1182 if (obj
->dslots
[i
] == JSVAL_HOLE
) {
1183 obj
->dslots
[i
] = JSVAL_VOID
;
1187 sprop
= js_AddScopeProperty(cx
, (JSScope
*)map
, id
, NULL
, NULL
,
1188 i
+ JS_INITIAL_NSLOTS
, JSPROP_ENUMERATE
,
1195 * Render our formerly-reserved count property GC-safe. If length fits in
1196 * a jsval, set our slow/sparse COUNT to the current length as a jsval, so
1197 * we can tell when only named properties have been added to a dense array
1198 * to make it slow-but-not-sparse.
1200 length
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
1201 obj
->fslots
[JSSLOT_ARRAY_COUNT
] = INT_FITS_IN_JSVAL(length
)
1202 ? INT_TO_JSVAL(length
)
1205 /* Make sure we preserve any flags borrowing bits in classword. */
1206 obj
->classword
^= (jsuword
) &js_ArrayClass
;
1207 obj
->classword
|= (jsuword
) &js_SlowArrayClass
;
1209 /* Swap in our new map. */
1212 array_destroyObjectMap(cx
, oldmap
);
1217 js_DestroyObjectMap(cx
, map
);
1221 enum ArrayToStringOp
{
1228 * When op is TO_STRING or TO_LOCALE_STRING sep indicates a separator to use
1229 * or "," when sep is NULL.
1230 * When op is TO_SOURCE sep must be NULL.
1233 array_join_sub(JSContext
*cx
, JSObject
*obj
, enum ArrayToStringOp op
,
1234 JSString
*sep
, jsval
*rval
)
1237 jsuint length
, index
;
1238 jschar
*chars
, *ochars
;
1239 size_t nchars
, growth
, seplen
, tmplen
, extratail
;
1240 const jschar
*sepstr
;
1245 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
1247 ok
= js_GetLengthProperty(cx
, obj
, &length
);
1251 he
= js_EnterSharpObject(cx
, obj
, NULL
, &chars
);
1255 growth
= (size_t) -1;
1258 if (op
== TO_SOURCE
) {
1260 #if JS_HAS_SHARP_VARS
1261 nchars
= js_strlen(chars
);
1272 * Always allocate 2 extra chars for closing ']' and terminating 0
1273 * and then preallocate 1 + extratail to include starting '['.
1276 growth
= (1 + extratail
) * sizeof(jschar
);
1279 chars
= (jschar
*) malloc(growth
);
1284 nchars
= js_strlen(chars
);
1285 growth
+= nchars
* sizeof(jschar
);
1286 chars
= (jschar
*)realloc((ochars
= chars
), growth
);
1292 chars
[nchars
++] = '[';
1293 JS_ASSERT(sep
== NULL
);
1294 sepstr
= NULL
; /* indicates to use ", " as separator */
1298 * Free any sharp variable definition in chars. Normally, we would
1299 * MAKE_SHARP(he) so that only the first sharp variable annotation is
1300 * a definition, and all the rest are references, but in the current
1301 * case of (op != TO_SOURCE), we don't need chars at all.
1307 extratail
= 1; /* allocate extra char for terminating 0 */
1309 /* Return the empty string on a cycle as well as on empty join. */
1310 if (IS_BUSY(he
) || length
== 0) {
1311 js_LeaveSharpObject(cx
, NULL
);
1312 *rval
= JS_GetEmptyStringValue(cx
);
1316 /* Flag he as BUSY so we can distinguish a cycle from a join-point. */
1320 JSSTRING_CHARS_AND_LENGTH(sep
, sepstr
, seplen
);
1322 sepstr
= NULL
; /* indicates to use "," as separator */
1327 /* Use rval to locally root each element value as we loop and convert. */
1328 for (index
= 0; index
< length
; index
++) {
1329 ok
= (JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
) &&
1330 GetArrayElement(cx
, obj
, index
, &hole
, rval
));
1335 (JSVAL_IS_VOID(*rval
) || JSVAL_IS_NULL(*rval
)))) {
1336 str
= cx
->runtime
->emptyString
;
1338 if (op
== TO_LOCALE_STRING
) {
1341 atom
= cx
->runtime
->atomState
.toLocaleStringAtom
;
1342 ok
= js_ValueToObject(cx
, *rval
, &robj
);
1344 /* Re-use *rval to protect robj temporarily. */
1345 *rval
= OBJECT_TO_JSVAL(robj
);
1346 ok
= js_TryMethod(cx
, robj
, atom
, 0, NULL
, rval
);
1350 str
= js_ValueToString(cx
, *rval
);
1351 } else if (op
== TO_STRING
) {
1352 str
= js_ValueToString(cx
, *rval
);
1354 JS_ASSERT(op
== TO_SOURCE
);
1355 str
= js_ValueToSource(cx
, *rval
);
1364 * Do not append separator after the last element unless it is a hole
1365 * and we are in toSource. In that case we append single ",".
1367 if (index
+ 1 == length
)
1368 seplen
= (hole
&& op
== TO_SOURCE
) ? 1 : 0;
1370 /* Allocate 1 at end for closing bracket and zero. */
1371 tmplen
= JSSTRING_LENGTH(str
);
1372 growth
= nchars
+ tmplen
+ seplen
+ extratail
;
1373 if (nchars
> growth
|| tmplen
> growth
||
1374 growth
> (size_t)-1 / sizeof(jschar
)) {
1381 growth
*= sizeof(jschar
);
1382 JS_COUNT_OPERATION(cx
, JSOW_ALLOCATION
);
1384 chars
= (jschar
*) malloc(growth
);
1388 chars
= (jschar
*) realloc((ochars
= chars
), growth
);
1395 js_strncpy(&chars
[nchars
], JSSTRING_CHARS(str
), tmplen
);
1400 js_strncpy(&chars
[nchars
], sepstr
, seplen
);
1402 JS_ASSERT(seplen
== 1 || seplen
== 2);
1403 chars
[nchars
] = ',';
1405 chars
[nchars
+ 1] = ' ';
1412 if (op
== TO_SOURCE
) {
1414 chars
[nchars
++] = ']';
1418 js_LeaveSharpObject(cx
, NULL
);
1427 JS_ReportOutOfMemory(cx
);
1431 JS_ASSERT(growth
== (size_t)-1 || (nchars
+ 1) * sizeof(jschar
) == growth
);
1432 str
= js_NewString(cx
, chars
, nchars
);
1437 *rval
= STRING_TO_JSVAL(str
);
1443 array_toSource(JSContext
*cx
, uintN argc
, jsval
*vp
)
1447 obj
= JS_THIS_OBJECT(cx
, vp
);
1448 if (OBJ_GET_CLASS(cx
, obj
) != &js_SlowArrayClass
&&
1449 !JS_InstanceOf(cx
, obj
, &js_ArrayClass
, vp
+ 2)) {
1452 return array_join_sub(cx
, obj
, TO_SOURCE
, NULL
, vp
);
1457 array_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1461 obj
= JS_THIS_OBJECT(cx
, vp
);
1462 if (OBJ_GET_CLASS(cx
, obj
) != &js_SlowArrayClass
&&
1463 !JS_InstanceOf(cx
, obj
, &js_ArrayClass
, vp
+ 2)) {
1466 return array_join_sub(cx
, obj
, TO_STRING
, NULL
, vp
);
1470 array_toLocaleString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1474 obj
= JS_THIS_OBJECT(cx
, vp
);
1475 if (OBJ_GET_CLASS(cx
, obj
) != &js_SlowArrayClass
&&
1476 !JS_InstanceOf(cx
, obj
, &js_ArrayClass
, vp
+ 2)) {
1481 * Passing comma here as the separator. Need a way to get a
1482 * locale-specific version.
1484 return array_join_sub(cx
, obj
, TO_LOCALE_STRING
, NULL
, vp
);
1488 InitArrayElements(JSContext
*cx
, JSObject
*obj
, jsuint start
, jsuint end
,
1491 if (OBJ_IS_DENSE_ARRAY(cx
, obj
)) {
1492 if (!EnsureLength(cx
, obj
, end
))
1495 if (end
> (uint32
)obj
->fslots
[JSSLOT_ARRAY_LENGTH
])
1496 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = end
;
1498 memcpy(obj
->dslots
+ start
, vector
, sizeof(jsval
) * (end
- start
));
1502 while (start
!= end
) {
1503 if (!JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
) ||
1504 !SetArrayElement(cx
, obj
, start
++, *vector
++)) {
1512 InitArrayObject(JSContext
*cx
, JSObject
*obj
, jsuint length
, jsval
*vector
,
1513 JSBool holey
= JS_FALSE
)
1515 JS_ASSERT(OBJ_IS_ARRAY(cx
, obj
));
1517 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = length
;
1520 if (!EnsureLength(cx
, obj
, length
))
1523 jsuint count
= length
;
1525 memcpy(obj
->dslots
, vector
, length
* sizeof (jsval
));
1527 for (jsuint i
= 0; i
< length
; i
++) {
1528 if (vector
[i
] == JSVAL_HOLE
)
1530 obj
->dslots
[i
] = vector
[i
];
1533 obj
->fslots
[JSSLOT_ARRAY_COUNT
] = count
;
1535 obj
->fslots
[JSSLOT_ARRAY_COUNT
] = 0;
1541 static JSString
* FASTCALL
1542 Array_p_join(JSContext
* cx
, JSObject
* obj
, JSString
*str
)
1545 if (!array_join_sub(cx
, obj
, TO_STRING
, str
, &v
))
1547 JS_ASSERT(JSVAL_IS_STRING(v
));
1548 return JSVAL_TO_STRING(v
);
1551 static JSString
* FASTCALL
1552 Array_p_toString(JSContext
* cx
, JSObject
* obj
)
1555 if (!array_join_sub(cx
, obj
, TO_STRING
, NULL
, &v
))
1557 JS_ASSERT(JSVAL_IS_STRING(v
));
1558 return JSVAL_TO_STRING(v
);
1563 * Perl-inspired join, reverse, and sort.
1566 array_join(JSContext
*cx
, uintN argc
, jsval
*vp
)
1571 if (argc
== 0 || JSVAL_IS_VOID(vp
[2])) {
1574 str
= js_ValueToString(cx
, vp
[2]);
1577 vp
[2] = STRING_TO_JSVAL(str
);
1579 obj
= JS_THIS_OBJECT(cx
, vp
);
1580 return obj
&& array_join_sub(cx
, obj
, TO_STRING
, str
, vp
);
1584 array_reverse(JSContext
*cx
, uintN argc
, jsval
*vp
)
1587 JSTempValueRooter tvr
;
1588 jsuint len
, half
, i
;
1589 JSBool ok
, hole
, hole2
;
1591 obj
= JS_THIS_OBJECT(cx
, vp
);
1592 if (!obj
|| !js_GetLengthProperty(cx
, obj
, &len
))
1596 JS_PUSH_SINGLE_TEMP_ROOT(cx
, JSVAL_NULL
, &tvr
);
1598 for (i
= 0; i
< half
; i
++) {
1599 ok
= JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
) &&
1600 GetArrayElement(cx
, obj
, i
, &hole
, &tvr
.u
.value
) &&
1601 GetArrayElement(cx
, obj
, len
- i
- 1, &hole2
, vp
) &&
1602 SetOrDeleteArrayElement(cx
, obj
, len
- i
- 1, hole
, tvr
.u
.value
) &&
1603 SetOrDeleteArrayElement(cx
, obj
, i
, hole2
, *vp
);
1607 JS_POP_TEMP_ROOT(cx
, &tvr
);
1609 *vp
= OBJECT_TO_JSVAL(obj
);
1613 typedef struct MSortArgs
{
1620 /* Helper function for js_MergeSort. */
1622 MergeArrays(MSortArgs
*msa
, void *src
, void *dest
, size_t run1
, size_t run2
)
1624 void *arg
, *a
, *b
, *c
;
1625 size_t elsize
, runtotal
;
1630 runtotal
= run1
+ run2
;
1632 elsize
= msa
->elsize
;
1635 fastcopy
= msa
->fastcopy
;
1637 #define CALL_CMP(a, b) \
1638 if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE;
1640 /* Copy runs already in sorted order. */
1641 b
= (char *)src
+ run1
* elsize
;
1642 a
= (char *)b
- elsize
;
1644 if (cmp_result
<= 0) {
1645 memcpy(dest
, src
, runtotal
* elsize
);
1649 #define COPY_ONE(p,q,n) \
1650 (fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n))
1654 for (; runtotal
!= 0; runtotal
--) {
1655 JSBool from_a
= run2
== 0;
1656 if (!from_a
&& run1
!= 0) {
1658 from_a
= cmp_result
<= 0;
1662 COPY_ONE(c
, a
, elsize
);
1664 a
= (char *)a
+ elsize
;
1666 COPY_ONE(c
, b
, elsize
);
1668 b
= (char *)b
+ elsize
;
1670 c
= (char *)c
+ elsize
;
1679 * This sort is stable, i.e. sequence of equal elements is preserved.
1680 * See also bug #224128.
1683 js_MergeSort(void *src
, size_t nel
, size_t elsize
,
1684 JSComparator cmp
, void *arg
, void *tmp
)
1686 void *swap
, *vec1
, *vec2
;
1688 size_t i
, j
, lo
, hi
, run
;
1692 /* Avoid memcpy overhead for word-sized and word-aligned elements. */
1693 fastcopy
= (elsize
== sizeof(jsval
) &&
1694 (((jsuword
) src
| (jsuword
) tmp
) & JSVAL_ALIGN
) == 0);
1695 #define COPY_ONE(p,q,n) \
1696 (fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n))
1697 #define CALL_CMP(a, b) \
1698 if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE;
1699 #define INS_SORT_INT 4
1702 * Apply insertion sort to small chunks to reduce the number of merge
1705 for (lo
= 0; lo
< nel
; lo
+= INS_SORT_INT
) {
1706 hi
= lo
+ INS_SORT_INT
;
1709 for (i
= lo
+ 1; i
< hi
; i
++) {
1710 vec1
= (char *)src
+ i
* elsize
;
1711 vec2
= (char *)vec1
- elsize
;
1712 for (j
= i
; j
> lo
; j
--) {
1713 CALL_CMP(vec2
, vec1
);
1714 /* "<=" instead of "<" insures the sort is stable */
1715 if (cmp_result
<= 0) {
1719 /* Swap elements, using "tmp" as tmp storage */
1720 COPY_ONE(tmp
, vec2
, elsize
);
1721 COPY_ONE(vec2
, vec1
, elsize
);
1722 COPY_ONE(vec1
, tmp
, elsize
);
1724 vec2
= (char *)vec1
- elsize
;
1731 msa
.elsize
= elsize
;
1734 msa
.fastcopy
= fastcopy
;
1738 for (run
= INS_SORT_INT
; run
< nel
; run
*= 2) {
1739 for (lo
= 0; lo
< nel
; lo
+= 2 * run
) {
1742 memcpy((char *)vec2
+ lo
* elsize
, (char *)vec1
+ lo
* elsize
,
1743 (nel
- lo
) * elsize
);
1746 if (!MergeArrays(&msa
, (char *)vec1
+ lo
* elsize
,
1747 (char *)vec2
+ lo
* elsize
, run
,
1748 hi
+ run
> nel
? nel
- hi
: run
)) {
1757 memcpy(src
, tmp
, nel
* elsize
);
1762 typedef struct CompareArgs
{
1765 jsval
*elemroot
; /* stack needed for js_Invoke */
1769 sort_compare(void *arg
, const void *a
, const void *b
, int *result
)
1771 jsval av
= *(const jsval
*)a
, bv
= *(const jsval
*)b
;
1772 CompareArgs
*ca
= (CompareArgs
*) arg
;
1773 JSContext
*cx
= ca
->context
;
1774 jsval
*invokevp
, *sp
;
1778 * array_sort deals with holes and undefs on its own and they should not
1781 JS_ASSERT(!JSVAL_IS_VOID(av
));
1782 JS_ASSERT(!JSVAL_IS_VOID(bv
));
1784 if (!JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
))
1787 invokevp
= ca
->elemroot
;
1794 if (!js_Invoke(cx
, 2, invokevp
, 0))
1797 cmp
= js_ValueToNumber(cx
, invokevp
);
1798 if (JSVAL_IS_NULL(*invokevp
))
1801 /* Clamp cmp to -1, 0, 1. */
1803 if (!JSDOUBLE_IS_NaN(cmp
) && cmp
!= 0)
1804 *result
= cmp
> 0 ? 1 : -1;
1807 * XXX else report some kind of error here? ECMA talks about 'consistent
1808 * compare functions' that don't return NaN, but is silent about what the
1809 * result should be. So we currently ignore it.
1816 sort_compare_strings(void *arg
, const void *a
, const void *b
, int *result
)
1818 jsval av
= *(const jsval
*)a
, bv
= *(const jsval
*)b
;
1820 JS_ASSERT(JSVAL_IS_STRING(av
));
1821 JS_ASSERT(JSVAL_IS_STRING(bv
));
1822 if (!JS_CHECK_OPERATION_LIMIT((JSContext
*)arg
, JSOW_JUMP
))
1825 *result
= (int) js_CompareStrings(JSVAL_TO_STRING(av
), JSVAL_TO_STRING(bv
));
1830 * The array_sort function below assumes JSVAL_NULL is zero in order to
1831 * perform initialization using memset. Other parts of SpiderMonkey likewise
1832 * "know" that JSVAL_NULL is zero; this static assertion covers all cases.
1834 JS_STATIC_ASSERT(JSVAL_NULL
== 0);
1837 array_sort(JSContext
*cx
, uintN argc
, jsval
*vp
)
1839 jsval
*argv
, fval
, *vec
, *mergesort_tmp
, v
;
1842 jsuint len
, newlen
, i
, undefs
;
1843 JSTempValueRooter tvr
;
1850 * Optimize the default compare function case if all of obj's elements
1851 * have values of type string.
1855 argv
= JS_ARGV(cx
, vp
);
1857 if (JSVAL_IS_PRIMITIVE(argv
[0])) {
1858 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1859 JSMSG_BAD_SORT_ARG
);
1862 fval
= argv
[0]; /* non-default compare function */
1867 obj
= JS_THIS_OBJECT(cx
, vp
);
1868 if (!obj
|| !js_GetLengthProperty(cx
, obj
, &len
))
1871 *vp
= OBJECT_TO_JSVAL(obj
);
1876 * We need a temporary array of 2 * len jsvals to hold the array elements
1877 * and the scratch space for merge sort. Check that its size does not
1878 * overflow size_t, which would allow for indexing beyond the end of the
1881 #if JS_BITS_PER_WORD == 32
1882 if ((size_t)len
> ~(size_t)0 / (2 * sizeof(jsval
))) {
1883 js_ReportAllocationOverflow(cx
);
1887 vec
= (jsval
*) JS_malloc(cx
, 2 * (size_t) len
* sizeof(jsval
));
1892 * Initialize vec as a root. We will clear elements of vec one by
1893 * one while increasing tvr.count when we know that the property at
1894 * the corresponding index exists and its value must be rooted.
1896 * In this way when sorting a huge mostly sparse array we will not
1897 * access the tail of vec corresponding to properties that do not
1898 * exist, allowing OS to avoiding committing RAM. See bug 330812.
1900 * After this point control must flow through label out: to exit.
1902 JS_PUSH_TEMP_ROOT(cx
, 0, vec
, &tvr
);
1905 * By ECMA 262, 15.4.4.11, a property that does not exist (which we
1906 * call a "hole") is always greater than an existing property with
1907 * value undefined and that is always greater than any other property.
1908 * Thus to sort holes and undefs we simply count them, sort the rest
1909 * of elements, append undefs after them and then make holes after
1914 all_strings
= JS_TRUE
;
1915 for (i
= 0; i
< len
; i
++) {
1916 ok
= JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
);
1920 /* Clear vec[newlen] before including it in the rooted set. */
1921 vec
[newlen
] = JSVAL_NULL
;
1922 tvr
.count
= newlen
+ 1;
1923 ok
= GetArrayElement(cx
, obj
, i
, &hole
, &vec
[newlen
]);
1930 if (JSVAL_IS_VOID(vec
[newlen
])) {
1935 /* We know JSVAL_IS_STRING yields 0 or 1, so avoid a branch via &=. */
1936 all_strings
&= JSVAL_IS_STRING(vec
[newlen
]);
1942 /* The array has only holes and undefs. */
1948 * The first newlen elements of vec are copied from the array object
1949 * (above). The remaining newlen positions are used as GC-rooted scratch
1950 * space for mergesort. We must clear the space before including it to
1951 * the root set covered by tvr.count. We assume JSVAL_NULL==0 to optimize
1952 * initialization using memset.
1954 mergesort_tmp
= vec
+ newlen
;
1955 memset(mergesort_tmp
, 0, newlen
* sizeof(jsval
));
1956 tvr
.count
= newlen
* 2;
1958 /* Here len == 2 * (newlen + undefs + number_of_holes). */
1959 if (fval
== JSVAL_NULL
) {
1961 * Sort using the default comparator converting all elements to
1965 elemsize
= sizeof(jsval
);
1968 * To avoid string conversion on each compare we do it only once
1969 * prior to sorting. But we also need the space for the original
1970 * values to recover the sorting result. To reuse
1971 * sort_compare_strings we move the original values to the odd
1972 * indexes in vec, put the string conversion results in the even
1973 * indexes and pass 2 * sizeof(jsval) as an element size to the
1974 * sorting function. In this way sort_compare_strings will only
1975 * see the string values when it casts the compare arguments as
1976 * pointers to jsval.
1978 * This requires doubling the temporary storage including the
1979 * scratch space for the merge sort. Since vec already contains
1980 * the rooted scratch space for newlen elements at the tail, we
1981 * can use it to rearrange and convert to strings first and try
1982 * realloc only when we know that we successfully converted all
1985 #if JS_BITS_PER_WORD == 32
1986 if ((size_t)newlen
> ~(size_t)0 / (4 * sizeof(jsval
))) {
1987 js_ReportAllocationOverflow(cx
);
1994 * Rearrange and string-convert the elements of the vector from
1995 * the tail here and, after sorting, move the results back
1996 * starting from the start to prevent overwrite the existing
2002 ok
= JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
);
2006 str
= js_ValueToString(cx
, v
);
2011 vec
[2 * i
] = STRING_TO_JSVAL(str
);
2015 JS_ASSERT(tvr
.u
.array
== vec
);
2016 vec
= (jsval
*) JS_realloc(cx
, vec
,
2017 4 * (size_t) newlen
* sizeof(jsval
));
2024 mergesort_tmp
= vec
+ 2 * newlen
;
2025 memset(mergesort_tmp
, 0, newlen
* 2 * sizeof(jsval
));
2026 tvr
.count
= newlen
* 4;
2027 elemsize
= 2 * sizeof(jsval
);
2029 ok
= js_MergeSort(vec
, (size_t) newlen
, elemsize
,
2030 sort_compare_strings
, cx
, mergesort_tmp
);
2035 * We want to make the following loop fast and to unroot the
2036 * cached results of toString invocations before the operation
2037 * callback has a chance to run the GC. For this reason we do
2038 * not call JS_CHECK_OPERATION_LIMIT in the loop.
2042 vec
[i
] = vec
[2 * i
+ 1];
2043 } while (++i
!= newlen
);
2050 ca
.elemroot
= js_AllocStack(cx
, 2 + 2, &mark
);
2055 ok
= js_MergeSort(vec
, (size_t) newlen
, sizeof(jsval
),
2056 sort_compare
, &ca
, mergesort_tmp
);
2057 js_FreeStack(cx
, mark
);
2063 * We no longer need to root the scratch space for the merge sort, so
2064 * unroot it now to make the job of a potential GC under InitArrayElements
2068 ok
= InitArrayElements(cx
, obj
, 0, newlen
, vec
);
2073 JS_POP_TEMP_ROOT(cx
, &tvr
);
2078 /* Set undefs that sorted after the rest of elements. */
2079 while (undefs
!= 0) {
2081 if (!JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
) ||
2082 !SetArrayElement(cx
, obj
, newlen
++, JSVAL_VOID
)) {
2087 /* Re-create any holes that sorted to the end of the array. */
2088 while (len
> newlen
) {
2089 if (!JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
) ||
2090 !DeleteArrayElement(cx
, obj
, --len
)) {
2094 *vp
= OBJECT_TO_JSVAL(obj
);
2099 * Perl-inspired push, pop, shift, unshift, and splice methods.
2102 array_push_slowly(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2104 jsuint length
, newlength
;
2106 if (!js_GetLengthProperty(cx
, obj
, &length
))
2108 newlength
= length
+ argc
;
2109 if (!InitArrayElements(cx
, obj
, length
, newlength
, argv
))
2112 /* Per ECMA-262, return the new array length. */
2113 if (!IndexToValue(cx
, newlength
, rval
))
2115 return js_SetLengthProperty(cx
, obj
, newlength
);
2119 array_push1_dense(JSContext
* cx
, JSObject
* obj
, jsval v
, jsval
*rval
)
2121 uint32 length
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
2122 if (INDEX_TOO_SPARSE(obj
, length
)) {
2123 if (!js_MakeArraySlow(cx
, obj
))
2125 return array_push_slowly(cx
, obj
, 1, &v
, rval
);
2128 if (!EnsureLength(cx
, obj
, length
+ 1))
2130 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = length
+ 1;
2132 JS_ASSERT(obj
->dslots
[length
] == JSVAL_HOLE
);
2133 obj
->fslots
[JSSLOT_ARRAY_COUNT
]++;
2134 obj
->dslots
[length
] = v
;
2135 return IndexToValue(cx
, obj
->fslots
[JSSLOT_ARRAY_LENGTH
], rval
);
2139 static jsval FASTCALL
2140 Array_p_push1(JSContext
* cx
, JSObject
* obj
, jsval v
)
2142 if (OBJ_IS_DENSE_ARRAY(cx
, obj
)
2143 ? array_push1_dense(cx
, obj
, v
, &v
)
2144 : array_push_slowly(cx
, obj
, 1, &v
, &v
)) {
2147 return JSVAL_ERROR_COOKIE
;
2152 array_push(JSContext
*cx
, uintN argc
, jsval
*vp
)
2156 /* Insist on one argument and obj of the expected class. */
2157 obj
= JS_THIS_OBJECT(cx
, vp
);
2160 if (argc
!= 1 || !OBJ_IS_DENSE_ARRAY(cx
, obj
))
2161 return array_push_slowly(cx
, obj
, argc
, vp
+ 2, vp
);
2163 return array_push1_dense(cx
, obj
, vp
[2], vp
);
2167 array_pop_slowly(JSContext
*cx
, JSObject
* obj
, jsval
*vp
)
2172 if (!js_GetLengthProperty(cx
, obj
, &index
))
2179 /* Get the to-be-deleted property's value into vp. */
2180 if (!GetArrayElement(cx
, obj
, index
, &hole
, vp
))
2182 if (!hole
&& !DeleteArrayElement(cx
, obj
, index
))
2185 return js_SetLengthProperty(cx
, obj
, index
);
2189 array_pop_dense(JSContext
*cx
, JSObject
* obj
, jsval
*vp
)
2194 index
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
2200 if (!GetArrayElement(cx
, obj
, index
, &hole
, vp
))
2202 if (!hole
&& !DeleteArrayElement(cx
, obj
, index
))
2204 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = index
;
2210 static jsval FASTCALL
2211 Array_p_pop(JSContext
* cx
, JSObject
* obj
)
2214 if (OBJ_IS_DENSE_ARRAY(cx
, obj
)
2215 ? array_pop_dense(cx
, obj
, &v
)
2216 : array_pop_slowly(cx
, obj
, &v
)) {
2219 return JSVAL_ERROR_COOKIE
;
2224 array_pop(JSContext
*cx
, uintN argc
, jsval
*vp
)
2228 obj
= JS_THIS_OBJECT(cx
, vp
);
2231 if (OBJ_IS_DENSE_ARRAY(cx
, obj
))
2232 return array_pop_dense(cx
, obj
, vp
);
2233 return array_pop_slowly(cx
, obj
, vp
);
2237 array_shift(JSContext
*cx
, uintN argc
, jsval
*vp
)
2242 JSTempValueRooter tvr
;
2244 obj
= JS_THIS_OBJECT(cx
, vp
);
2245 if (!obj
|| !js_GetLengthProperty(cx
, obj
, &length
))
2252 /* Get the to-be-deleted property's value into vp ASAP. */
2253 if (!GetArrayElement(cx
, obj
, 0, &hole
, vp
))
2256 /* Slide down the array above the first element. */
2258 JS_PUSH_SINGLE_TEMP_ROOT(cx
, JSVAL_NULL
, &tvr
);
2259 for (i
= 0; i
!= length
; i
++) {
2260 ok
= JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
) &&
2261 GetArrayElement(cx
, obj
, i
+ 1, &hole
, &tvr
.u
.value
) &&
2262 SetOrDeleteArrayElement(cx
, obj
, i
, hole
, tvr
.u
.value
);
2266 JS_POP_TEMP_ROOT(cx
, &tvr
);
2270 /* Delete the only or last element when it exist. */
2271 if (!hole
&& !DeleteArrayElement(cx
, obj
, length
))
2274 return js_SetLengthProperty(cx
, obj
, length
);
2278 array_unshift(JSContext
*cx
, uintN argc
, jsval
*vp
)
2282 jsuint length
, last
;
2284 JSTempValueRooter tvr
;
2286 obj
= JS_THIS_OBJECT(cx
, vp
);
2287 if (!obj
|| !js_GetLengthProperty(cx
, obj
, &length
))
2290 /* Slide up the array to make room for argc at the bottom. */
2291 argv
= JS_ARGV(cx
, vp
);
2295 JS_PUSH_SINGLE_TEMP_ROOT(cx
, JSVAL_NULL
, &tvr
);
2298 ok
= JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
) &&
2299 GetArrayElement(cx
, obj
, last
, &hole
, &tvr
.u
.value
) &&
2300 SetOrDeleteArrayElement(cx
, obj
, last
+ argc
, hole
,
2304 } while (last
!= 0);
2305 JS_POP_TEMP_ROOT(cx
, &tvr
);
2310 /* Copy from argv to the bottom of the array. */
2311 if (!InitArrayElements(cx
, obj
, 0, argc
, argv
))
2315 if (!js_SetLengthProperty(cx
, obj
, length
))
2319 /* Follow Perl by returning the new array length. */
2320 return IndexToValue(cx
, length
, vp
);
2324 array_splice(JSContext
*cx
, uintN argc
, jsval
*vp
)
2328 jsuint length
, begin
, end
, count
, delta
, last
;
2332 JSTempValueRooter tvr
;
2335 * Create a new array value to return. Our ECMA v2 proposal specs
2336 * that splice always returns an array value, even when given no
2337 * arguments. We think this is best because it eliminates the need
2338 * for callers to do an extra test to handle the empty splice case.
2340 obj2
= js_NewArrayObject(cx
, 0, NULL
);
2343 *vp
= OBJECT_TO_JSVAL(obj2
);
2345 /* Nothing to do if no args. Otherwise get length. */
2348 argv
= JS_ARGV(cx
, vp
);
2349 obj
= JS_THIS_OBJECT(cx
, vp
);
2350 if (!obj
|| !js_GetLengthProperty(cx
, obj
, &length
))
2353 /* Convert the first argument into a starting index. */
2354 d
= js_ValueToNumber(cx
, argv
);
2355 if (JSVAL_IS_NULL(*argv
))
2357 d
= js_DoubleToInteger(d
);
2362 } else if (d
> length
) {
2365 begin
= (jsuint
)d
; /* d has been clamped to uint32 */
2369 /* Convert the second argument from a count into a fencepost index. */
2370 delta
= length
- begin
;
2375 d
= js_ValueToNumber(cx
, argv
);
2376 if (JSVAL_IS_NULL(*argv
))
2378 d
= js_DoubleToInteger(d
);
2384 end
= begin
+ count
;
2389 MUST_FLOW_THROUGH("out");
2390 JS_PUSH_SINGLE_TEMP_ROOT(cx
, JSVAL_NULL
, &tvr
);
2392 /* If there are elements to remove, put them into the return value. */
2394 for (last
= begin
; last
< end
; last
++) {
2395 ok
= JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
) &&
2396 GetArrayElement(cx
, obj
, last
, &hole
, &tvr
.u
.value
);
2400 /* Copy tvr.u.value to new array unless it's a hole. */
2402 ok
= SetArrayElement(cx
, obj2
, last
- begin
, tvr
.u
.value
);
2408 ok
= js_SetLengthProperty(cx
, obj2
, end
- begin
);
2413 /* Find the direction (up or down) to copy and make way for argv. */
2415 delta
= (jsuint
)argc
- count
;
2417 /* (uint) end could be 0, so can't use vanilla >= test */
2418 while (last
-- > end
) {
2419 ok
= JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
) &&
2420 GetArrayElement(cx
, obj
, last
, &hole
, &tvr
.u
.value
) &&
2421 SetOrDeleteArrayElement(cx
, obj
, last
+ delta
, hole
,
2427 } else if (argc
< count
) {
2428 delta
= count
- (jsuint
)argc
;
2429 for (last
= end
; last
< length
; last
++) {
2430 ok
= JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
) &&
2431 GetArrayElement(cx
, obj
, last
, &hole
, &tvr
.u
.value
) &&
2432 SetOrDeleteArrayElement(cx
, obj
, last
- delta
, hole
,
2440 /* Copy from argv into the hole to complete the splice. */
2441 ok
= InitArrayElements(cx
, obj
, begin
, begin
+ argc
, argv
);
2445 /* Update length in case we deleted elements from the end. */
2446 ok
= js_SetLengthProperty(cx
, obj
, length
);
2449 JS_POP_TEMP_ROOT(cx
, &tvr
);
2454 * Python-esque sequence operations.
2457 array_concat(JSContext
*cx
, uintN argc
, jsval
*vp
)
2460 JSObject
*aobj
, *nobj
;
2461 jsuint length
, alength
, slot
;
2464 JSTempValueRooter tvr
;
2466 /* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
2467 argv
= JS_ARGV(cx
, vp
) - 1;
2468 JS_ASSERT(JS_THIS_OBJECT(cx
, vp
) == JSVAL_TO_OBJECT(argv
[0]));
2470 /* Create a new Array object and root it using *vp. */
2471 aobj
= JS_THIS_OBJECT(cx
, vp
);
2472 if (OBJ_IS_DENSE_ARRAY(cx
, aobj
)) {
2474 * Clone aobj but pass the minimum of its length and capacity (aka
2475 * "dense length"), to handle a = [1,2,3]; a.length = 10000 "dense"
2476 * cases efficiently. In such a case we'll pass 8 (not 3) due to the
2477 * ARRAY_GROWBY over-allocation policy, which will cause nobj to be
2478 * over-allocated to 16. But in the normal case where length is <=
2479 * capacity, nobj and aobj will have the same dense length.
2481 length
= aobj
->fslots
[JSSLOT_ARRAY_LENGTH
];
2482 jsuint capacity
= ARRAY_DENSE_LENGTH(aobj
);
2483 nobj
= js_NewArrayObject(cx
, JS_MIN(length
, capacity
), aobj
->dslots
,
2484 aobj
->fslots
[JSSLOT_ARRAY_COUNT
] !=
2488 nobj
->fslots
[JSSLOT_ARRAY_LENGTH
] = length
;
2489 *vp
= OBJECT_TO_JSVAL(nobj
);
2495 nobj
= js_NewArrayObject(cx
, 0, NULL
);
2498 *vp
= OBJECT_TO_JSVAL(nobj
);
2502 MUST_FLOW_THROUGH("out");
2503 JS_PUSH_SINGLE_TEMP_ROOT(cx
, JSVAL_NULL
, &tvr
);
2505 /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */
2506 for (i
= 0; i
<= argc
; i
++) {
2507 ok
= JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
);
2511 if (!JSVAL_IS_PRIMITIVE(v
)) {
2514 aobj
= JSVAL_TO_OBJECT(v
);
2515 wobj
= js_GetWrappedObject(cx
, aobj
);
2516 if (OBJ_IS_ARRAY(cx
, wobj
)) {
2517 ok
= OBJ_GET_PROPERTY(cx
, aobj
,
2518 ATOM_TO_JSID(cx
->runtime
->atomState
2523 alength
= ValueIsLength(cx
, &tvr
.u
.value
);
2524 ok
= !JSVAL_IS_NULL(tvr
.u
.value
);
2527 for (slot
= 0; slot
< alength
; slot
++) {
2528 ok
= JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
) &&
2529 GetArrayElement(cx
, aobj
, slot
, &hole
,
2535 * Per ECMA 262, 15.4.4.4, step 9, ignore non-existent
2539 ok
= SetArrayElement(cx
, nobj
, length
+ slot
,
2550 ok
= SetArrayElement(cx
, nobj
, length
, v
);
2556 ok
= js_SetLengthProperty(cx
, nobj
, length
);
2559 JS_POP_TEMP_ROOT(cx
, &tvr
);
2564 array_slice(JSContext
*cx
, uintN argc
, jsval
*vp
)
2567 JSObject
*nobj
, *obj
;
2568 jsuint length
, begin
, end
, slot
;
2571 JSTempValueRooter tvr
;
2573 argv
= JS_ARGV(cx
, vp
);
2575 obj
= JS_THIS_OBJECT(cx
, vp
);
2576 if (!obj
|| !js_GetLengthProperty(cx
, obj
, &length
))
2582 d
= js_ValueToNumber(cx
, &argv
[0]);
2583 if (JSVAL_IS_NULL(argv
[0]))
2585 d
= js_DoubleToInteger(d
);
2590 } else if (d
> length
) {
2596 d
= js_ValueToNumber(cx
, &argv
[1]);
2597 if (JSVAL_IS_NULL(argv
[1]))
2599 d
= js_DoubleToInteger(d
);
2604 } else if (d
> length
) {
2614 if (OBJ_IS_DENSE_ARRAY(cx
, obj
) && end
<= ARRAY_DENSE_LENGTH(obj
)) {
2615 nobj
= js_NewArrayObject(cx
, end
- begin
, obj
->dslots
+ begin
,
2616 obj
->fslots
[JSSLOT_ARRAY_COUNT
] !=
2617 obj
->fslots
[JSSLOT_ARRAY_LENGTH
]);
2620 *vp
= OBJECT_TO_JSVAL(nobj
);
2624 /* Create a new Array object and root it using *vp. */
2625 nobj
= js_NewArrayObject(cx
, 0, NULL
);
2628 *vp
= OBJECT_TO_JSVAL(nobj
);
2630 MUST_FLOW_THROUGH("out");
2631 JS_PUSH_SINGLE_TEMP_ROOT(cx
, JSVAL_NULL
, &tvr
);
2633 for (slot
= begin
; slot
< end
; slot
++) {
2634 ok
= JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
) &&
2635 GetArrayElement(cx
, obj
, slot
, &hole
, &tvr
.u
.value
);
2639 ok
= SetArrayElement(cx
, nobj
, slot
- begin
, tvr
.u
.value
);
2644 ok
= js_SetLengthProperty(cx
, nobj
, end
- begin
);
2647 JS_POP_TEMP_ROOT(cx
, &tvr
);
2651 #if JS_HAS_ARRAY_EXTRAS
2654 array_indexOfHelper(JSContext
*cx
, JSBool isLast
, uintN argc
, jsval
*vp
)
2657 jsuint length
, i
, stop
;
2662 obj
= JS_THIS_OBJECT(cx
, vp
);
2663 if (!obj
|| !js_GetLengthProperty(cx
, obj
, &length
))
2669 i
= isLast
? length
- 1 : 0;
2670 tosearch
= (argc
!= 0) ? vp
[2] : JSVAL_VOID
;
2675 start
= js_ValueToNumber(cx
, &vp
[3]);
2676 if (JSVAL_IS_NULL(vp
[3]))
2678 start
= js_DoubleToInteger(start
);
2688 } else if (start
>= length
) {
2706 if (!JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
) ||
2707 !GetArrayElement(cx
, obj
, (jsuint
)i
, &hole
, vp
)) {
2710 if (!hole
&& js_StrictlyEqual(cx
, *vp
, tosearch
))
2711 return js_NewNumberInRootedValue(cx
, i
, vp
);
2718 *vp
= INT_TO_JSVAL(-1);
2723 array_indexOf(JSContext
*cx
, uintN argc
, jsval
*vp
)
2725 return array_indexOfHelper(cx
, JS_FALSE
, argc
, vp
);
2729 array_lastIndexOf(JSContext
*cx
, uintN argc
, jsval
*vp
)
2731 return array_indexOfHelper(cx
, JS_TRUE
, argc
, vp
);
2734 /* Order is important; extras that take a predicate funarg must follow MAP. */
2735 typedef enum ArrayExtraMode
{
2745 #define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT)
2748 array_extra(JSContext
*cx
, ArrayExtraMode mode
, uintN argc
, jsval
*vp
)
2751 jsuint length
, newlen
;
2752 jsval
*argv
, *elemroot
, *invokevp
, *sp
;
2753 JSBool ok
, cond
, hole
;
2754 JSObject
*callable
, *thisp
, *newarr
;
2755 jsint start
, end
, step
, i
;
2758 obj
= JS_THIS_OBJECT(cx
, vp
);
2759 if (!obj
|| !js_GetLengthProperty(cx
, obj
, &length
))
2763 * First, get or compute our callee, so that we error out consistently
2764 * when passed a non-callable object.
2767 js_ReportMissingArg(cx
, vp
, 0);
2771 callable
= js_ValueToCallableObject(cx
, &argv
[0], JSV2F_SEARCH_STACK
);
2776 * Set our initial return condition, used for zero-length array cases
2777 * (and pre-size our map return to match our known length, for all cases).
2779 #ifdef __GNUC__ /* quell GCC overwarning */
2783 start
= 0, end
= length
, step
= 1;
2787 start
= length
- 1, end
= -1, step
= -1;
2790 if (length
== 0 && argc
== 1) {
2791 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2792 JSMSG_EMPTY_ARRAY_REDUCE
);
2799 if (!GetArrayElement(cx
, obj
, start
, &hole
, vp
))
2802 } while (hole
&& start
!= end
);
2804 if (hole
&& start
== end
) {
2805 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2806 JSMSG_EMPTY_ARRAY_REDUCE
);
2813 newlen
= (mode
== MAP
) ? length
: 0;
2814 newarr
= js_NewArrayObject(cx
, newlen
, NULL
);
2817 *vp
= OBJECT_TO_JSVAL(newarr
);
2833 if (argc
> 1 && !REDUCE_MODE(mode
)) {
2834 if (!js_ValueToObject(cx
, argv
[1], &thisp
))
2836 argv
[1] = OBJECT_TO_JSVAL(thisp
);
2842 * For all but REDUCE, we call with 3 args (value, index, array). REDUCE
2843 * requires 4 args (accum, value, index, array).
2845 argc
= 3 + REDUCE_MODE(mode
);
2846 elemroot
= js_AllocStack(cx
, 1 + 2 + argc
, &mark
);
2850 MUST_FLOW_THROUGH("out");
2852 invokevp
= elemroot
+ 1;
2854 for (i
= start
; i
!= end
; i
+= step
) {
2855 ok
= JS_CHECK_OPERATION_LIMIT(cx
, JSOW_JUMP
) &&
2856 GetArrayElement(cx
, obj
, i
, &hole
, elemroot
);
2863 * Push callable and 'this', then args. We must do this for every
2864 * iteration around the loop since js_Invoke uses spbase[0] for return
2865 * value storage, while some native functions use spbase[1] for local
2869 *sp
++ = OBJECT_TO_JSVAL(callable
);
2870 *sp
++ = OBJECT_TO_JSVAL(thisp
);
2871 if (REDUCE_MODE(mode
))
2874 *sp
++ = INT_TO_JSVAL(i
);
2875 *sp
++ = OBJECT_TO_JSVAL(obj
);
2878 ok
= js_Invoke(cx
, argc
, invokevp
, 0);
2883 cond
= js_ValueToBoolean(*invokevp
);
2884 #ifdef __GNUC__ /* quell GCC overwarning */
2897 ok
= SetArrayElement(cx
, newarr
, i
, *invokevp
);
2904 /* The filter passed *elemroot, so push it onto our result. */
2905 ok
= SetArrayElement(cx
, newarr
, newlen
++, *elemroot
);
2925 js_FreeStack(cx
, mark
);
2926 if (ok
&& mode
== FILTER
)
2927 ok
= js_SetLengthProperty(cx
, newarr
, newlen
);
2932 array_forEach(JSContext
*cx
, uintN argc
, jsval
*vp
)
2934 return array_extra(cx
, FOREACH
, argc
, vp
);
2938 array_map(JSContext
*cx
, uintN argc
, jsval
*vp
)
2940 return array_extra(cx
, MAP
, argc
, vp
);
2944 array_reduce(JSContext
*cx
, uintN argc
, jsval
*vp
)
2946 return array_extra(cx
, REDUCE
, argc
, vp
);
2950 array_reduceRight(JSContext
*cx
, uintN argc
, jsval
*vp
)
2952 return array_extra(cx
, REDUCE_RIGHT
, argc
, vp
);
2956 array_filter(JSContext
*cx
, uintN argc
, jsval
*vp
)
2958 return array_extra(cx
, FILTER
, argc
, vp
);
2962 array_some(JSContext
*cx
, uintN argc
, jsval
*vp
)
2964 return array_extra(cx
, SOME
, argc
, vp
);
2968 array_every(JSContext
*cx
, uintN argc
, jsval
*vp
)
2970 return array_extra(cx
, EVERY
, argc
, vp
);
2974 static JSPropertySpec array_props
[] = {
2975 {js_length_str
, -1, JSPROP_SHARED
| JSPROP_PERMANENT
,
2976 array_length_getter
, array_length_setter
},
2980 JS_DEFINE_TRCINFO_1(array_toString
,
2981 (2, (static, STRING_FAIL
, Array_p_toString
, CONTEXT
, THIS
, 0, 0)))
2982 JS_DEFINE_TRCINFO_1(array_join
,
2983 (3, (static, STRING_FAIL
, Array_p_join
, CONTEXT
, THIS
, STRING
, 0, 0)))
2984 JS_DEFINE_TRCINFO_1(array_push
,
2985 (3, (static, JSVAL_FAIL
, Array_p_push1
, CONTEXT
, THIS
, JSVAL
, 0, 0)))
2986 JS_DEFINE_TRCINFO_1(array_pop
,
2987 (2, (static, JSVAL_FAIL
, Array_p_pop
, CONTEXT
, THIS
, 0, 0)))
2989 static JSFunctionSpec array_methods
[] = {
2991 JS_FN(js_toSource_str
, array_toSource
, 0,0),
2993 JS_TN(js_toString_str
, array_toString
, 0,0, array_toString_trcinfo
),
2994 JS_FN(js_toLocaleString_str
,array_toLocaleString
,0,0),
2996 /* Perl-ish methods. */
2997 JS_TN("join", array_join
, 1,JSFUN_GENERIC_NATIVE
, array_join_trcinfo
),
2998 JS_FN("reverse", array_reverse
, 0,JSFUN_GENERIC_NATIVE
),
2999 JS_FN("sort", array_sort
, 1,JSFUN_GENERIC_NATIVE
),
3000 JS_TN("push", array_push
, 1,JSFUN_GENERIC_NATIVE
, array_push_trcinfo
),
3001 JS_TN("pop", array_pop
, 0,JSFUN_GENERIC_NATIVE
, array_pop_trcinfo
),
3002 JS_FN("shift", array_shift
, 0,JSFUN_GENERIC_NATIVE
),
3003 JS_FN("unshift", array_unshift
, 1,JSFUN_GENERIC_NATIVE
),
3004 JS_FN("splice", array_splice
, 2,JSFUN_GENERIC_NATIVE
),
3006 /* Pythonic sequence methods. */
3007 JS_FN("concat", array_concat
, 1,JSFUN_GENERIC_NATIVE
),
3008 JS_FN("slice", array_slice
, 2,JSFUN_GENERIC_NATIVE
),
3010 #if JS_HAS_ARRAY_EXTRAS
3011 JS_FN("indexOf", array_indexOf
, 1,JSFUN_GENERIC_NATIVE
),
3012 JS_FN("lastIndexOf", array_lastIndexOf
, 1,JSFUN_GENERIC_NATIVE
),
3013 JS_FN("forEach", array_forEach
, 1,JSFUN_GENERIC_NATIVE
),
3014 JS_FN("map", array_map
, 1,JSFUN_GENERIC_NATIVE
),
3015 JS_FN("reduce", array_reduce
, 1,JSFUN_GENERIC_NATIVE
),
3016 JS_FN("reduceRight", array_reduceRight
, 1,JSFUN_GENERIC_NATIVE
),
3017 JS_FN("filter", array_filter
, 1,JSFUN_GENERIC_NATIVE
),
3018 JS_FN("some", array_some
, 1,JSFUN_GENERIC_NATIVE
),
3019 JS_FN("every", array_every
, 1,JSFUN_GENERIC_NATIVE
),
3026 js_Array(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
3031 /* If called without new, replace obj with a new Array object. */
3032 if (!JS_IsConstructing(cx
)) {
3033 obj
= js_NewObject(cx
, &js_ArrayClass
, NULL
, NULL
, 0);
3036 *rval
= OBJECT_TO_JSVAL(obj
);
3042 } else if (argc
> 1) {
3043 length
= (jsuint
) argc
;
3045 } else if (!JSVAL_IS_NUMBER(argv
[0])) {
3049 length
= ValueIsLength(cx
, &argv
[0]);
3050 if (JSVAL_IS_NULL(argv
[0]))
3054 return InitArrayObject(cx
, obj
, length
, vector
);
3057 JS_STATIC_ASSERT(JSSLOT_PRIVATE
== JSSLOT_ARRAY_LENGTH
);
3058 JS_STATIC_ASSERT(JSSLOT_ARRAY_LENGTH
+ 1 == JSSLOT_ARRAY_COUNT
);
3063 js_FastNewArray(JSContext
* cx
, JSObject
* proto
)
3065 JS_ASSERT(OBJ_IS_ARRAY(cx
, proto
));
3067 JS_ASSERT(JS_ON_TRACE(cx
));
3068 JSObject
* obj
= (JSObject
*) js_NewGCThing(cx
, GCX_OBJECT
, sizeof(JSObject
));
3072 JSClass
* clasp
= &js_ArrayClass
;
3073 obj
->classword
= jsuword(clasp
);
3075 obj
->fslots
[JSSLOT_PROTO
] = OBJECT_TO_JSVAL(proto
);
3076 obj
->fslots
[JSSLOT_PARENT
] = proto
->fslots
[JSSLOT_PARENT
];
3078 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = 0;
3079 obj
->fslots
[JSSLOT_ARRAY_COUNT
] = 0;
3080 for (unsigned i
= JSSLOT_ARRAY_COUNT
+ 1; i
!= JS_INITIAL_NSLOTS
; ++i
)
3081 obj
->fslots
[i
] = JSVAL_VOID
;
3083 JSObjectOps
* ops
= clasp
->getObjectOps(cx
, clasp
);
3084 obj
->map
= ops
->newObjectMap(cx
, 1, ops
, clasp
, obj
);
3092 js_FastNewArrayWithLength(JSContext
* cx
, JSObject
* proto
, uint32 i
)
3094 JS_ASSERT(JS_ON_TRACE(cx
));
3095 JSObject
* obj
= js_FastNewArray(cx
, proto
);
3097 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = i
;
3102 js_NewUninitializedArray(JSContext
* cx
, JSObject
* proto
, uint32 len
)
3104 JSObject
*obj
= js_FastNewArrayWithLength(cx
, proto
, len
);
3105 if (!obj
|| !ResizeSlots(cx
, obj
, 0, JS_MAX(len
, ARRAY_GROWBY
)))
3110 #define ARRAY_CTOR_GUTS(exact_len, newslots_code) \
3111 JS_ASSERT(JS_ON_TRACE(cx)); \
3112 JSObject* obj = js_FastNewArray(cx, proto); \
3114 const uint32 len = ARRAY_GROWBY; \
3115 jsval* newslots = (jsval*) JS_malloc(cx, sizeof (jsval) * (len + 1)); \
3117 obj->dslots = newslots + 1; \
3118 ARRAY_SET_DENSE_LENGTH(obj, len); \
3120 while (++newslots < obj->dslots + len) \
3121 *newslots = JSVAL_HOLE; \
3122 obj->fslots[JSSLOT_ARRAY_LENGTH] = (exact_len); \
3129 js_Array_1str(JSContext
* cx
, JSObject
* proto
, JSString
*str
)
3131 ARRAY_CTOR_GUTS(1, *++newslots
= STRING_TO_JSVAL(str
);)
3134 #endif /* JS_TRACER */
3137 js_InitArrayClass(JSContext
*cx
, JSObject
*obj
)
3141 /* Initialize the ops structure used by slow arrays */
3142 memcpy(&js_SlowArrayObjectOps
, &js_ObjectOps
, sizeof(JSObjectOps
));
3143 js_SlowArrayObjectOps
.trace
= slowarray_trace
;
3144 js_SlowArrayObjectOps
.enumerate
= slowarray_enumerate
;
3145 js_SlowArrayObjectOps
.call
= NULL
;
3147 proto
= JS_InitClass(cx
, obj
, NULL
, &js_ArrayClass
, js_Array
, 1,
3148 array_props
, array_methods
, NULL
, NULL
);
3150 /* Initialize the Array prototype object so it gets a length property. */
3151 if (!proto
|| !InitArrayObject(cx
, proto
, 0, NULL
))
3157 js_NewArrayObject(JSContext
*cx
, jsuint length
, jsval
*vector
, JSBool holey
)
3159 JSTempValueRooter tvr
;
3162 obj
= js_NewObject(cx
, &js_ArrayClass
, NULL
, NULL
, 0);
3166 JS_PUSH_TEMP_ROOT_OBJECT(cx
, obj
, &tvr
);
3167 if (!InitArrayObject(cx
, obj
, length
, vector
, holey
))
3169 JS_POP_TEMP_ROOT(cx
, &tvr
);
3171 /* Set/clear newborn root, in case we lost it. */
3172 cx
->weakRoots
.newborn
[GCX_OBJECT
] = obj
;
3177 js_NewSlowArrayObject(JSContext
*cx
)
3179 JSObject
*obj
= js_NewObject(cx
, &js_SlowArrayClass
, NULL
, NULL
, 0);
3181 obj
->fslots
[JSSLOT_ARRAY_LENGTH
] = 0;
3187 js_ArrayInfo(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
3192 for (i
= 0; i
< argc
; i
++) {
3195 bytes
= js_DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, argv
[i
],
3199 if (JSVAL_IS_PRIMITIVE(argv
[i
]) ||
3200 !OBJ_IS_ARRAY(cx
, (array
= JSVAL_TO_OBJECT(argv
[i
])))) {
3201 fprintf(stderr
, "%s: not array\n", bytes
);
3205 fprintf(stderr
, "%s: %s (len %lu", bytes
,
3206 OBJ_IS_DENSE_ARRAY(cx
, array
) ? "dense" : "sparse",
3207 array
->fslots
[JSSLOT_ARRAY_LENGTH
]);
3208 if (OBJ_IS_DENSE_ARRAY(cx
, array
)) {
3209 fprintf(stderr
, ", count %lu, denselen %lu",
3210 array
->fslots
[JSSLOT_ARRAY_COUNT
],
3211 ARRAY_DENSE_LENGTH(array
));
3213 fputs(")\n", stderr
);
3220 JS_FRIEND_API(JSBool
)
3221 js_ArrayToJSUint8Buffer(JSContext
*cx
, JSObject
*obj
, jsuint offset
, jsuint count
,
3226 if (!obj
|| !OBJ_IS_DENSE_ARRAY(cx
, obj
))
3229 length
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
3230 if (length
< offset
+ count
)
3237 for (uintN i
= offset
; i
< offset
+count
; i
++) {
3239 if (!JSVAL_IS_INT(v
) || (vi
= JSVAL_TO_INT(v
)) < 0)
3242 *dp
++ = (JSUint8
) vi
;
3248 JS_FRIEND_API(JSBool
)
3249 js_ArrayToJSUint16Buffer(JSContext
*cx
, JSObject
*obj
, jsuint offset
, jsuint count
,
3254 if (!obj
|| !OBJ_IS_DENSE_ARRAY(cx
, obj
))
3257 length
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
3258 if (length
< offset
+ count
)
3264 JSUint16
*dp
= dest
;
3265 for (uintN i
= offset
; i
< offset
+count
; i
++) {
3267 if (!JSVAL_IS_INT(v
) || (vi
= JSVAL_TO_INT(v
)) < 0)
3270 *dp
++ = (JSUint16
) vi
;
3276 JS_FRIEND_API(JSBool
)
3277 js_ArrayToJSUint32Buffer(JSContext
*cx
, JSObject
*obj
, jsuint offset
, jsuint count
,
3282 if (!obj
|| !OBJ_IS_DENSE_ARRAY(cx
, obj
))
3285 length
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
3286 if (length
< offset
+ count
)
3292 JSUint32
*dp
= dest
;
3293 for (uintN i
= offset
; i
< offset
+count
; i
++) {
3295 if (!JSVAL_IS_INT(v
) || (vi
= JSVAL_TO_INT(v
)) < 0)
3298 *dp
++ = (JSUint32
) vi
;
3304 JS_FRIEND_API(JSBool
)
3305 js_ArrayToJSInt8Buffer(JSContext
*cx
, JSObject
*obj
, jsuint offset
, jsuint count
,
3310 if (!obj
|| !OBJ_IS_DENSE_ARRAY(cx
, obj
))
3313 length
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
3314 if (length
< offset
+ count
)
3319 for (uintN i
= offset
; i
< offset
+count
; i
++) {
3321 if (!JSVAL_IS_INT(v
))
3324 *dp
++ = (JSInt8
) JSVAL_TO_INT(v
);
3330 JS_FRIEND_API(JSBool
)
3331 js_ArrayToJSInt16Buffer(JSContext
*cx
, JSObject
*obj
, jsuint offset
, jsuint count
,
3336 if (!obj
|| !OBJ_IS_DENSE_ARRAY(cx
, obj
))
3339 length
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
3340 if (length
< offset
+ count
)
3345 for (uintN i
= offset
; i
< offset
+count
; i
++) {
3347 if (!JSVAL_IS_INT(v
))
3350 *dp
++ = (JSInt16
) JSVAL_TO_INT(v
);
3356 JS_FRIEND_API(JSBool
)
3357 js_ArrayToJSInt32Buffer(JSContext
*cx
, JSObject
*obj
, jsuint offset
, jsuint count
,
3362 if (!obj
|| !OBJ_IS_DENSE_ARRAY(cx
, obj
))
3365 length
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
3366 if (length
< offset
+ count
)
3371 for (uintN i
= offset
; i
< offset
+count
; i
++) {
3373 if (!JSVAL_IS_INT(v
))
3376 *dp
++ = (JSInt32
) JSVAL_TO_INT(v
);
3382 JS_FRIEND_API(JSBool
)
3383 js_ArrayToJSDoubleBuffer(JSContext
*cx
, JSObject
*obj
, jsuint offset
, jsuint count
,
3388 if (!obj
|| !OBJ_IS_DENSE_ARRAY(cx
, obj
))
3391 length
= obj
->fslots
[JSSLOT_ARRAY_LENGTH
];
3392 if (length
< offset
+ count
)
3396 jsdouble
*dp
= dest
;
3397 for (uintN i
= offset
; i
< offset
+count
; i
++) {
3399 if (JSVAL_IS_INT(v
))
3400 *dp
++ = (jsdouble
) JSVAL_TO_INT(v
);
3401 else if (JSVAL_IS_DOUBLE(v
))
3402 *dp
++ = *(JSVAL_TO_DOUBLE(v
));
3410 JS_DEFINE_CALLINFO_4(extern, BOOL
, js_Array_dense_setelem
, CONTEXT
, OBJECT
, INT32
, JSVAL
, 0, 0)
3411 JS_DEFINE_CALLINFO_2(extern, OBJECT
, js_FastNewArray
, CONTEXT
, OBJECT
, 0, 0)
3412 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_NewUninitializedArray
, CONTEXT
, OBJECT
, UINT32
, 0, 0)
3413 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_FastNewArrayWithLength
, CONTEXT
, OBJECT
, UINT32
, 0, 0)
3414 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_Array_1str
, CONTEXT
, OBJECT
, STRING
, 0, 0)