Bug 469739 - Add support for displaying Vista UAC shield icon; r=joe sr=vladimir
[wine-gecko.git] / js / src / jsarray.cpp
blobbc8915e03736c12178fb1515f1b5c6f2ebf2baa6
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
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
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.
25 * Contributor(s):
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 ***** */
42 * JS array class.
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
53 * dslots is non-NULL.
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
60 * are met:
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.
78 #include "jsstddef.h"
79 #include <stdlib.h>
80 #include <string.h>
81 #include "jstypes.h"
82 #include "jsutil.h" /* Added by JSIFY */
83 #include "jsapi.h"
84 #include "jsarray.h"
85 #include "jsatom.h"
86 #include "jsbit.h"
87 #include "jsbool.h"
88 #include "jsbuiltins.h"
89 #include "jscntxt.h"
90 #include "jsversion.h"
91 #include "jsdbgapi.h" /* for js_TraceWatchPoints */
92 #include "jsdtoa.h"
93 #include "jsfun.h"
94 #include "jsgc.h"
95 #include "jsinterp.h"
96 #include "jslock.h"
97 #include "jsnum.h"
98 #include "jsobj.h"
99 #include "jsscope.h"
100 #include "jsstr.h"
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
129 * to 2^32-1."
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.
137 JSBool
138 js_IdIsIndex(jsval id, jsuint *indexp)
140 JSString *str;
141 jschar *cp;
143 if (JSVAL_IS_INT(id)) {
144 jsint i;
145 i = JSVAL_TO_INT(id);
146 if (i < 0)
147 return JS_FALSE;
148 *indexp = (jsuint)i;
149 return JS_TRUE;
152 /* NB: id should be a string, but jsxml.c may call us with an object id. */
153 if (!JSVAL_IS_STRING(id))
154 return JS_FALSE;
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++);
160 jsuint oldIndex = 0;
161 jsuint c = 0;
162 if (index != 0) {
163 while (JS7_ISDEC(*cp)) {
164 oldIndex = index;
165 c = JS7_UNDEC(*cp);
166 index = 10*index + c;
167 cp++;
171 /* Ensure that all characters were consumed and we didn't overflow. */
172 if (*cp == 0 &&
173 (oldIndex < (MAXINDEX / 10) ||
174 (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10))))
176 *indexp = index;
177 return JS_TRUE;
180 return JS_FALSE;
183 static jsuint
184 ValueIsLength(JSContext *cx, jsval* vp)
186 jsint i;
187 jsdouble d;
188 jsuint length;
190 if (JSVAL_IS_INT(*vp)) {
191 i = JSVAL_TO_INT(*vp);
192 if (i < 0)
193 goto error;
194 return (jsuint) i;
197 d = js_ValueToNumber(cx, vp);
198 if (JSVAL_IS_NULL(*vp))
199 goto error;
201 if (JSDOUBLE_IS_NaN(d))
202 goto error;
203 length = (jsuint) d;
204 if (d != (jsdouble) length)
205 goto error;
206 return length;
208 error:
209 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
210 JSMSG_BAD_ARRAY_LENGTH);
211 *vp = JSVAL_NULL;
212 return 0;
215 JSBool
216 js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
218 JSTempValueRooter tvr;
219 jsid id;
220 JSBool ok;
221 jsint i;
223 if (OBJ_IS_ARRAY(cx, obj)) {
224 *lengthp = obj->fslots[JSSLOT_ARRAY_LENGTH];
225 return JS_TRUE;
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);
231 if (ok) {
232 if (JSVAL_IS_INT(tvr.u.value)) {
233 i = JSVAL_TO_INT(tvr.u.value);
234 *lengthp = (jsuint)i; /* jsuint cast does ToUint32 */
235 } else {
236 *lengthp = js_ValueToECMAUint32(cx, &tvr.u.value);
237 ok = !JSVAL_IS_NULL(tvr.u.value);
240 JS_POP_TEMP_ROOT(cx, &tvr);
241 return ok;
244 static JSBool
245 IndexToValue(JSContext *cx, jsuint index, jsval *vp)
247 if (index <= JSVAL_INT_MAX) {
248 *vp = INT_TO_JSVAL(index);
249 return JS_TRUE;
251 return JS_NewDoubleValue(cx, (jsdouble)index, vp);
254 JSBool JS_FASTCALL
255 js_IndexToId(JSContext *cx, jsuint index, jsid *idp)
257 JSString *str;
259 if (index <= JSVAL_INT_MAX) {
260 *idp = INT_TO_JSID(index);
261 return JS_TRUE;
263 str = js_NumberToString(cx, index);
264 if (!str)
265 return JS_FALSE;
266 return js_ValueToStringId(cx, STRING_TO_JSVAL(str), idp);
269 static JSBool
270 BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom,
271 jsid *idp)
273 jschar buf[10], *start;
274 JSClass *clasp;
275 JSAtom *atom;
276 JS_STATIC_ASSERT((jsuint)-1 == 4294967295U);
278 JS_ASSERT(index > JSVAL_INT_MAX);
280 start = JS_ARRAY_END(buf);
281 do {
282 --start;
283 *start = (jschar)('0' + index % 10);
284 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
293 * in any case.
295 if (!createAtom &&
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);
300 if (!atom) {
301 *idp = JSVAL_VOID;
302 return JS_TRUE;
304 } else {
305 atom = js_AtomizeChars(cx, start, JS_ARRAY_END(buf) - start, 0);
306 if (!atom)
307 return JS_FALSE;
310 *idp = ATOM_TO_JSID(atom);
311 return JS_TRUE;
314 static JSBool
315 ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldlen, uint32 len)
317 jsval *slots, *newslots;
319 if (len == 0) {
320 if (obj->dslots) {
321 JS_free(cx, obj->dslots - 1);
322 obj->dslots = NULL;
324 return JS_TRUE;
327 if (len > ~(uint32)0 / sizeof(jsval)) {
328 js_ReportAllocationOverflow(cx);
329 return JS_FALSE;
332 slots = obj->dslots ? obj->dslots - 1 : NULL;
333 newslots = (jsval *) JS_realloc(cx, slots, sizeof (jsval) * (len + 1));
334 if (!newslots)
335 return JS_FALSE;
337 obj->dslots = newslots + 1;
338 ARRAY_SET_DENSE_LENGTH(obj, len);
340 for (slots = obj->dslots + oldlen; slots < obj->dslots + len; slots++)
341 *slots = JSVAL_HOLE;
343 return JS_TRUE;
346 static JSBool
347 EnsureLength(JSContext *cx, JSObject *obj, uint32 len)
349 uint32 oldlen = ARRAY_DENSE_LENGTH(obj);
351 if (len > oldlen) {
352 return ResizeSlots(cx, obj, oldlen,
353 len + ARRAY_GROWBY - (len % ARRAY_GROWBY));
355 return JS_TRUE;
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.
364 static JSBool
365 GetArrayElement(JSContext *cx, JSObject *obj, jsuint index, JSBool *hole,
366 jsval *vp)
368 jsid id;
369 JSObject *obj2;
370 JSProperty *prop;
372 if (OBJ_IS_DENSE_ARRAY(cx, obj) && index < ARRAY_DENSE_LENGTH(obj) &&
373 (*vp = obj->dslots[index]) != JSVAL_HOLE) {
374 *hole = JS_FALSE;
375 return JS_TRUE;
378 if (index <= JSVAL_INT_MAX) {
379 id = INT_TO_JSID(index);
380 } else {
381 if (!BigIndexToId(cx, obj, index, JS_FALSE, &id))
382 return JS_FALSE;
383 if (JSVAL_IS_VOID(id)) {
384 *hole = JS_TRUE;
385 *vp = JSVAL_VOID;
386 return JS_TRUE;
390 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
391 return JS_FALSE;
392 if (!prop) {
393 *hole = JS_TRUE;
394 *vp = JSVAL_VOID;
395 } else {
396 OBJ_DROP_PROPERTY(cx, obj2, prop);
397 if (!OBJ_GET_PROPERTY(cx, obj, id, vp))
398 return JS_FALSE;
399 *hole = JS_FALSE;
401 return JS_TRUE;
405 * Set the value of the property at the given index to v assuming v is rooted.
407 static JSBool
408 SetArrayElement(JSContext *cx, JSObject *obj, jsuint index, jsval v)
410 jsid id;
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))
416 return JS_FALSE;
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;
422 return JS_TRUE;
425 if (!js_MakeArraySlow(cx, obj))
426 return JS_FALSE;
429 if (index <= JSVAL_INT_MAX) {
430 id = INT_TO_JSID(index);
431 } else {
432 if (!BigIndexToId(cx, obj, index, JS_TRUE, &id))
433 return JS_FALSE;
434 JS_ASSERT(!JSVAL_IS_VOID(id));
436 return OBJ_SET_PROPERTY(cx, obj, id, &v);
439 static JSBool
440 DeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index)
442 jsid id;
443 jsval junk;
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;
451 return JS_TRUE;
454 if (index <= JSVAL_INT_MAX) {
455 id = INT_TO_JSID(index);
456 } else {
457 if (!BigIndexToId(cx, obj, index, JS_FALSE, &id))
458 return JS_FALSE;
459 if (JSVAL_IS_VOID(id))
460 return JS_TRUE;
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.
469 static JSBool
470 SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index,
471 JSBool hole, jsval v)
473 if (hole) {
474 JS_ASSERT(JSVAL_IS_VOID(v));
475 return DeleteArrayElement(cx, obj, index);
477 return SetArrayElement(cx, obj, index, v);
480 JSBool
481 js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length)
483 jsval v;
484 jsid id;
486 if (!IndexToValue(cx, length, &v))
487 return JS_FALSE;
488 id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
489 return OBJ_SET_PROPERTY(cx, obj, id, &v);
492 JSBool
493 js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
495 JSErrorReporter older;
496 JSTempValueRooter tvr;
497 jsid id;
498 JSBool ok;
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);
505 if (ok) {
506 *lengthp = ValueIsLength(cx, &tvr.u.value);
507 ok = !JSVAL_IS_NULL(tvr.u.value);
509 JS_POP_TEMP_ROOT(cx, &tvr);
510 return ok;
513 JSBool
514 js_IsArrayLike(JSContext *cx, JSObject *obj, JSBool *answerp, jsuint *lengthp)
516 JSClass *clasp;
518 clasp = OBJ_GET_CLASS(cx, obj);
519 *answerp = (clasp == &js_ArgumentsClass || clasp == &js_ArrayClass ||
520 clasp == &js_SlowArrayClass);
521 if (!*answerp) {
522 *lengthp = 0;
523 return JS_TRUE;
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.
540 static JSBool
541 array_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
543 do {
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);
547 return JS_TRUE;
550 static JSBool
551 array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
553 jsuint newlen, oldlen, gap, index;
554 jsval junk;
555 JSObject *iter;
556 JSTempValueRooter tvr;
557 JSBool ok;
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))
568 return JS_FALSE;
569 oldlen = obj->fslots[JSSLOT_ARRAY_LENGTH];
571 if (oldlen == newlen)
572 return JS_TRUE;
574 if (!IndexToValue(cx, newlen, vp))
575 return JS_FALSE;
577 if (oldlen < newlen) {
578 obj->fslots[JSSLOT_ARRAY_LENGTH] = newlen;
579 return JS_TRUE;
582 if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
583 if (ARRAY_DENSE_LENGTH(obj) && !ResizeSlots(cx, obj, oldlen, newlen))
584 return JS_FALSE;
585 } else if (oldlen - newlen < (1 << 24)) {
586 do {
587 --oldlen;
588 if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||
589 !DeleteArrayElement(cx, obj, oldlen)) {
590 return JS_FALSE;
592 } while (oldlen != newlen);
593 } else {
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
599 * bug 322135.
601 iter = JS_NewPropertyIterator(cx, obj);
602 if (!iter)
603 return JS_FALSE;
605 /* Protect iter against GC in OBJ_DELETE_PROPERTY. */
606 JS_PUSH_TEMP_ROOT_OBJECT(cx, iter, &tvr);
607 gap = oldlen - newlen;
608 for (;;) {
609 ok = (JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
610 JS_NextProperty(cx, iter, &id));
611 if (!ok)
612 break;
613 if (JSVAL_IS_VOID(id))
614 break;
615 if (js_IdIsIndex(id, &index) && index - newlen < gap) {
616 ok = OBJ_DELETE_PROPERTY(cx, obj, id, &junk);
617 if (!ok)
618 break;
621 JS_POP_TEMP_ROOT(cx, &tvr);
622 if (!ok)
623 return JS_FALSE;
626 obj->fslots[JSSLOT_ARRAY_LENGTH] = newlen;
627 return JS_TRUE;
630 static JSBool
631 array_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
632 JSProperty **propp)
634 uint32 i;
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);
652 if (!proto) {
653 *objp = NULL;
654 *propp = NULL;
655 return JS_TRUE;
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]);
667 *propp = u.p;
668 *objp = obj;
669 return JS_TRUE;
672 static void
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]));
677 #ifdef DEBUG
678 obj->fslots[JSSLOT_ARRAY_LOOKUP_HOLDER] = JSVAL_VOID;
679 #endif
682 static JSBool
683 array_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
685 uint32 i;
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);
692 return JS_TRUE;
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) {
700 JSObject *obj2;
701 JSProperty *prop;
702 JSScopeProperty *sprop;
704 JSObject *proto = STOBJ_GET_PROTO(obj);
705 if (!proto) {
706 *vp = JSVAL_VOID;
707 return JS_TRUE;
710 *vp = JSVAL_VOID;
711 if (js_LookupPropertyWithFlags(cx, proto, id, cx->resolveFlags,
712 &obj2, &prop) < 0)
713 return JS_FALSE;
715 if (prop) {
716 if (OBJ_IS_NATIVE(obj2)) {
717 sprop = (JSScopeProperty *) prop;
718 if (!js_NativeGet(cx, obj, obj2, sprop, vp))
719 return JS_FALSE;
721 OBJ_DROP_PROPERTY(cx, obj2, prop);
723 return JS_TRUE;
726 *vp = obj->dslots[i];
727 return JS_TRUE;
730 static JSBool
731 slowarray_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
733 jsuint index, length;
735 if (!js_IdIsIndex(id, &index))
736 return JS_TRUE;
737 length = obj->fslots[JSSLOT_ARRAY_LENGTH];
738 if (index >= length)
739 obj->fslots[JSSLOT_ARRAY_LENGTH] = index + 1;
740 return JS_TRUE;
743 static void
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;
761 static JSObjectOps *
762 slowarray_getObjectOps(JSContext *cx, JSClass *clasp)
764 return &js_SlowArrayObjectOps;
767 static JSBool
768 array_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
770 uint32 i;
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))
780 return JS_FALSE;
781 return js_SetProperty(cx, obj, id, vp);
784 if (!EnsureLength(cx, obj, i + 1))
785 return JS_FALSE;
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;
792 return JS_TRUE;
795 #ifdef JS_TRACER
796 JSBool FASTCALL
797 js_Array_dense_setelem(JSContext* cx, JSObject* obj, jsint i, jsval v)
799 JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
801 do {
802 jsuint length = ARRAY_DENSE_LENGTH(obj);
803 if ((jsuint)i < length) {
804 if (obj->dslots[i] == JSVAL_HOLE) {
805 if (cx->runtime->anyArrayProtoHasElement)
806 break;
807 if (i >= obj->fslots[JSSLOT_ARRAY_LENGTH])
808 obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1;
809 obj->fslots[JSSLOT_ARRAY_COUNT]++;
811 obj->dslots[i] = v;
812 return JS_TRUE;
814 } while (0);
815 return OBJ_SET_PROPERTY(cx, obj, INT_TO_JSID(i), &v);
817 #endif
819 static JSBool
820 array_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
821 JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
822 JSProperty **propp)
824 uint32 i;
825 JSBool isIndex;
827 if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
828 return JS_TRUE;
830 isIndex = js_IdIsIndex(ID_TO_VALUE(id), &i);
831 if (!isIndex || attrs != JSPROP_ENUMERATE) {
832 if (!ENSURE_SLOW_ARRAY(cx, obj))
833 return JS_FALSE;
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);
842 static JSBool
843 array_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
844 uintN *attrsp)
846 *attrsp = id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
847 ? JSPROP_PERMANENT : JSPROP_ENUMERATE;
848 return JS_TRUE;
851 static JSBool
852 array_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
853 uintN *attrsp)
855 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
856 JSMSG_CANT_SET_ARRAY_ATTRS);
857 return JS_FALSE;
860 static JSBool
861 array_deleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *rval)
863 uint32 i;
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)) {
869 *rval = JSVAL_FALSE;
870 return JS_TRUE;
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;
879 *rval = JSVAL_TRUE;
880 return JS_TRUE;
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 {
929 uint32 index;
930 uint32 length;
931 JSBool hasHoles;
934 * Variable-length bitmap representing array's holes. It must not be
935 * accessed when hasHoles is false.
937 jsbitmap holes[1];
938 } JSIndexIterState;
940 #define INDEX_ITER_TAG 3
942 JS_STATIC_ASSERT(JSVAL_INT == 1);
944 static JSBool
945 array_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
946 jsval *statep, jsid *idp)
948 uint32 length, i;
949 JSIndexIterState *ii;
951 switch (enum_op) {
952 case JSENUMERATE_INIT:
953 JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
954 length = ARRAY_DENSE_LENGTH(obj);
955 if (idp)
956 *idp = INT_TO_JSVAL(obj->fslots[JSSLOT_ARRAY_COUNT]);
957 ii = NULL;
958 for (i = 0; i != length; ++i) {
959 if (obj->dslots[i] == JSVAL_HOLE) {
960 if (!ii) {
961 ii = (JSIndexIterState *)
962 JS_malloc(cx, offsetof(JSIndexIterState, holes) +
963 JS_BITMAP_SIZE(length));
964 if (!ii)
965 return JS_FALSE;
966 ii->hasHoles = JS_TRUE;
967 memset(ii->holes, 0, JS_BITMAP_SIZE(length));
969 JS_SET_BIT(ii->holes, i);
972 if (!ii) {
973 /* Array has no holes. */
974 if (length <= PACKED_UINT_PAIR_MASK) {
975 *statep = UINT_PAIR_TO_BOOLEAN_JSVAL(0, length);
976 break;
978 ii = (JSIndexIterState *)
979 JS_malloc(cx, offsetof(JSIndexIterState, holes));
980 if (!ii)
981 return JS_FALSE;
982 ii->hasHoles = JS_FALSE;
984 ii->index = 0;
985 ii->length = length;
986 *statep = (jsval) ii | INDEX_ITER_TAG;
987 JS_ASSERT(*statep & JSVAL_INT);
988 break;
990 case JSENUMERATE_NEXT:
991 if (JSVAL_TAG(*statep) == JSVAL_BOOLEAN) {
992 BOOLEAN_JSVAL_TO_UINT_PAIR(*statep, i, length);
993 if (i != length) {
994 *idp = INT_TO_JSID(i);
995 *statep = UINT_PAIR_TO_BOOLEAN_JSVAL(i + 1, length);
996 break;
998 } else {
999 JS_ASSERT((*statep & INDEX_ITER_TAG) == INDEX_ITER_TAG);
1000 ii = (JSIndexIterState *) (*statep & ~INDEX_ITER_TAG);
1001 i = ii->index;
1002 if (i != ii->length) {
1003 /* Skip holes if any. */
1004 if (ii->hasHoles) {
1005 while (JS_TEST_BIT(ii->holes, i) && ++i != ii->length)
1006 continue;
1008 if (i != ii->length) {
1009 ii->index = i + 1;
1010 return js_IndexToId(cx, i, idp);
1014 /* FALL THROUGH */
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);
1020 JS_free(cx, ii);
1022 *statep = JSVAL_NULL;
1023 break;
1025 return JS_TRUE;
1028 static JSBool
1029 slowarray_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
1030 jsval *statep, jsid *idp)
1032 JSBool ok;
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);
1044 return ok;
1047 static void
1048 array_finalize(JSContext *cx, JSObject *obj)
1050 if (obj->dslots)
1051 JS_free(cx, obj->dslots - 1);
1052 obj->dslots = NULL;
1055 static void
1056 array_trace(JSTracer *trc, JSObject *obj)
1058 uint32 length;
1059 size_t i;
1060 jsval v;
1062 JS_ASSERT(OBJ_IS_DENSE_ARRAY(cx, obj));
1064 length = ARRAY_DENSE_LENGTH(obj);
1065 for (i = 0; i < length; i++) {
1066 v = obj->dslots[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)
1086 #ifdef DEBUG
1087 extern JSClass js_ArrayClass;
1088 extern JSObjectOps js_ArrayObjectOps;
1089 #endif
1090 JSObjectMap *map = (JSObjectMap *) JS_malloc(cx, sizeof(*map));
1091 if (!map)
1092 return NULL;
1094 map->nrefs = nrefs;
1095 JS_ASSERT(ops == &js_ArrayObjectOps);
1096 map->ops = ops;
1097 JS_ASSERT(clasp == &js_ArrayClass);
1098 map->freeslot = JSSLOT_FREE(clasp);
1100 return map;
1103 void
1104 array_destroyObjectMap(JSContext *cx, JSObjectMap *map)
1106 JS_free(cx, 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,
1117 NULL, NULL,
1118 NULL, js_HasInstance,
1119 js_SetProtoOrParent, js_SetProtoOrParent,
1120 array_trace, NULL,
1121 NULL, NULL
1124 static JSObjectOps *
1125 array_getObjectOps(JSContext *cx, JSClass *clasp)
1127 return &js_ArrayObjectOps;
1130 JSClass js_ArrayClass = {
1131 "Array",
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 = {
1141 "Array",
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.
1152 JSBool
1153 js_MakeArraySlow(JSContext *cx, JSObject *obj)
1155 JSObjectMap *map, *oldmap;
1156 uint32 i, length;
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);
1163 if (!map)
1164 return JS_FALSE;
1166 length = ARRAY_DENSE_LENGTH(obj);
1167 if (length) {
1168 map->freeslot = STOBJ_NSLOTS(obj) + JS_INITIAL_NSLOTS;
1169 obj->dslots[-1] = JS_INITIAL_NSLOTS + length;
1170 } else {
1171 map->freeslot = STOBJ_NSLOTS(obj);
1174 /* Create new properties pointing to existing values in dslots */
1175 for (i = 0; i < length; i++) {
1176 jsid id;
1177 JSScopeProperty *sprop;
1179 if (!JS_ValueToId(cx, INT_TO_JSVAL(i), &id))
1180 goto out_bad;
1182 if (obj->dslots[i] == JSVAL_HOLE) {
1183 obj->dslots[i] = JSVAL_VOID;
1184 continue;
1187 sprop = js_AddScopeProperty(cx, (JSScope *)map, id, NULL, NULL,
1188 i + JS_INITIAL_NSLOTS, JSPROP_ENUMERATE,
1189 0, 0);
1190 if (!sprop)
1191 goto out_bad;
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)
1203 : JSVAL_VOID;
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. */
1210 oldmap = obj->map;
1211 obj->map = map;
1212 array_destroyObjectMap(cx, oldmap);
1214 return JS_TRUE;
1216 out_bad:
1217 js_DestroyObjectMap(cx, map);
1218 return JS_FALSE;
1221 enum ArrayToStringOp {
1222 TO_STRING,
1223 TO_LOCALE_STRING,
1224 TO_SOURCE
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.
1232 static JSBool
1233 array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op,
1234 JSString *sep, jsval *rval)
1236 JSBool ok, hole;
1237 jsuint length, index;
1238 jschar *chars, *ochars;
1239 size_t nchars, growth, seplen, tmplen, extratail;
1240 const jschar *sepstr;
1241 JSString *str;
1242 JSHashEntry *he;
1243 JSAtom *atom;
1245 JS_CHECK_RECURSION(cx, return JS_FALSE);
1247 ok = js_GetLengthProperty(cx, obj, &length);
1248 if (!ok)
1249 return JS_FALSE;
1251 he = js_EnterSharpObject(cx, obj, NULL, &chars);
1252 if (!he)
1253 return JS_FALSE;
1254 #ifdef DEBUG
1255 growth = (size_t) -1;
1256 #endif
1258 if (op == TO_SOURCE) {
1259 if (IS_SHARP(he)) {
1260 #if JS_HAS_SHARP_VARS
1261 nchars = js_strlen(chars);
1262 #else
1263 chars[0] = '[';
1264 chars[1] = ']';
1265 chars[2] = 0;
1266 nchars = 2;
1267 #endif
1268 goto make_string;
1272 * Always allocate 2 extra chars for closing ']' and terminating 0
1273 * and then preallocate 1 + extratail to include starting '['.
1275 extratail = 2;
1276 growth = (1 + extratail) * sizeof(jschar);
1277 if (!chars) {
1278 nchars = 0;
1279 chars = (jschar *) malloc(growth);
1280 if (!chars)
1281 goto done;
1282 } else {
1283 MAKE_SHARP(he);
1284 nchars = js_strlen(chars);
1285 growth += nchars * sizeof(jschar);
1286 chars = (jschar *)realloc((ochars = chars), growth);
1287 if (!chars) {
1288 free(ochars);
1289 goto done;
1292 chars[nchars++] = '[';
1293 JS_ASSERT(sep == NULL);
1294 sepstr = NULL; /* indicates to use ", " as separator */
1295 seplen = 2;
1296 } else {
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.
1303 if (chars)
1304 JS_free(cx, chars);
1305 chars = NULL;
1306 nchars = 0;
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);
1313 return ok;
1316 /* Flag he as BUSY so we can distinguish a cycle from a join-point. */
1317 MAKE_BUSY(he);
1319 if (sep) {
1320 JSSTRING_CHARS_AND_LENGTH(sep, sepstr, seplen);
1321 } else {
1322 sepstr = NULL; /* indicates to use "," as separator */
1323 seplen = 1;
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));
1331 if (!ok)
1332 goto done;
1333 if (hole ||
1334 (op != TO_SOURCE &&
1335 (JSVAL_IS_VOID(*rval) || JSVAL_IS_NULL(*rval)))) {
1336 str = cx->runtime->emptyString;
1337 } else {
1338 if (op == TO_LOCALE_STRING) {
1339 JSObject *robj;
1341 atom = cx->runtime->atomState.toLocaleStringAtom;
1342 ok = js_ValueToObject(cx, *rval, &robj);
1343 if (ok) {
1344 /* Re-use *rval to protect robj temporarily. */
1345 *rval = OBJECT_TO_JSVAL(robj);
1346 ok = js_TryMethod(cx, robj, atom, 0, NULL, rval);
1348 if (!ok)
1349 goto done;
1350 str = js_ValueToString(cx, *rval);
1351 } else if (op == TO_STRING) {
1352 str = js_ValueToString(cx, *rval);
1353 } else {
1354 JS_ASSERT(op == TO_SOURCE);
1355 str = js_ValueToSource(cx, *rval);
1357 if (!str) {
1358 ok = JS_FALSE;
1359 goto done;
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)) {
1375 if (chars) {
1376 free(chars);
1377 chars = NULL;
1379 goto done;
1381 growth *= sizeof(jschar);
1382 JS_COUNT_OPERATION(cx, JSOW_ALLOCATION);
1383 if (!chars) {
1384 chars = (jschar *) malloc(growth);
1385 if (!chars)
1386 goto done;
1387 } else {
1388 chars = (jschar *) realloc((ochars = chars), growth);
1389 if (!chars) {
1390 free(ochars);
1391 goto done;
1395 js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen);
1396 nchars += tmplen;
1398 if (seplen) {
1399 if (sepstr) {
1400 js_strncpy(&chars[nchars], sepstr, seplen);
1401 } else {
1402 JS_ASSERT(seplen == 1 || seplen == 2);
1403 chars[nchars] = ',';
1404 if (seplen == 2)
1405 chars[nchars + 1] = ' ';
1407 nchars += seplen;
1411 done:
1412 if (op == TO_SOURCE) {
1413 if (chars)
1414 chars[nchars++] = ']';
1415 } else {
1416 CLEAR_BUSY(he);
1418 js_LeaveSharpObject(cx, NULL);
1419 if (!ok) {
1420 if (chars)
1421 free(chars);
1422 return ok;
1425 make_string:
1426 if (!chars) {
1427 JS_ReportOutOfMemory(cx);
1428 return JS_FALSE;
1430 chars[nchars] = 0;
1431 JS_ASSERT(growth == (size_t)-1 || (nchars + 1) * sizeof(jschar) == growth);
1432 str = js_NewString(cx, chars, nchars);
1433 if (!str) {
1434 free(chars);
1435 return JS_FALSE;
1437 *rval = STRING_TO_JSVAL(str);
1438 return JS_TRUE;
1441 #if JS_HAS_TOSOURCE
1442 static JSBool
1443 array_toSource(JSContext *cx, uintN argc, jsval *vp)
1445 JSObject *obj;
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)) {
1450 return JS_FALSE;
1452 return array_join_sub(cx, obj, TO_SOURCE, NULL, vp);
1454 #endif
1456 static JSBool
1457 array_toString(JSContext *cx, uintN argc, jsval *vp)
1459 JSObject *obj;
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)) {
1464 return JS_FALSE;
1466 return array_join_sub(cx, obj, TO_STRING, NULL, vp);
1469 static JSBool
1470 array_toLocaleString(JSContext *cx, uintN argc, jsval *vp)
1472 JSObject *obj;
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)) {
1477 return JS_FALSE;
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);
1487 static JSBool
1488 InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint end,
1489 jsval *vector)
1491 if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
1492 if (!EnsureLength(cx, obj, end))
1493 return JS_FALSE;
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));
1499 return JS_TRUE;
1502 while (start != end) {
1503 if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||
1504 !SetArrayElement(cx, obj, start++, *vector++)) {
1505 return JS_FALSE;
1508 return JS_TRUE;
1511 static JSBool
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;
1519 if (vector) {
1520 if (!EnsureLength(cx, obj, length))
1521 return JS_FALSE;
1523 jsuint count = length;
1524 if (!holey) {
1525 memcpy(obj->dslots, vector, length * sizeof (jsval));
1526 } else {
1527 for (jsuint i = 0; i < length; i++) {
1528 if (vector[i] == JSVAL_HOLE)
1529 --count;
1530 obj->dslots[i] = vector[i];
1533 obj->fslots[JSSLOT_ARRAY_COUNT] = count;
1534 } else {
1535 obj->fslots[JSSLOT_ARRAY_COUNT] = 0;
1537 return JS_TRUE;
1540 #ifdef JS_TRACER
1541 static JSString* FASTCALL
1542 Array_p_join(JSContext* cx, JSObject* obj, JSString *str)
1544 jsval v;
1545 if (!array_join_sub(cx, obj, TO_STRING, str, &v))
1546 return NULL;
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)
1554 jsval v;
1555 if (!array_join_sub(cx, obj, TO_STRING, NULL, &v))
1556 return NULL;
1557 JS_ASSERT(JSVAL_IS_STRING(v));
1558 return JSVAL_TO_STRING(v);
1560 #endif
1563 * Perl-inspired join, reverse, and sort.
1565 static JSBool
1566 array_join(JSContext *cx, uintN argc, jsval *vp)
1568 JSString *str;
1569 JSObject *obj;
1571 if (argc == 0 || JSVAL_IS_VOID(vp[2])) {
1572 str = NULL;
1573 } else {
1574 str = js_ValueToString(cx, vp[2]);
1575 if (!str)
1576 return JS_FALSE;
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);
1583 static JSBool
1584 array_reverse(JSContext *cx, uintN argc, jsval *vp)
1586 JSObject *obj;
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))
1593 return JS_FALSE;
1595 ok = JS_TRUE;
1596 JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
1597 half = len / 2;
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);
1604 if (!ok)
1605 break;
1607 JS_POP_TEMP_ROOT(cx, &tvr);
1609 *vp = OBJECT_TO_JSVAL(obj);
1610 return ok;
1613 typedef struct MSortArgs {
1614 size_t elsize;
1615 JSComparator cmp;
1616 void *arg;
1617 JSBool fastcopy;
1618 } MSortArgs;
1620 /* Helper function for js_MergeSort. */
1621 static JSBool
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;
1626 int cmp_result;
1627 JSComparator cmp;
1628 JSBool fastcopy;
1630 runtotal = run1 + run2;
1632 elsize = msa->elsize;
1633 cmp = msa->cmp;
1634 arg = msa->arg;
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;
1643 CALL_CMP(a, b);
1644 if (cmp_result <= 0) {
1645 memcpy(dest, src, runtotal * elsize);
1646 return JS_TRUE;
1649 #define COPY_ONE(p,q,n) \
1650 (fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n))
1652 a = src;
1653 c = dest;
1654 for (; runtotal != 0; runtotal--) {
1655 JSBool from_a = run2 == 0;
1656 if (!from_a && run1 != 0) {
1657 CALL_CMP(a,b);
1658 from_a = cmp_result <= 0;
1661 if (from_a) {
1662 COPY_ONE(c, a, elsize);
1663 run1--;
1664 a = (char *)a + elsize;
1665 } else {
1666 COPY_ONE(c, b, elsize);
1667 run2--;
1668 b = (char *)b + elsize;
1670 c = (char *)c + elsize;
1672 #undef COPY_ONE
1673 #undef CALL_CMP
1675 return JS_TRUE;
1679 * This sort is stable, i.e. sequence of equal elements is preserved.
1680 * See also bug #224128.
1682 JSBool
1683 js_MergeSort(void *src, size_t nel, size_t elsize,
1684 JSComparator cmp, void *arg, void *tmp)
1686 void *swap, *vec1, *vec2;
1687 MSortArgs msa;
1688 size_t i, j, lo, hi, run;
1689 JSBool fastcopy;
1690 int cmp_result;
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
1703 * passes needed.
1705 for (lo = 0; lo < nel; lo += INS_SORT_INT) {
1706 hi = lo + INS_SORT_INT;
1707 if (hi >= nel)
1708 hi = nel;
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) {
1716 break;
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);
1723 vec1 = vec2;
1724 vec2 = (char *)vec1 - elsize;
1728 #undef CALL_CMP
1729 #undef COPY_ONE
1731 msa.elsize = elsize;
1732 msa.cmp = cmp;
1733 msa.arg = arg;
1734 msa.fastcopy = fastcopy;
1736 vec1 = src;
1737 vec2 = tmp;
1738 for (run = INS_SORT_INT; run < nel; run *= 2) {
1739 for (lo = 0; lo < nel; lo += 2 * run) {
1740 hi = lo + run;
1741 if (hi >= nel) {
1742 memcpy((char *)vec2 + lo * elsize, (char *)vec1 + lo * elsize,
1743 (nel - lo) * elsize);
1744 break;
1746 if (!MergeArrays(&msa, (char *)vec1 + lo * elsize,
1747 (char *)vec2 + lo * elsize, run,
1748 hi + run > nel ? nel - hi : run)) {
1749 return JS_FALSE;
1752 swap = vec1;
1753 vec1 = vec2;
1754 vec2 = swap;
1756 if (src != vec1)
1757 memcpy(src, tmp, nel * elsize);
1759 return JS_TRUE;
1762 typedef struct CompareArgs {
1763 JSContext *context;
1764 jsval fval;
1765 jsval *elemroot; /* stack needed for js_Invoke */
1766 } CompareArgs;
1768 static JSBool
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;
1775 jsdouble cmp;
1778 * array_sort deals with holes and undefs on its own and they should not
1779 * come here.
1781 JS_ASSERT(!JSVAL_IS_VOID(av));
1782 JS_ASSERT(!JSVAL_IS_VOID(bv));
1784 if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP))
1785 return JS_FALSE;
1787 invokevp = ca->elemroot;
1788 sp = invokevp;
1789 *sp++ = ca->fval;
1790 *sp++ = JSVAL_NULL;
1791 *sp++ = av;
1792 *sp++ = bv;
1794 if (!js_Invoke(cx, 2, invokevp, 0))
1795 return JS_FALSE;
1797 cmp = js_ValueToNumber(cx, invokevp);
1798 if (JSVAL_IS_NULL(*invokevp))
1799 return JS_FALSE;
1801 /* Clamp cmp to -1, 0, 1. */
1802 *result = 0;
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.
1812 return JS_TRUE;
1815 static int
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))
1823 return JS_FALSE;
1825 *result = (int) js_CompareStrings(JSVAL_TO_STRING(av), JSVAL_TO_STRING(bv));
1826 return JS_TRUE;
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);
1836 static JSBool
1837 array_sort(JSContext *cx, uintN argc, jsval *vp)
1839 jsval *argv, fval, *vec, *mergesort_tmp, v;
1840 JSObject *obj;
1841 CompareArgs ca;
1842 jsuint len, newlen, i, undefs;
1843 JSTempValueRooter tvr;
1844 JSBool hole;
1845 bool ok;
1846 size_t elemsize;
1847 JSString *str;
1850 * Optimize the default compare function case if all of obj's elements
1851 * have values of type string.
1853 JSBool all_strings;
1855 argv = JS_ARGV(cx, vp);
1856 if (argc > 0) {
1857 if (JSVAL_IS_PRIMITIVE(argv[0])) {
1858 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1859 JSMSG_BAD_SORT_ARG);
1860 return JS_FALSE;
1862 fval = argv[0]; /* non-default compare function */
1863 } else {
1864 fval = JSVAL_NULL;
1867 obj = JS_THIS_OBJECT(cx, vp);
1868 if (!obj || !js_GetLengthProperty(cx, obj, &len))
1869 return JS_FALSE;
1870 if (len == 0) {
1871 *vp = OBJECT_TO_JSVAL(obj);
1872 return JS_TRUE;
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
1879 * malloc'd vector.
1881 #if JS_BITS_PER_WORD == 32
1882 if ((size_t)len > ~(size_t)0 / (2 * sizeof(jsval))) {
1883 js_ReportAllocationOverflow(cx);
1884 return JS_FALSE;
1886 #endif
1887 vec = (jsval *) JS_malloc(cx, 2 * (size_t) len * sizeof(jsval));
1888 if (!vec)
1889 return JS_FALSE;
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
1910 * undefs.
1912 undefs = 0;
1913 newlen = 0;
1914 all_strings = JS_TRUE;
1915 for (i = 0; i < len; i++) {
1916 ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP);
1917 if (!ok)
1918 goto out;
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]);
1924 if (!ok)
1925 goto out;
1927 if (hole)
1928 continue;
1930 if (JSVAL_IS_VOID(vec[newlen])) {
1931 ++undefs;
1932 continue;
1935 /* We know JSVAL_IS_STRING yields 0 or 1, so avoid a branch via &=. */
1936 all_strings &= JSVAL_IS_STRING(vec[newlen]);
1938 ++newlen;
1941 if (newlen == 0) {
1942 /* The array has only holes and undefs. */
1943 ok = JS_TRUE;
1944 goto out;
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
1962 * strings.
1964 if (all_strings) {
1965 elemsize = sizeof(jsval);
1966 } else {
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
1983 * the elements.
1985 #if JS_BITS_PER_WORD == 32
1986 if ((size_t)newlen > ~(size_t)0 / (4 * sizeof(jsval))) {
1987 js_ReportAllocationOverflow(cx);
1988 ok = JS_FALSE;
1989 goto out;
1991 #endif
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
1997 * elements.
1999 i = newlen;
2000 do {
2001 --i;
2002 ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP);
2003 if (!ok)
2004 goto out;
2005 v = vec[i];
2006 str = js_ValueToString(cx, v);
2007 if (!str) {
2008 ok = JS_FALSE;
2009 goto out;
2011 vec[2 * i] = STRING_TO_JSVAL(str);
2012 vec[2 * i + 1] = v;
2013 } while (i != 0);
2015 JS_ASSERT(tvr.u.array == vec);
2016 vec = (jsval *) JS_realloc(cx, vec,
2017 4 * (size_t) newlen * sizeof(jsval));
2018 if (!vec) {
2019 vec = tvr.u.array;
2020 ok = JS_FALSE;
2021 goto out;
2023 tvr.u.array = vec;
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);
2031 if (!ok)
2032 goto out;
2033 if (!all_strings) {
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.
2040 i = 0;
2041 do {
2042 vec[i] = vec[2 * i + 1];
2043 } while (++i != newlen);
2045 } else {
2046 void *mark;
2048 ca.context = cx;
2049 ca.fval = fval;
2050 ca.elemroot = js_AllocStack(cx, 2 + 2, &mark);
2051 if (!ca.elemroot) {
2052 ok = JS_FALSE;
2053 goto out;
2055 ok = js_MergeSort(vec, (size_t) newlen, sizeof(jsval),
2056 sort_compare, &ca, mergesort_tmp);
2057 js_FreeStack(cx, mark);
2058 if (!ok)
2059 goto out;
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
2065 * easier.
2067 tvr.count = newlen;
2068 ok = InitArrayElements(cx, obj, 0, newlen, vec);
2069 if (!ok)
2070 goto out;
2072 out:
2073 JS_POP_TEMP_ROOT(cx, &tvr);
2074 JS_free(cx, vec);
2075 if (!ok)
2076 return JS_FALSE;
2078 /* Set undefs that sorted after the rest of elements. */
2079 while (undefs != 0) {
2080 --undefs;
2081 if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||
2082 !SetArrayElement(cx, obj, newlen++, JSVAL_VOID)) {
2083 return JS_FALSE;
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)) {
2091 return JS_FALSE;
2094 *vp = OBJECT_TO_JSVAL(obj);
2095 return JS_TRUE;
2099 * Perl-inspired push, pop, shift, unshift, and splice methods.
2101 static JSBool
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))
2107 return JS_FALSE;
2108 newlength = length + argc;
2109 if (!InitArrayElements(cx, obj, length, newlength, argv))
2110 return JS_FALSE;
2112 /* Per ECMA-262, return the new array length. */
2113 if (!IndexToValue(cx, newlength, rval))
2114 return JS_FALSE;
2115 return js_SetLengthProperty(cx, obj, newlength);
2118 static JSBool
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))
2124 return JS_FALSE;
2125 return array_push_slowly(cx, obj, 1, &v, rval);
2128 if (!EnsureLength(cx, obj, length + 1))
2129 return JS_FALSE;
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);
2138 #ifdef JS_TRACER
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)) {
2145 return v;
2147 return JSVAL_ERROR_COOKIE;
2149 #endif
2151 static JSBool
2152 array_push(JSContext *cx, uintN argc, jsval *vp)
2154 JSObject *obj;
2156 /* Insist on one argument and obj of the expected class. */
2157 obj = JS_THIS_OBJECT(cx, vp);
2158 if (!obj)
2159 return JS_FALSE;
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);
2166 static JSBool
2167 array_pop_slowly(JSContext *cx, JSObject* obj, jsval *vp)
2169 jsuint index;
2170 JSBool hole;
2172 if (!js_GetLengthProperty(cx, obj, &index))
2173 return JS_FALSE;
2174 if (index == 0) {
2175 *vp = JSVAL_VOID;
2176 } else {
2177 index--;
2179 /* Get the to-be-deleted property's value into vp. */
2180 if (!GetArrayElement(cx, obj, index, &hole, vp))
2181 return JS_FALSE;
2182 if (!hole && !DeleteArrayElement(cx, obj, index))
2183 return JS_FALSE;
2185 return js_SetLengthProperty(cx, obj, index);
2188 static JSBool
2189 array_pop_dense(JSContext *cx, JSObject* obj, jsval *vp)
2191 jsuint index;
2192 JSBool hole;
2194 index = obj->fslots[JSSLOT_ARRAY_LENGTH];
2195 if (index == 0) {
2196 *vp = JSVAL_VOID;
2197 return JS_TRUE;
2199 index--;
2200 if (!GetArrayElement(cx, obj, index, &hole, vp))
2201 return JS_FALSE;
2202 if (!hole && !DeleteArrayElement(cx, obj, index))
2203 return JS_FALSE;
2204 obj->fslots[JSSLOT_ARRAY_LENGTH] = index;
2205 return JS_TRUE;
2209 #ifdef JS_TRACER
2210 static jsval FASTCALL
2211 Array_p_pop(JSContext* cx, JSObject* obj)
2213 jsval v;
2214 if (OBJ_IS_DENSE_ARRAY(cx, obj)
2215 ? array_pop_dense(cx, obj, &v)
2216 : array_pop_slowly(cx, obj, &v)) {
2217 return v;
2219 return JSVAL_ERROR_COOKIE;
2221 #endif
2223 static JSBool
2224 array_pop(JSContext *cx, uintN argc, jsval *vp)
2226 JSObject *obj;
2228 obj = JS_THIS_OBJECT(cx, vp);
2229 if (!obj)
2230 return JS_FALSE;
2231 if (OBJ_IS_DENSE_ARRAY(cx, obj))
2232 return array_pop_dense(cx, obj, vp);
2233 return array_pop_slowly(cx, obj, vp);
2236 static JSBool
2237 array_shift(JSContext *cx, uintN argc, jsval *vp)
2239 JSObject *obj;
2240 jsuint length, i;
2241 JSBool hole, ok;
2242 JSTempValueRooter tvr;
2244 obj = JS_THIS_OBJECT(cx, vp);
2245 if (!obj || !js_GetLengthProperty(cx, obj, &length))
2246 return JS_FALSE;
2247 if (length == 0) {
2248 *vp = JSVAL_VOID;
2249 } else {
2250 length--;
2252 /* Get the to-be-deleted property's value into vp ASAP. */
2253 if (!GetArrayElement(cx, obj, 0, &hole, vp))
2254 return JS_FALSE;
2256 /* Slide down the array above the first element. */
2257 ok = JS_TRUE;
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);
2263 if (!ok)
2264 break;
2266 JS_POP_TEMP_ROOT(cx, &tvr);
2267 if (!ok)
2268 return JS_FALSE;
2270 /* Delete the only or last element when it exist. */
2271 if (!hole && !DeleteArrayElement(cx, obj, length))
2272 return JS_FALSE;
2274 return js_SetLengthProperty(cx, obj, length);
2277 static JSBool
2278 array_unshift(JSContext *cx, uintN argc, jsval *vp)
2280 JSObject *obj;
2281 jsval *argv;
2282 jsuint length, last;
2283 JSBool hole, ok;
2284 JSTempValueRooter tvr;
2286 obj = JS_THIS_OBJECT(cx, vp);
2287 if (!obj || !js_GetLengthProperty(cx, obj, &length))
2288 return JS_FALSE;
2289 if (argc > 0) {
2290 /* Slide up the array to make room for argc at the bottom. */
2291 argv = JS_ARGV(cx, vp);
2292 if (length > 0) {
2293 last = length;
2294 ok = JS_TRUE;
2295 JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
2296 do {
2297 --last;
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,
2301 tvr.u.value);
2302 if (!ok)
2303 break;
2304 } while (last != 0);
2305 JS_POP_TEMP_ROOT(cx, &tvr);
2306 if (!ok)
2307 return JS_FALSE;
2310 /* Copy from argv to the bottom of the array. */
2311 if (!InitArrayElements(cx, obj, 0, argc, argv))
2312 return JS_FALSE;
2314 length += argc;
2315 if (!js_SetLengthProperty(cx, obj, length))
2316 return JS_FALSE;
2319 /* Follow Perl by returning the new array length. */
2320 return IndexToValue(cx, length, vp);
2323 static JSBool
2324 array_splice(JSContext *cx, uintN argc, jsval *vp)
2326 jsval *argv;
2327 JSObject *obj;
2328 jsuint length, begin, end, count, delta, last;
2329 jsdouble d;
2330 JSBool hole, ok;
2331 JSObject *obj2;
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);
2341 if (!obj2)
2342 return JS_FALSE;
2343 *vp = OBJECT_TO_JSVAL(obj2);
2345 /* Nothing to do if no args. Otherwise get length. */
2346 if (argc == 0)
2347 return JS_TRUE;
2348 argv = JS_ARGV(cx, vp);
2349 obj = JS_THIS_OBJECT(cx, vp);
2350 if (!obj || !js_GetLengthProperty(cx, obj, &length))
2351 return JS_FALSE;
2353 /* Convert the first argument into a starting index. */
2354 d = js_ValueToNumber(cx, argv);
2355 if (JSVAL_IS_NULL(*argv))
2356 return JS_FALSE;
2357 d = js_DoubleToInteger(d);
2358 if (d < 0) {
2359 d += length;
2360 if (d < 0)
2361 d = 0;
2362 } else if (d > length) {
2363 d = length;
2365 begin = (jsuint)d; /* d has been clamped to uint32 */
2366 argc--;
2367 argv++;
2369 /* Convert the second argument from a count into a fencepost index. */
2370 delta = length - begin;
2371 if (argc == 0) {
2372 count = delta;
2373 end = length;
2374 } else {
2375 d = js_ValueToNumber(cx, argv);
2376 if (JSVAL_IS_NULL(*argv))
2377 return JS_FALSE;
2378 d = js_DoubleToInteger(d);
2379 if (d < 0)
2380 d = 0;
2381 else if (d > delta)
2382 d = delta;
2383 count = (jsuint)d;
2384 end = begin + count;
2385 argc--;
2386 argv++;
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. */
2393 if (count > 0) {
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);
2397 if (!ok)
2398 goto out;
2400 /* Copy tvr.u.value to new array unless it's a hole. */
2401 if (!hole) {
2402 ok = SetArrayElement(cx, obj2, last - begin, tvr.u.value);
2403 if (!ok)
2404 goto out;
2408 ok = js_SetLengthProperty(cx, obj2, end - begin);
2409 if (!ok)
2410 goto out;
2413 /* Find the direction (up or down) to copy and make way for argv. */
2414 if (argc > count) {
2415 delta = (jsuint)argc - count;
2416 last = length;
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,
2422 tvr.u.value);
2423 if (!ok)
2424 goto out;
2426 length += delta;
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,
2433 tvr.u.value);
2434 if (!ok)
2435 goto out;
2437 length -= delta;
2440 /* Copy from argv into the hole to complete the splice. */
2441 ok = InitArrayElements(cx, obj, begin, begin + argc, argv);
2442 if (!ok)
2443 goto out;
2445 /* Update length in case we deleted elements from the end. */
2446 ok = js_SetLengthProperty(cx, obj, length);
2448 out:
2449 JS_POP_TEMP_ROOT(cx, &tvr);
2450 return ok;
2454 * Python-esque sequence operations.
2456 static JSBool
2457 array_concat(JSContext *cx, uintN argc, jsval *vp)
2459 jsval *argv, v;
2460 JSObject *aobj, *nobj;
2461 jsuint length, alength, slot;
2462 uintN i;
2463 JSBool hole, ok;
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] !=
2485 (jsval) length);
2486 if (!nobj)
2487 return JS_FALSE;
2488 nobj->fslots[JSSLOT_ARRAY_LENGTH] = length;
2489 *vp = OBJECT_TO_JSVAL(nobj);
2490 if (argc == 0)
2491 return JS_TRUE;
2492 argc--;
2493 argv++;
2494 } else {
2495 nobj = js_NewArrayObject(cx, 0, NULL);
2496 if (!nobj)
2497 return JS_FALSE;
2498 *vp = OBJECT_TO_JSVAL(nobj);
2499 length = 0;
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);
2508 if (!ok)
2509 goto out;
2510 v = argv[i];
2511 if (!JSVAL_IS_PRIMITIVE(v)) {
2512 JSObject *wobj;
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
2519 .lengthAtom),
2520 &tvr.u.value);
2521 if (!ok)
2522 goto out;
2523 alength = ValueIsLength(cx, &tvr.u.value);
2524 ok = !JSVAL_IS_NULL(tvr.u.value);
2525 if (!ok)
2526 goto out;
2527 for (slot = 0; slot < alength; slot++) {
2528 ok = JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) &&
2529 GetArrayElement(cx, aobj, slot, &hole,
2530 &tvr.u.value);
2531 if (!ok)
2532 goto out;
2535 * Per ECMA 262, 15.4.4.4, step 9, ignore non-existent
2536 * properties.
2538 if (!hole) {
2539 ok = SetArrayElement(cx, nobj, length + slot,
2540 tvr.u.value);
2541 if (!ok)
2542 goto out;
2545 length += alength;
2546 continue;
2550 ok = SetArrayElement(cx, nobj, length, v);
2551 if (!ok)
2552 goto out;
2553 length++;
2556 ok = js_SetLengthProperty(cx, nobj, length);
2558 out:
2559 JS_POP_TEMP_ROOT(cx, &tvr);
2560 return ok;
2563 static JSBool
2564 array_slice(JSContext *cx, uintN argc, jsval *vp)
2566 jsval *argv;
2567 JSObject *nobj, *obj;
2568 jsuint length, begin, end, slot;
2569 jsdouble d;
2570 JSBool hole, ok;
2571 JSTempValueRooter tvr;
2573 argv = JS_ARGV(cx, vp);
2575 obj = JS_THIS_OBJECT(cx, vp);
2576 if (!obj || !js_GetLengthProperty(cx, obj, &length))
2577 return JS_FALSE;
2578 begin = 0;
2579 end = length;
2581 if (argc > 0) {
2582 d = js_ValueToNumber(cx, &argv[0]);
2583 if (JSVAL_IS_NULL(argv[0]))
2584 return JS_FALSE;
2585 d = js_DoubleToInteger(d);
2586 if (d < 0) {
2587 d += length;
2588 if (d < 0)
2589 d = 0;
2590 } else if (d > length) {
2591 d = length;
2593 begin = (jsuint)d;
2595 if (argc > 1) {
2596 d = js_ValueToNumber(cx, &argv[1]);
2597 if (JSVAL_IS_NULL(argv[1]))
2598 return JS_FALSE;
2599 d = js_DoubleToInteger(d);
2600 if (d < 0) {
2601 d += length;
2602 if (d < 0)
2603 d = 0;
2604 } else if (d > length) {
2605 d = length;
2607 end = (jsuint)d;
2611 if (begin > end)
2612 begin = end;
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]);
2618 if (!nobj)
2619 return JS_FALSE;
2620 *vp = OBJECT_TO_JSVAL(nobj);
2621 return JS_TRUE;
2624 /* Create a new Array object and root it using *vp. */
2625 nobj = js_NewArrayObject(cx, 0, NULL);
2626 if (!nobj)
2627 return JS_FALSE;
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);
2636 if (!ok)
2637 goto out;
2638 if (!hole) {
2639 ok = SetArrayElement(cx, nobj, slot - begin, tvr.u.value);
2640 if (!ok)
2641 goto out;
2644 ok = js_SetLengthProperty(cx, nobj, end - begin);
2646 out:
2647 JS_POP_TEMP_ROOT(cx, &tvr);
2648 return ok;
2651 #if JS_HAS_ARRAY_EXTRAS
2653 static JSBool
2654 array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, jsval *vp)
2656 JSObject *obj;
2657 jsuint length, i, stop;
2658 jsval tosearch;
2659 jsint direction;
2660 JSBool hole;
2662 obj = JS_THIS_OBJECT(cx, vp);
2663 if (!obj || !js_GetLengthProperty(cx, obj, &length))
2664 return JS_FALSE;
2665 if (length == 0)
2666 goto not_found;
2668 if (argc <= 1) {
2669 i = isLast ? length - 1 : 0;
2670 tosearch = (argc != 0) ? vp[2] : JSVAL_VOID;
2671 } else {
2672 jsdouble start;
2674 tosearch = vp[2];
2675 start = js_ValueToNumber(cx, &vp[3]);
2676 if (JSVAL_IS_NULL(vp[3]))
2677 return JS_FALSE;
2678 start = js_DoubleToInteger(start);
2679 if (start < 0) {
2680 start += length;
2681 if (start < 0) {
2682 if (isLast)
2683 goto not_found;
2684 i = 0;
2685 } else {
2686 i = (jsuint)start;
2688 } else if (start >= length) {
2689 if (!isLast)
2690 goto not_found;
2691 i = length - 1;
2692 } else {
2693 i = (jsuint)start;
2697 if (isLast) {
2698 stop = 0;
2699 direction = -1;
2700 } else {
2701 stop = length - 1;
2702 direction = 1;
2705 for (;;) {
2706 if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_JUMP) ||
2707 !GetArrayElement(cx, obj, (jsuint)i, &hole, vp)) {
2708 return JS_FALSE;
2710 if (!hole && js_StrictlyEqual(cx, *vp, tosearch))
2711 return js_NewNumberInRootedValue(cx, i, vp);
2712 if (i == stop)
2713 goto not_found;
2714 i += direction;
2717 not_found:
2718 *vp = INT_TO_JSVAL(-1);
2719 return JS_TRUE;
2722 static JSBool
2723 array_indexOf(JSContext *cx, uintN argc, jsval *vp)
2725 return array_indexOfHelper(cx, JS_FALSE, argc, vp);
2728 static JSBool
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 {
2736 FOREACH,
2737 REDUCE,
2738 REDUCE_RIGHT,
2739 MAP,
2740 FILTER,
2741 SOME,
2742 EVERY
2743 } ArrayExtraMode;
2745 #define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT)
2747 static JSBool
2748 array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, jsval *vp)
2750 JSObject *obj;
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;
2756 void *mark;
2758 obj = JS_THIS_OBJECT(cx, vp);
2759 if (!obj || !js_GetLengthProperty(cx, obj, &length))
2760 return JS_FALSE;
2763 * First, get or compute our callee, so that we error out consistently
2764 * when passed a non-callable object.
2766 if (argc == 0) {
2767 js_ReportMissingArg(cx, vp, 0);
2768 return JS_FALSE;
2770 argv = vp + 2;
2771 callable = js_ValueToCallableObject(cx, &argv[0], JSV2F_SEARCH_STACK);
2772 if (!callable)
2773 return JS_FALSE;
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 */
2780 newlen = 0;
2781 newarr = NULL;
2782 #endif
2783 start = 0, end = length, step = 1;
2785 switch (mode) {
2786 case REDUCE_RIGHT:
2787 start = length - 1, end = -1, step = -1;
2788 /* FALL THROUGH */
2789 case REDUCE:
2790 if (length == 0 && argc == 1) {
2791 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2792 JSMSG_EMPTY_ARRAY_REDUCE);
2793 return JS_FALSE;
2795 if (argc >= 2) {
2796 *vp = argv[1];
2797 } else {
2798 do {
2799 if (!GetArrayElement(cx, obj, start, &hole, vp))
2800 return JS_FALSE;
2801 start += step;
2802 } while (hole && start != end);
2804 if (hole && start == end) {
2805 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2806 JSMSG_EMPTY_ARRAY_REDUCE);
2807 return JS_FALSE;
2810 break;
2811 case MAP:
2812 case FILTER:
2813 newlen = (mode == MAP) ? length : 0;
2814 newarr = js_NewArrayObject(cx, newlen, NULL);
2815 if (!newarr)
2816 return JS_FALSE;
2817 *vp = OBJECT_TO_JSVAL(newarr);
2818 break;
2819 case SOME:
2820 *vp = JSVAL_FALSE;
2821 break;
2822 case EVERY:
2823 *vp = JSVAL_TRUE;
2824 break;
2825 case FOREACH:
2826 *vp = JSVAL_VOID;
2827 break;
2830 if (length == 0)
2831 return JS_TRUE;
2833 if (argc > 1 && !REDUCE_MODE(mode)) {
2834 if (!js_ValueToObject(cx, argv[1], &thisp))
2835 return JS_FALSE;
2836 argv[1] = OBJECT_TO_JSVAL(thisp);
2837 } else {
2838 thisp = NULL;
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);
2847 if (!elemroot)
2848 return JS_FALSE;
2850 MUST_FLOW_THROUGH("out");
2851 ok = JS_TRUE;
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);
2857 if (!ok)
2858 goto out;
2859 if (hole)
2860 continue;
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
2866 * rooting.
2868 sp = invokevp;
2869 *sp++ = OBJECT_TO_JSVAL(callable);
2870 *sp++ = OBJECT_TO_JSVAL(thisp);
2871 if (REDUCE_MODE(mode))
2872 *sp++ = *vp;
2873 *sp++ = *elemroot;
2874 *sp++ = INT_TO_JSVAL(i);
2875 *sp++ = OBJECT_TO_JSVAL(obj);
2877 /* Do the call. */
2878 ok = js_Invoke(cx, argc, invokevp, 0);
2879 if (!ok)
2880 break;
2882 if (mode > MAP)
2883 cond = js_ValueToBoolean(*invokevp);
2884 #ifdef __GNUC__ /* quell GCC overwarning */
2885 else
2886 cond = JS_FALSE;
2887 #endif
2889 switch (mode) {
2890 case FOREACH:
2891 break;
2892 case REDUCE:
2893 case REDUCE_RIGHT:
2894 *vp = *invokevp;
2895 break;
2896 case MAP:
2897 ok = SetArrayElement(cx, newarr, i, *invokevp);
2898 if (!ok)
2899 goto out;
2900 break;
2901 case FILTER:
2902 if (!cond)
2903 break;
2904 /* The filter passed *elemroot, so push it onto our result. */
2905 ok = SetArrayElement(cx, newarr, newlen++, *elemroot);
2906 if (!ok)
2907 goto out;
2908 break;
2909 case SOME:
2910 if (cond) {
2911 *vp = JSVAL_TRUE;
2912 goto out;
2914 break;
2915 case EVERY:
2916 if (!cond) {
2917 *vp = JSVAL_FALSE;
2918 goto out;
2920 break;
2924 out:
2925 js_FreeStack(cx, mark);
2926 if (ok && mode == FILTER)
2927 ok = js_SetLengthProperty(cx, newarr, newlen);
2928 return ok;
2931 static JSBool
2932 array_forEach(JSContext *cx, uintN argc, jsval *vp)
2934 return array_extra(cx, FOREACH, argc, vp);
2937 static JSBool
2938 array_map(JSContext *cx, uintN argc, jsval *vp)
2940 return array_extra(cx, MAP, argc, vp);
2943 static JSBool
2944 array_reduce(JSContext *cx, uintN argc, jsval *vp)
2946 return array_extra(cx, REDUCE, argc, vp);
2949 static JSBool
2950 array_reduceRight(JSContext *cx, uintN argc, jsval *vp)
2952 return array_extra(cx, REDUCE_RIGHT, argc, vp);
2955 static JSBool
2956 array_filter(JSContext *cx, uintN argc, jsval *vp)
2958 return array_extra(cx, FILTER, argc, vp);
2961 static JSBool
2962 array_some(JSContext *cx, uintN argc, jsval *vp)
2964 return array_extra(cx, SOME, argc, vp);
2967 static JSBool
2968 array_every(JSContext *cx, uintN argc, jsval *vp)
2970 return array_extra(cx, EVERY, argc, vp);
2972 #endif
2974 static JSPropertySpec array_props[] = {
2975 {js_length_str, -1, JSPROP_SHARED | JSPROP_PERMANENT,
2976 array_length_getter, array_length_setter},
2977 {0,0,0,0,0}
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[] = {
2990 #if JS_HAS_TOSOURCE
2991 JS_FN(js_toSource_str, array_toSource, 0,0),
2992 #endif
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),
3020 #endif
3022 JS_FS_END
3025 JSBool
3026 js_Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
3028 jsuint length;
3029 jsval *vector;
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);
3034 if (!obj)
3035 return JS_FALSE;
3036 *rval = OBJECT_TO_JSVAL(obj);
3039 if (argc == 0) {
3040 length = 0;
3041 vector = NULL;
3042 } else if (argc > 1) {
3043 length = (jsuint) argc;
3044 vector = argv;
3045 } else if (!JSVAL_IS_NUMBER(argv[0])) {
3046 length = 1;
3047 vector = argv;
3048 } else {
3049 length = ValueIsLength(cx, &argv[0]);
3050 if (JSVAL_IS_NULL(argv[0]))
3051 return JS_FALSE;
3052 vector = NULL;
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);
3060 #ifdef JS_TRACER
3062 JSObject* FASTCALL
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));
3069 if (!obj)
3070 return NULL;
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);
3085 if (!obj->map)
3086 return NULL;
3087 obj->dslots = NULL;
3088 return obj;
3091 JSObject* FASTCALL
3092 js_FastNewArrayWithLength(JSContext* cx, JSObject* proto, uint32 i)
3094 JS_ASSERT(JS_ON_TRACE(cx));
3095 JSObject* obj = js_FastNewArray(cx, proto);
3096 if (obj)
3097 obj->fslots[JSSLOT_ARRAY_LENGTH] = i;
3098 return obj;
3101 JSObject* FASTCALL
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)))
3106 return NULL;
3107 return obj;
3110 #define ARRAY_CTOR_GUTS(exact_len, newslots_code) \
3111 JS_ASSERT(JS_ON_TRACE(cx)); \
3112 JSObject* obj = js_FastNewArray(cx, proto); \
3113 if (obj) { \
3114 const uint32 len = ARRAY_GROWBY; \
3115 jsval* newslots = (jsval*) JS_malloc(cx, sizeof (jsval) * (len + 1)); \
3116 if (newslots) { \
3117 obj->dslots = newslots + 1; \
3118 ARRAY_SET_DENSE_LENGTH(obj, len); \
3119 {newslots_code} \
3120 while (++newslots < obj->dslots + len) \
3121 *newslots = JSVAL_HOLE; \
3122 obj->fslots[JSSLOT_ARRAY_LENGTH] = (exact_len); \
3123 return obj; \
3126 return NULL;
3128 JSObject* FASTCALL
3129 js_Array_1str(JSContext* cx, JSObject* proto, JSString *str)
3131 ARRAY_CTOR_GUTS(1, *++newslots = STRING_TO_JSVAL(str);)
3134 #endif /* JS_TRACER */
3136 JSObject *
3137 js_InitArrayClass(JSContext *cx, JSObject *obj)
3139 JSObject *proto;
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))
3152 return NULL;
3153 return proto;
3156 JSObject *
3157 js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector, JSBool holey)
3159 JSTempValueRooter tvr;
3160 JSObject *obj;
3162 obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL, 0);
3163 if (!obj)
3164 return NULL;
3166 JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
3167 if (!InitArrayObject(cx, obj, length, vector, holey))
3168 obj = NULL;
3169 JS_POP_TEMP_ROOT(cx, &tvr);
3171 /* Set/clear newborn root, in case we lost it. */
3172 cx->weakRoots.newborn[GCX_OBJECT] = obj;
3173 return obj;
3176 JSObject *
3177 js_NewSlowArrayObject(JSContext *cx)
3179 JSObject *obj = js_NewObject(cx, &js_SlowArrayClass, NULL, NULL, 0);
3180 if (obj)
3181 obj->fslots[JSSLOT_ARRAY_LENGTH] = 0;
3182 return obj;
3185 #ifdef DEBUG_ARRAYS
3186 JSBool
3187 js_ArrayInfo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
3189 uintN i;
3190 JSObject *array;
3192 for (i = 0; i < argc; i++) {
3193 char *bytes;
3195 bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, argv[i],
3196 NULL);
3197 if (!bytes)
3198 return JS_FALSE;
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);
3202 JS_free(cx, bytes);
3203 continue;
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);
3214 JS_free(cx, bytes);
3216 return JS_TRUE;
3218 #endif
3220 JS_FRIEND_API(JSBool)
3221 js_ArrayToJSUint8Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count,
3222 JSUint8 *dest)
3224 uint32 length;
3226 if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj))
3227 return JS_FALSE;
3229 length = obj->fslots[JSSLOT_ARRAY_LENGTH];
3230 if (length < offset + count)
3231 return JS_FALSE;
3233 jsval v;
3234 jsint vi;
3236 JSUint8 *dp = dest;
3237 for (uintN i = offset; i < offset+count; i++) {
3238 v = obj->dslots[i];
3239 if (!JSVAL_IS_INT(v) || (vi = JSVAL_TO_INT(v)) < 0)
3240 return JS_FALSE;
3242 *dp++ = (JSUint8) vi;
3245 return JS_TRUE;
3248 JS_FRIEND_API(JSBool)
3249 js_ArrayToJSUint16Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count,
3250 JSUint16 *dest)
3252 uint32 length;
3254 if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj))
3255 return JS_FALSE;
3257 length = obj->fslots[JSSLOT_ARRAY_LENGTH];
3258 if (length < offset + count)
3259 return JS_FALSE;
3261 jsval v;
3262 jsint vi;
3264 JSUint16 *dp = dest;
3265 for (uintN i = offset; i < offset+count; i++) {
3266 v = obj->dslots[i];
3267 if (!JSVAL_IS_INT(v) || (vi = JSVAL_TO_INT(v)) < 0)
3268 return JS_FALSE;
3270 *dp++ = (JSUint16) vi;
3273 return JS_TRUE;
3276 JS_FRIEND_API(JSBool)
3277 js_ArrayToJSUint32Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count,
3278 JSUint32 *dest)
3280 uint32 length;
3282 if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj))
3283 return JS_FALSE;
3285 length = obj->fslots[JSSLOT_ARRAY_LENGTH];
3286 if (length < offset + count)
3287 return JS_FALSE;
3289 jsval v;
3290 jsint vi;
3292 JSUint32 *dp = dest;
3293 for (uintN i = offset; i < offset+count; i++) {
3294 v = obj->dslots[i];
3295 if (!JSVAL_IS_INT(v) || (vi = JSVAL_TO_INT(v)) < 0)
3296 return JS_FALSE;
3298 *dp++ = (JSUint32) vi;
3301 return JS_TRUE;
3304 JS_FRIEND_API(JSBool)
3305 js_ArrayToJSInt8Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count,
3306 JSInt8 *dest)
3308 uint32 length;
3310 if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj))
3311 return JS_FALSE;
3313 length = obj->fslots[JSSLOT_ARRAY_LENGTH];
3314 if (length < offset + count)
3315 return JS_FALSE;
3317 jsval v;
3318 JSInt8 *dp = dest;
3319 for (uintN i = offset; i < offset+count; i++) {
3320 v = obj->dslots[i];
3321 if (!JSVAL_IS_INT(v))
3322 return JS_FALSE;
3324 *dp++ = (JSInt8) JSVAL_TO_INT(v);
3327 return JS_TRUE;
3330 JS_FRIEND_API(JSBool)
3331 js_ArrayToJSInt16Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count,
3332 JSInt16 *dest)
3334 uint32 length;
3336 if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj))
3337 return JS_FALSE;
3339 length = obj->fslots[JSSLOT_ARRAY_LENGTH];
3340 if (length < offset + count)
3341 return JS_FALSE;
3343 jsval v;
3344 JSInt16 *dp = dest;
3345 for (uintN i = offset; i < offset+count; i++) {
3346 v = obj->dslots[i];
3347 if (!JSVAL_IS_INT(v))
3348 return JS_FALSE;
3350 *dp++ = (JSInt16) JSVAL_TO_INT(v);
3353 return JS_TRUE;
3356 JS_FRIEND_API(JSBool)
3357 js_ArrayToJSInt32Buffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count,
3358 JSInt32 *dest)
3360 uint32 length;
3362 if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj))
3363 return JS_FALSE;
3365 length = obj->fslots[JSSLOT_ARRAY_LENGTH];
3366 if (length < offset + count)
3367 return JS_FALSE;
3369 jsval v;
3370 JSInt32 *dp = dest;
3371 for (uintN i = offset; i < offset+count; i++) {
3372 v = obj->dslots[i];
3373 if (!JSVAL_IS_INT(v))
3374 return JS_FALSE;
3376 *dp++ = (JSInt32) JSVAL_TO_INT(v);
3379 return JS_TRUE;
3382 JS_FRIEND_API(JSBool)
3383 js_ArrayToJSDoubleBuffer(JSContext *cx, JSObject *obj, jsuint offset, jsuint count,
3384 jsdouble *dest)
3386 uint32 length;
3388 if (!obj || !OBJ_IS_DENSE_ARRAY(cx, obj))
3389 return JS_FALSE;
3391 length = obj->fslots[JSSLOT_ARRAY_LENGTH];
3392 if (length < offset + count)
3393 return JS_FALSE;
3395 jsval v;
3396 jsdouble *dp = dest;
3397 for (uintN i = offset; i < offset+count; i++) {
3398 v = obj->dslots[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));
3403 else
3404 return JS_FALSE;
3407 return JS_TRUE;
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)