summaryrefslogtreecommitdiffstats
path: root/src/dom/js/jsarray.c
diff options
context:
space:
mode:
authorBob Jamison <ishmalius@gmail.com>2007-03-05 10:34:59 +0000
committerishmal <ishmal@users.sourceforge.net>2007-03-05 10:34:59 +0000
commit33837efd4b94c4ebb80f95b3d9dbb6efd5499a98 (patch)
treede482d7687b3bffa97cc78608ba75a8ad7e52b60 /src/dom/js/jsarray.c
parentAdding optional dialog preview. Implments RFE [ 1435276 ] switch preview on/o... (diff)
downloadinkscape-33837efd4b94c4ebb80f95b3d9dbb6efd5499a98.tar.gz
inkscape-33837efd4b94c4ebb80f95b3d9dbb6efd5499a98.zip
update JS
(bzr r2555)
Diffstat (limited to 'src/dom/js/jsarray.c')
-rw-r--r--src/dom/js/jsarray.c955
1 files changed, 714 insertions, 241 deletions
diff --git a/src/dom/js/jsarray.c b/src/dom/js/jsarray.c
index a05a6ee3c..ef94be4e5 100644
--- a/src/dom/js/jsarray.c
+++ b/src/dom/js/jsarray.c
@@ -1,4 +1,5 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set sw=4 ts=8 et tw=80:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
@@ -48,6 +49,7 @@
#include "jsapi.h"
#include "jsarray.h"
#include "jsatom.h"
+#include "jsbool.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsfun.h"
@@ -63,7 +65,7 @@
#define MAXSTR "4294967295"
/*
- * Determine if the id represents an array index.
+ * Determine if the id represents an array index or an XML property index.
*
* An id is an array index according to ECMA by (15.4):
*
@@ -78,8 +80,8 @@
* that calling a standard conversion routine might allow strings such as
* "08" or "4.0" as array indices, which they are not.
*/
-static JSBool
-IdIsIndex(jsid id, jsuint *indexp)
+JSBool
+js_IdIsIndex(jsval id, jsuint *indexp)
{
JSString *str;
jschar *cp;
@@ -93,7 +95,10 @@ IdIsIndex(jsid id, jsuint *indexp)
return JS_TRUE;
}
- /* It must be a string. */
+ /* NB: id should be a string, but jsxml.c may call us with an object id. */
+ if (!JSVAL_IS_STRING(id))
+ return JS_FALSE;
+
str = JSVAL_TO_STRING(id);
cp = JSSTRING_CHARS(str);
if (JS7_ISDEC(*cp) && JSSTRING_LENGTH(str) < sizeof(MAXSTR)) {
@@ -108,9 +113,8 @@ IdIsIndex(jsid id, jsuint *indexp)
cp++;
}
}
- /* Make sure all characters were consumed and that it couldn't
- * have overflowed.
- */
+
+ /* Ensure that all characters were consumed and we didn't overflow. */
if (*cp == 0 &&
(oldIndex < (MAXINDEX / 10) ||
(oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10))))
@@ -138,7 +142,7 @@ ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp)
*lengthp = (jsuint) i;
return JS_TRUE;
}
-
+
if (!js_ValueToNumber(cx, v, &d)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_ARRAY_LENGTH);
@@ -160,57 +164,94 @@ ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp)
JSBool
js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
{
+ JSTempValueRooter tvr;
jsid id;
+ JSBool ok;
jsint i;
- jsval v;
-
- id = (jsid) cx->runtime->atomState.lengthAtom;
- if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
- return JS_FALSE;
- /* Short-circuit, because js_ValueToECMAUint32 fails when
- * called during init time.
- */
- if (JSVAL_IS_INT(v)) {
- i = JSVAL_TO_INT(v);
- /* jsuint cast does ToUint32. */
- *lengthp = (jsuint)i;
- return JS_TRUE;
+ JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
+ id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
+ ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value);
+ if (ok) {
+ /*
+ * Short-circuit, because js_ValueToECMAUint32 fails when called
+ * during init time.
+ */
+ if (JSVAL_IS_INT(tvr.u.value)) {
+ i = JSVAL_TO_INT(tvr.u.value);
+ *lengthp = (jsuint)i; /* jsuint cast does ToUint32 */
+ } else {
+ ok = js_ValueToECMAUint32(cx, tvr.u.value, (uint32 *)lengthp);
+ }
}
- return js_ValueToECMAUint32(cx, v, (uint32 *)lengthp);
+ JS_POP_TEMP_ROOT(cx, &tvr);
+ return ok;
}
static JSBool
-IndexToValue(JSContext *cx, jsuint length, jsval *vp)
+IndexToValue(JSContext *cx, jsuint index, jsval *vp)
{
- if (length <= JSVAL_INT_MAX) {
- *vp = INT_TO_JSVAL(length);
+ if (index <= JSVAL_INT_MAX) {
+ *vp = INT_TO_JSVAL(index);
return JS_TRUE;
}
- return js_NewDoubleValue(cx, (jsdouble)length, vp);
+ return js_NewDoubleValue(cx, (jsdouble)index, vp);
}
static JSBool
-IndexToId(JSContext *cx, jsuint length, jsid *idp)
+IndexToId(JSContext *cx, jsuint index, jsid *idp)
{
JSString *str;
JSAtom *atom;
- if (length <= JSVAL_INT_MAX) {
- *idp = (jsid) INT_TO_JSVAL(length);
+ if (index <= JSVAL_INT_MAX) {
+ *idp = INT_TO_JSID(index);
} else {
- str = js_NumberToString(cx, (jsdouble)length);
+ str = js_NumberToString(cx, (jsdouble)index);
if (!str)
return JS_FALSE;
atom = js_AtomizeString(cx, str, 0);
if (!atom)
return JS_FALSE;
- *idp = (jsid)atom;
+ *idp = ATOM_TO_JSID(atom);
}
return JS_TRUE;
}
+static JSBool
+PropertyExists(JSContext *cx, JSObject *obj, jsid id, JSBool *foundp)
+{
+ JSObject *obj2;
+ JSProperty *prop;
+
+ if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
+ return JS_FALSE;
+
+ *foundp = prop != NULL;
+ if (*foundp) {
+ OBJ_DROP_PROPERTY(cx, obj2, prop);
+ }
+
+ return JS_TRUE;
+}
+
+#define JSID_HOLE JSVAL_NULL
+
+static JSBool
+IndexToExistingId(JSContext *cx, JSObject *obj, jsuint index, jsid *idp)
+{
+ JSBool exists;
+
+ if (!IndexToId(cx, index, idp))
+ return JS_FALSE;
+ if (!PropertyExists(cx, obj, *idp, &exists))
+ return JS_FALSE;
+ if (!exists)
+ *idp = JSID_HOLE;
+ return JS_TRUE;
+}
+
JSBool
js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length)
{
@@ -219,7 +260,7 @@ js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length)
if (!IndexToValue(cx, length, &v))
return JS_FALSE;
- id = (jsid) cx->runtime->atomState.lengthAtom;
+ id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
return OBJ_SET_PROPERTY(cx, obj, id, &v);
}
@@ -227,17 +268,19 @@ JSBool
js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
{
JSErrorReporter older;
+ JSTempValueRooter tvr;
jsid id;
JSBool ok;
- jsval v;
older = JS_SetErrorReporter(cx, NULL);
- id = (jsid) cx->runtime->atomState.lengthAtom;
- ok = OBJ_GET_PROPERTY(cx, obj, id, &v);
+ JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
+ id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
+ ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value);
JS_SetErrorReporter(cx, older);
- if (!ok)
- return JS_FALSE;
- return ValueIsLength(cx, v, lengthp);
+ if (ok)
+ ok = ValueIsLength(cx, tvr.u.value, lengthp);
+ JS_POP_TEMP_ROOT(cx, &tvr);
+ return ok;
}
/*
@@ -278,7 +321,7 @@ array_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
jsuint index, length;
- if (!(IdIsIndex(id, &index)))
+ if (!js_IdIsIndex(id, &index))
return JS_TRUE;
if (!js_GetLengthProperty(cx, obj, &length))
return JS_FALSE;
@@ -294,7 +337,7 @@ array_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
jsuint length;
- if (cx->version == JSVERSION_1_2) {
+ if (JS_VERSION_IS_1_2(cx)) {
if (!js_GetLengthProperty(cx, obj, &length))
return JS_FALSE;
switch (type) {
@@ -323,14 +366,20 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
jsval *rval, JSBool localeString)
{
JSBool ok;
- jsval v;
jsuint length, index;
jschar *chars, *ochars;
size_t nchars, growth, seplen, tmplen;
const jschar *sepstr;
JSString *str;
JSHashEntry *he;
- JSObject *obj2;
+ JSTempValueRooter tvr;
+ JSAtom *atom;
+ int stackDummy;
+
+ if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
+ return JS_FALSE;
+ }
ok = js_GetLengthProperty(cx, obj, &length);
if (!ok)
@@ -398,24 +447,28 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
sepstr = NULL;
seplen = JSSTRING_LENGTH(sep);
+ /* Use rval to locally root each element value as we loop and convert. */
+#define v (*rval)
+
v = JSVAL_NULL;
for (index = 0; index < length; index++) {
ok = JS_GetElement(cx, obj, index, &v);
if (!ok)
goto done;
- if (!literalize && (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v))) {
+ if ((!literalize || JS_VERSION_IS_1_2(cx)) &&
+ (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v))) {
str = cx->runtime->emptyString;
} else {
if (localeString) {
- if (!js_ValueToObject(cx, v, &obj2) ||
- !js_TryMethod(cx, obj2,
- cx->runtime->atomState.toLocaleStringAtom,
- 0, NULL, &v)) {
- str = NULL;
- } else {
- str = js_ValueToString(cx, v);
- }
+ atom = cx->runtime->atomState.toLocaleStringAtom;
+ JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr);
+ ok = js_ValueToObject(cx, v, &tvr.u.object) &&
+ js_TryMethod(cx, tvr.u.object, atom, 0, NULL, &v);
+ JS_POP_TEMP_ROOT(cx, &tvr);
+ if (!ok)
+ goto done;
+ str = js_ValueToString(cx, v);
} else {
str = (literalize ? js_ValueToSource : js_ValueToString)(cx, v);
}
@@ -426,9 +479,18 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
}
/* Allocate 3 + 1 at end for ", ", closing bracket, and zero. */
- growth = (nchars + (sepstr ? seplen : 0) +
- JSSTRING_LENGTH(str) +
- 3 + 1) * sizeof(jschar);
+ tmplen = JSSTRING_LENGTH(str);
+ growth = (nchars + (sepstr ? seplen : 0) + tmplen + 3 + 1);
+ if (nchars > growth || tmplen > growth ||
+ growth > (size_t)-1 / sizeof(jschar)) {
+ if (chars) {
+ free(chars);
+ chars = NULL;
+ }
+ JS_ReportOutOfMemory(cx);
+ goto done;
+ }
+ growth *= sizeof(jschar);
if (!chars) {
chars = (jschar *) malloc(growth);
if (!chars)
@@ -447,7 +509,6 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
}
sepstr = JSSTRING_CHARS(sep);
- tmplen = JSSTRING_LENGTH(str);
js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen);
nchars += tmplen;
}
@@ -471,6 +532,8 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
return ok;
}
+#undef v
+
make_string:
if (!chars) {
JS_ReportOutOfMemory(cx);
@@ -510,7 +573,7 @@ array_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
* JS1.2 arrays convert to array literals, with a comma followed by a space
* between each element.
*/
- literalize = (cx->version == JSVERSION_1_2);
+ literalize = JS_VERSION_IS_1_2(cx);
return array_join_sub(cx, obj, literalize ? &comma_space : &comma,
literalize, rval, JS_FALSE);
}
@@ -533,6 +596,8 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint length, jsval *vector)
jsid id;
for (index = 0; index < length; index++) {
+ JS_ASSERT(vector[index] != JSVAL_HOLE);
+
if (!IndexToId(cx, index, &id))
return JS_FALSE;
if (!OBJ_SET_PROPERTY(cx, obj, id, &vector[index]))
@@ -549,7 +614,7 @@ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector)
if (!IndexToValue(cx, length, &v))
return JS_FALSE;
- id = (jsid) cx->runtime->atomState.lengthAtom;
+ id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
if (!OBJ_DEFINE_PROPERTY(cx, obj, id, v,
array_length_getter, array_length_setter,
JSPROP_PERMANENT,
@@ -585,42 +650,76 @@ array_reverse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
{
jsuint len, half, i;
jsid id, id2;
- jsval v, v2;
+ jsval *tmproot, *tmproot2;
+ JSBool idexists, id2exists, ok;
if (!js_GetLengthProperty(cx, obj, &len))
return JS_FALSE;
+ /*
+ * When len > JSVAL_INT_MAX + 1 the loop below accesses indexes greater
+ * than JSVAL_INT_MAX. For such indexes the corresponding ids are atoms.
+ * We use JS_KEEP_ATOMS to protect them against GC since OBJ_GET_PROPERTY
+ * can potentially execute an arbitrary script. See bug 341956.
+ *
+ * After this point control must flow through label out: to exit.
+ */
+ if (len > JSVAL_INT_MAX + 1)
+ JS_KEEP_ATOMS(cx->runtime);
+
+ /*
+ * Use argv[argc] and argv[argc + 1] as local roots to hold temporarily
+ * array elements for GC-safe swap.
+ */
+ tmproot = argv + argc;
+ tmproot2 = argv + argc + 1;
half = len / 2;
for (i = 0; i < half; i++) {
+ /*
+ * Get both values while checking for holes to make sure they don't
+ * get filled.
+ */
if (!IndexToId(cx, i, &id))
- return JS_FALSE;
- if (!IndexToId(cx, len - i - 1, &id2))
- return JS_FALSE;
- if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
- return JS_FALSE;
- if (!OBJ_GET_PROPERTY(cx, obj, id2, &v2))
- return JS_FALSE;
-
-#if JS_HAS_SPARSE_ARRAYS
- /* This part isn't done yet. */
+ goto bad;
+ if (!PropertyExists(cx, obj, id, &idexists))
+ goto bad;
+ if (idexists && !OBJ_GET_PROPERTY(cx, obj, id, tmproot))
+ goto bad;
- if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
- return JS_FALSE;
- if (!prop) {
- OBJ_DELETE_PROPERTY(cx, obj, id2, &v); /* v is junk. */
- continue;
+ if (!IndexToId(cx, len - i - 1, &id2))
+ goto bad;
+ if (!PropertyExists(cx, obj, id2, &id2exists))
+ goto bad;
+ if (id2exists && !OBJ_GET_PROPERTY(cx, obj, id2, tmproot2))
+ goto bad;
+
+ /* Exchange the values. */
+ if (idexists) {
+ if (!OBJ_SET_PROPERTY(cx, obj, id2, tmproot))
+ goto bad;
+ } else {
+ if (!OBJ_DELETE_PROPERTY(cx, obj, id2, tmproot))
+ goto bad;
+ }
+ if (id2exists) {
+ if (!OBJ_SET_PROPERTY(cx, obj, id, tmproot2))
+ goto bad;
+ } else {
+ if (!OBJ_DELETE_PROPERTY(cx, obj, id, tmproot2))
+ goto bad;
}
- OBJ_DROP_PROPERTY(cx, obj2, prop);
-#endif
-
- if (!OBJ_SET_PROPERTY(cx, obj, id, &v2))
- return JS_FALSE;
- if (!OBJ_SET_PROPERTY(cx, obj, id2, &v))
- return JS_FALSE;
}
+ ok = JS_TRUE;
+ out:
+ if (len > JSVAL_INT_MAX + 1)
+ JS_UNKEEP_ATOMS(cx->runtime);
*rval = OBJECT_TO_JSVAL(obj);
- return JS_TRUE;
+ return ok;
+
+ bad:
+ ok = JS_FALSE;
+ goto out;
}
typedef struct HSortArgs {
@@ -666,7 +765,7 @@ HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi)
a = (char *)vec + (hi - 1) * elsize;
b = (char *)vec2 + j * elsize;
- /*
+ /*
* During sorting phase b points to a member of heap that cannot be
* bigger then biggest of vec[0] and vec[1], and cmp(a, b, arg) <= 0
* always holds.
@@ -703,16 +802,13 @@ HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi)
#undef MEMCPY
}
-JSBool
-js_HeapSort(void *vec, size_t nel, size_t elsize, JSComparator cmp, void *arg)
+void
+js_HeapSort(void *vec, size_t nel, void *pivot, size_t elsize,
+ JSComparator cmp, void *arg)
{
- void *pivot;
HSortArgs hsa;
size_t i;
- pivot = malloc(elsize);
- if (!pivot)
- return JS_FALSE;
hsa.vec = vec;
hsa.elsize = elsize;
hsa.pivot = pivot;
@@ -724,15 +820,13 @@ js_HeapSort(void *vec, size_t nel, size_t elsize, JSComparator cmp, void *arg)
HeapSortHelper(JS_TRUE, &hsa, i, nel);
while (nel > 2)
HeapSortHelper(JS_FALSE, &hsa, 1, --nel);
-
- free(pivot);
- return JS_TRUE;
}
typedef struct CompareArgs {
- JSContext *context;
- jsval fval;
- JSBool status;
+ JSContext *context;
+ jsval fval;
+ jsval *localroot; /* need one local root, for sort_compare */
+ JSBool status;
} CompareArgs;
static int
@@ -742,46 +836,75 @@ sort_compare(const void *a, const void *b, void *arg)
CompareArgs *ca = (CompareArgs *) arg;
JSContext *cx = ca->context;
jsdouble cmp = -1;
- jsval fval, argv[2], rval;
+ jsval fval, argv[2], special;
JSBool ok;
fval = ca->fval;
- if (fval == JSVAL_NULL) {
+
+ /*
+ * By ECMA 262, 15.4.4.11, existence of the property with value undefined
+ * takes precedence over an undefined property (which we call a "hole").
+ */
+ if (av == JSVAL_HOLE || bv == JSVAL_HOLE)
+ special = JSVAL_HOLE;
+ else if (av == JSVAL_VOID || bv == JSVAL_VOID)
+ special = JSVAL_VOID;
+ else
+ special = JSVAL_NULL;
+
+ if (special != JSVAL_NULL) {
+ if (av == bv)
+ cmp = 0;
+ else if (av != special)
+ cmp = -1;
+ else
+ cmp = 1;
+ } else if (fval == JSVAL_NULL) {
JSString *astr, *bstr;
if (av == bv) {
cmp = 0;
- } else if (av == JSVAL_VOID || bv == JSVAL_VOID) {
- /* Put undefined properties at the end. */
- cmp = (av == JSVAL_VOID) ? 1 : -1;
- } else if ((astr = js_ValueToString(cx, av)) != NULL &&
- (bstr = js_ValueToString(cx, bv)) != NULL) {
- cmp = js_CompareStrings(astr, bstr);
} else {
- ca->status = JS_FALSE;
+ /*
+ * Set our local root to astr in case the second js_ValueToString
+ * displaces the newborn root in cx, and the GC nests under that
+ * call. Don't bother guarding the local root store with an astr
+ * non-null test. If we tag null as a string, the GC will untag,
+ * null-test, and avoid dereferencing null.
+ */
+ astr = js_ValueToString(cx, av);
+ *ca->localroot = STRING_TO_JSVAL(astr);
+ if (astr && (bstr = js_ValueToString(cx, bv)))
+ cmp = js_CompareStrings(astr, bstr);
+ else
+ ca->status = JS_FALSE;
}
} else {
argv[0] = av;
argv[1] = bv;
ok = js_InternalCall(cx,
OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fval)),
- fval, 2, argv, &rval);
+ fval, 2, argv, ca->localroot);
if (ok) {
- ok = js_ValueToNumber(cx, rval, &cmp);
+ ok = js_ValueToNumber(cx, *ca->localroot, &cmp);
+
/* Clamp cmp to -1, 0, 1. */
- if (JSDOUBLE_IS_NaN(cmp)) {
- /* XXX report some kind of error here? ECMA talks about
- * 'consistent compare functions' that don't return NaN, but is
- * silent about what the result should be. So we currently
- * ignore it.
- */
- cmp = 0;
- } else if (cmp != 0) {
- cmp = cmp > 0 ? 1 : -1;
+ if (ok) {
+ if (JSDOUBLE_IS_NaN(cmp)) {
+ /*
+ * XXX report some kind of error here? ECMA talks about
+ * 'consistent compare functions' that don't return NaN,
+ * but is silent about what the result should be. So we
+ * currently ignore it.
+ */
+ cmp = 0;
+ } else if (cmp != 0) {
+ cmp = cmp > 0 ? 1 : -1;
+ }
}
- } else {
- ca->status = ok;
}
+ if (!ok)
+ ca->status = ok;
}
return (int)cmp;
}
@@ -794,17 +917,13 @@ sort_compare_strings(const void *a, const void *b, void *arg)
return (int) js_CompareStrings(JSVAL_TO_STRING(av), JSVAL_TO_STRING(bv));
}
-/* XXXmccabe do the sort helper functions need to take int? (Or can we claim
- * that 2^32 * 32 is too large to worry about?) Something dumps when I change
- * to unsigned int; is qsort using -1 as a fencepost?
- */
static JSBool
array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
- jsval fval;
+ jsval fval, *vec, *pivotroot;
CompareArgs ca;
jsuint len, newlen, i;
- jsval *vec;
+ JSStackFrame *fp;
jsid id;
size_t nbytes;
@@ -835,80 +954,70 @@ array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
}
/*
- * Test for size_t overflow, which could lead to indexing beyond the end
- * of the malloc'd vector.
+ * We need a temporary array of len jsvals to hold elements of the array.
+ * Check that its size does not overflow size_t, which would allow for
+ * indexing beyond the end of the malloc'd vector.
*/
- nbytes = len * sizeof(jsval);
- if (nbytes != (double) len * sizeof(jsval)) {
+ if (len > ((size_t) -1) / sizeof(jsval)) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
+ nbytes = ((size_t) len) * sizeof(jsval);
+
vec = (jsval *) JS_malloc(cx, nbytes);
if (!vec)
return JS_FALSE;
-#if JS_HAS_SPARSE_ARRAYS
- newlen = 0;
-#else
- newlen = len;
-#endif
+ /* Root vec, clearing it first in case a GC nests while we're filling it. */
+ memset(vec, 0, nbytes);
+ fp = cx->fp;
+ fp->vars = vec;
+ fp->nvars = len;
+ newlen = 0;
for (i = 0; i < len; i++) {
- ca.status = IndexToId(cx, i, &id);
+ ca.status = IndexToExistingId(cx, obj, i, &id);
if (!ca.status)
goto out;
-#if JS_HAS_SPARSE_ARRAYS
- {
- JSObject *obj2;
- JSProperty *prop;
- ca.status = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
- if (!ca.status)
- goto out;
- if (!prop) {
- vec[i] = JSVAL_VOID;
- continue;
- }
- OBJ_DROP_PROPERTY(cx, obj2, prop);
- newlen++;
+
+ if (id == JSID_HOLE) {
+ vec[i] = JSVAL_HOLE;
+ all_strings = JS_FALSE;
+ continue;
}
-#endif
+ newlen++;
+
ca.status = OBJ_GET_PROPERTY(cx, obj, id, &vec[i]);
if (!ca.status)
goto out;
/* We know JSVAL_IS_STRING yields 0 or 1, so avoid a branch via &=. */
- all_strings &= JSVAL_IS_STRING(vec[i]);
+ all_strings &= JSVAL_IS_STRING(vec[i]);
}
ca.context = cx;
ca.fval = fval;
+ ca.localroot = argv + argc; /* local GC root for temporary string */
+ pivotroot = argv + argc + 1; /* local GC root for pivot val */
ca.status = JS_TRUE;
- if (!js_HeapSort(vec, (size_t) len, sizeof(jsval),
- all_strings ? sort_compare_strings : sort_compare,
- &ca)) {
- JS_ReportOutOfMemory(cx);
- ca.status = JS_FALSE;
- }
+ js_HeapSort(vec, (size_t) len, pivotroot, sizeof(jsval),
+ all_strings ? sort_compare_strings : sort_compare,
+ &ca);
if (ca.status) {
ca.status = InitArrayElements(cx, obj, newlen, vec);
if (ca.status)
*rval = OBJECT_TO_JSVAL(obj);
-#if JS_HAS_SPARSE_ARRAYS
- /* set length of newly-created array object to old length. */
- if (ca.status && newlen < len) {
- ca.status = js_SetLengthProperty(cx, obj, len);
-
- /* Delete any leftover properties greater than newlen. */
- while (ca.status && newlen < len) {
- jsval junk;
-
- ca.status = !IndexToId(cx, newlen, &id) ||
- !OBJ_DELETE_PROPERTY(cx, obj, id, &junk);
- newlen++;
- }
+
+ /* Re-create any holes that sorted to the end of the array. */
+ while (len > newlen) {
+ jsval junk;
+
+ if (!IndexToId(cx, --len, &id))
+ return JS_FALSE;
+ if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk))
+ return JS_FALSE;
}
-#endif
}
out:
@@ -943,7 +1052,7 @@ array_push(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
* return the new array length.
*/
length += argc;
- if (cx->version == JSVERSION_1_2) {
+ if (JS_VERSION_IS_1_2(cx)) {
*rval = argc ? argv[argc-1] : JSVAL_VOID;
} else {
if (!IndexToValue(cx, length, rval))
@@ -957,20 +1066,26 @@ array_pop(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
jsuint index;
jsid id;
+ JSBool ok;
jsval junk;
if (!js_GetLengthProperty(cx, obj, &index))
return JS_FALSE;
if (index > 0) {
index--;
- if (!IndexToId(cx, index, &id))
- return JS_FALSE;
/* Get the to-be-deleted property's value into rval. */
- if (!OBJ_GET_PROPERTY(cx, obj, id, rval))
+ if (!IndexToId(cx, index, &id))
return JS_FALSE;
- if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk))
+ /* See comments in array_reverse. */
+ if (index > JSVAL_INT_MAX)
+ JS_KEEP_ATOMS(cx->runtime);
+ ok = OBJ_GET_PROPERTY(cx, obj, id, rval) &&
+ OBJ_DELETE_PROPERTY(cx, obj, id, &junk);
+ if (index > JSVAL_INT_MAX)
+ JS_UNKEEP_ATOMS(cx->runtime);
+ if (!ok)
return JS_FALSE;
}
return js_SetLengthProperty(cx, obj, index);
@@ -981,7 +1096,7 @@ array_shift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
jsuint length, i;
jsid id, id2;
- jsval v, junk;
+ jsval junk;
if (!js_GetLengthProperty(cx, obj, &length))
return JS_FALSE;
@@ -1000,16 +1115,23 @@ array_shift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
for (i = 1; i <= length; i++) {
if (!IndexToId(cx, i, &id))
return JS_FALSE;
- if (!IndexToId(cx, i - 1, &id2))
+ if (!OBJ_GET_PROPERTY(cx, obj, id, &argv[0]))
return JS_FALSE;
- if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
+
+ /* Get id after value to avoid nested GC hazards. */
+ if (!IndexToId(cx, i - 1, &id2))
return JS_FALSE;
- if (!OBJ_SET_PROPERTY(cx, obj, id2, &v))
+ if (!OBJ_SET_PROPERTY(cx, obj, id2, &argv[0]))
return JS_FALSE;
}
}
- /* Delete the only or last element. */
+ /*
+ * Delete the only or the last element. We recreate id when it is an
+ * atom to protect against a nested GC during the last iteration.
+ */
+ if (length > JSVAL_INT_MAX && !IndexToId(cx, length, &id))
+ return JS_FALSE;
if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk))
return JS_FALSE;
}
@@ -1023,11 +1145,7 @@ array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsuint length, last;
uintN i;
jsid id, id2;
- jsval v;
-#if JS_HAS_SPARSE_ARRAYS
- JSObject *obj2;
- JSProperty *prop;
-#endif
+ jsval *vp, junk;
if (!js_GetLengthProperty(cx, obj, &length))
return JS_FALSE;
@@ -1035,24 +1153,25 @@ array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
/* Slide up the array to make room for argc at the bottom. */
if (length > 0) {
last = length;
+ vp = argv + argc;
while (last--) {
- if (!IndexToId(cx, last, &id))
+ if (!IndexToExistingId(cx, obj, last, &id))
return JS_FALSE;
+ if (id != JSID_HOLE) {
+ if (!OBJ_GET_PROPERTY(cx, obj, id, vp))
+ return JS_FALSE;
+ }
+
+ /* Get id after value to avoid nested GC hazards. */
if (!IndexToId(cx, last + argc, &id2))
return JS_FALSE;
-#if JS_HAS_SPARSE_ARRAYS
- if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
- return JS_FALSE;
- if (!prop) {
- OBJ_DELETE_PROPERTY(cx, obj, id2, &v); /* v is junk. */
- continue;
+ if (id == JSID_HOLE) {
+ if (!OBJ_DELETE_PROPERTY(cx, obj, id2, &junk))
+ return JS_FALSE;
+ } else {
+ if (!OBJ_SET_PROPERTY(cx, obj, id2, vp))
+ return JS_FALSE;
}
- OBJ_DROP_PROPERTY(cx, obj2, prop);
-#endif
- if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
- return JS_FALSE;
- if (!OBJ_SET_PROPERTY(cx, obj, id2, &v))
- return JS_FALSE;
}
}
@@ -1075,16 +1194,20 @@ array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
static JSBool
array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
+ jsval *vp, junk;
jsuint length, begin, end, count, delta, last;
- uintN i;
jsdouble d;
jsid id, id2;
- jsval v;
JSObject *obj2;
+ uintN i;
- /* Nothing to do if no args. Otherwise lock and load length. */
+ /*
+ * Nothing to do if no args. Otherwise point vp at our one explicit local
+ * root and get length.
+ */
if (argc == 0)
return JS_TRUE;
+ vp = argv + argc;
if (!js_GetLengthProperty(cx, obj, &length))
return JS_FALSE;
@@ -1122,7 +1245,7 @@ array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
argv++;
}
- if (count == 1 && cx->version == JSVERSION_1_2) {
+ if (count == 1 && JS_VERSION_IS_1_2(cx)) {
/*
* JS lacks "list context", whereby in Perl one turns the single
* scalar that's spliced out into an array just by assigning it to
@@ -1139,7 +1262,7 @@ array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
if (!OBJ_GET_PROPERTY(cx, obj, id, rval))
return JS_FALSE;
} else {
- if (cx->version != JSVERSION_1_2 || count > 0) {
+ if (!JS_VERSION_IS_1_2(cx) || count > 0) {
/*
* Create a new array value to return. Our ECMA v2 proposal specs
* that splice always returns an array value, even when given no
@@ -1154,15 +1277,22 @@ array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
/* If there are elements to remove, put them into the return value. */
if (count > 0) {
for (last = begin; last < end; last++) {
- if (!IndexToId(cx, last, &id))
+ if (!IndexToExistingId(cx, obj, last, &id))
return JS_FALSE;
- if (!IndexToId(cx, last - begin, &id2))
+ if (id == JSID_HOLE)
+ continue; /* don't fill holes in the new array */
+ if (!OBJ_GET_PROPERTY(cx, obj, id, vp))
return JS_FALSE;
- if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
+
+ /* Get id after value to avoid nested GC hazards. */
+ if (!IndexToId(cx, last - begin, &id2))
return JS_FALSE;
- if (!OBJ_SET_PROPERTY(cx, obj2, id2, &v))
+ if (!OBJ_SET_PROPERTY(cx, obj2, id2, vp))
return JS_FALSE;
}
+
+ if (!js_SetLengthProperty(cx, obj2, end - begin))
+ return JS_FALSE;
}
}
}
@@ -1173,27 +1303,45 @@ array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
last = length;
/* (uint) end could be 0, so can't use vanilla >= test */
while (last-- > end) {
- if (!IndexToId(cx, last, &id))
+ if (!IndexToExistingId(cx, obj, last, &id))
return JS_FALSE;
+ if (id != JSID_HOLE) {
+ if (!OBJ_GET_PROPERTY(cx, obj, id, vp))
+ return JS_FALSE;
+ }
+
+ /* Get id after value to avoid nested GC hazards. */
if (!IndexToId(cx, last + delta, &id2))
return JS_FALSE;
- if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
- return JS_FALSE;
- if (!OBJ_SET_PROPERTY(cx, obj, id2, &v))
- return JS_FALSE;
+ if (id != JSID_HOLE) {
+ if (!OBJ_SET_PROPERTY(cx, obj, id2, vp))
+ return JS_FALSE;
+ } else {
+ if (!OBJ_DELETE_PROPERTY(cx, obj, id2, &junk))
+ return JS_FALSE;
+ }
}
length += delta;
} else if (argc < count) {
delta = count - (jsuint)argc;
for (last = end; last < length; last++) {
- if (!IndexToId(cx, last, &id))
+ if (!IndexToExistingId(cx, obj, last, &id))
return JS_FALSE;
+ if (id != JSID_HOLE) {
+ if (!OBJ_GET_PROPERTY(cx, obj, id, vp))
+ return JS_FALSE;
+ }
+
+ /* Get id after value to avoid nested GC hazards. */
if (!IndexToId(cx, last - delta, &id2))
return JS_FALSE;
- if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
- return JS_FALSE;
- if (!OBJ_SET_PROPERTY(cx, obj, id2, &v))
- return JS_FALSE;
+ if (id != JSID_HOLE) {
+ if (!OBJ_SET_PROPERTY(cx, obj, id2, vp))
+ return JS_FALSE;
+ } else {
+ if (!OBJ_DELETE_PROPERTY(cx, obj, id2, &junk))
+ return JS_FALSE;
+ }
}
length -= delta;
}
@@ -1218,12 +1366,15 @@ array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
static JSBool
array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
+ jsval *vp, v;
JSObject *nobj, *aobj;
jsuint length, alength, slot;
uintN i;
- jsval v;
jsid id, id2;
+ /* Hoist the explicit local root address computation. */
+ vp = argv + argc;
+
/* Treat obj as the first argument; see ECMA 15.4.4.4. */
--argv;
JS_ASSERT(obj == JSVAL_TO_OBJECT(argv[0]));
@@ -1242,20 +1393,30 @@ array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
aobj = JSVAL_TO_OBJECT(v);
if (aobj && OBJ_GET_CLASS(cx, aobj) == &js_ArrayClass) {
if (!OBJ_GET_PROPERTY(cx, aobj,
- (jsid)cx->runtime->atomState.lengthAtom,
- &v)) {
+ ATOM_TO_JSID(cx->runtime->atomState
+ .lengthAtom),
+ vp)) {
return JS_FALSE;
}
- if (!ValueIsLength(cx, v, &alength))
+ if (!ValueIsLength(cx, *vp, &alength))
return JS_FALSE;
for (slot = 0; slot < alength; slot++) {
- if (!IndexToId(cx, slot, &id))
+ if (!IndexToExistingId(cx, aobj, slot, &id))
return JS_FALSE;
- if (!IndexToId(cx, length + slot, &id2))
+ if (id == JSID_HOLE) {
+ /*
+ * Per ECMA 262, 15.4.4.4, step 9, ignore non-existent
+ * properties.
+ */
+ continue;
+ }
+ if (!OBJ_GET_PROPERTY(cx, aobj, id, vp))
return JS_FALSE;
- if (!OBJ_GET_PROPERTY(cx, aobj, id, &v))
+
+ /* Get id after value to avoid nested GC hazards. */
+ if (!IndexToId(cx, length + slot, &id2))
return JS_FALSE;
- if (!OBJ_SET_PROPERTY(cx, nobj, id2, &v))
+ if (!OBJ_SET_PROPERTY(cx, nobj, id2, vp))
return JS_FALSE;
}
length += alength;
@@ -1263,28 +1424,34 @@ array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
}
}
+ *vp = v;
if (!IndexToId(cx, length, &id))
return JS_FALSE;
- if (!OBJ_SET_PROPERTY(cx, nobj, id, &v))
+ if (!OBJ_SET_PROPERTY(cx, nobj, id, vp))
return JS_FALSE;
length++;
}
- return JS_TRUE;
+ return js_SetLengthProperty(cx, nobj, length);
}
static JSBool
array_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
+ jsval *vp;
JSObject *nobj;
jsuint length, begin, end, slot;
jsdouble d;
jsid id, id2;
- jsval v;
+ /* Hoist the explicit local root address computation. */
+ vp = argv + argc;
+
+ /* Create a new Array object and store it in the rval local root. */
nobj = js_NewArrayObject(cx, 0, NULL);
if (!nobj)
return JS_FALSE;
+ *rval = OBJECT_TO_JSVAL(nobj);
if (!js_GetLengthProperty(cx, obj, &length))
return JS_FALSE;
@@ -1319,20 +1486,316 @@ array_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
}
}
+ if (begin > end)
+ begin = end;
+
for (slot = begin; slot < end; slot++) {
- if (!IndexToId(cx, slot, &id))
+ if (!IndexToExistingId(cx, obj, slot, &id))
+ return JS_FALSE;
+ if (id == JSID_HOLE)
+ continue;
+ if (!OBJ_GET_PROPERTY(cx, obj, id, vp))
return JS_FALSE;
+
+ /* Get id after value to avoid nested GC hazards. */
if (!IndexToId(cx, slot - begin, &id2))
return JS_FALSE;
- if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
+ if (!OBJ_SET_PROPERTY(cx, nobj, id2, vp))
return JS_FALSE;
- if (!OBJ_SET_PROPERTY(cx, nobj, id2, &v))
+ }
+ return js_SetLengthProperty(cx, nobj, end - begin);
+}
+#endif /* JS_HAS_SEQUENCE_OPS */
+
+#if JS_HAS_ARRAY_EXTRAS
+
+static JSBool
+array_indexOfHelper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+ jsval *rval, JSBool isLast)
+{
+ jsuint length, i, stop;
+ jsint direction;
+
+ if (!js_GetLengthProperty(cx, obj, &length))
+ return JS_FALSE;
+ if (length == 0)
+ goto not_found;
+
+ if (argc <= 1) {
+ i = isLast ? length - 1 : 0;
+ } else {
+ jsdouble start;
+
+ if (!js_ValueToNumber(cx, argv[1], &start))
return JS_FALSE;
+ start = js_DoubleToInteger(start);
+ if (start < 0) {
+ start += length;
+ i = (start < 0) ? 0 : (jsuint)start;
+ } else if (start >= length) {
+ i = length - 1;
+ } else {
+ i = (jsuint)start;
+ }
}
- *rval = OBJECT_TO_JSVAL(nobj);
+
+ if (isLast) {
+ stop = 0;
+ direction = -1;
+ } else {
+ stop = length - 1;
+ direction = 1;
+ }
+
+ for (;;) {
+ jsid id;
+ jsval v;
+
+ if (!IndexToExistingId(cx, obj, (jsuint)i, &id))
+ return JS_FALSE;
+ if (id != JSID_HOLE) {
+ if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
+ return JS_FALSE;
+ if (js_StrictlyEqual(v, argv[0]))
+ return js_NewNumberValue(cx, i, rval);
+ }
+
+ if (i == stop)
+ goto not_found;
+ i += direction;
+ }
+
+ not_found:
+ *rval = INT_TO_JSVAL(-1);
return JS_TRUE;
}
-#endif /* JS_HAS_SEQUENCE_OPS */
+
+static JSBool
+array_indexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+ jsval *rval)
+{
+ return array_indexOfHelper(cx, obj, argc, argv, rval, JS_FALSE);
+}
+
+static JSBool
+array_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+ jsval *rval)
+{
+ return array_indexOfHelper(cx, obj, argc, argv, rval, JS_TRUE);
+}
+
+/* Order is important; extras that use a caller's predicate must follow MAP. */
+typedef enum ArrayExtraMode {
+ FOREACH,
+ MAP,
+ FILTER,
+ SOME,
+ EVERY
+} ArrayExtraMode;
+
+static JSBool
+array_extra(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval,
+ ArrayExtraMode mode)
+{
+ jsval *vp, *sp, *origsp, *oldsp;
+ jsuint length, newlen, i;
+ JSObject *callable, *thisp, *newarr;
+ void *mark;
+ JSStackFrame *fp;
+ JSBool ok, b;
+
+ /* Hoist the explicit local root address computation. */
+ vp = argv + argc;
+
+ if (!js_GetLengthProperty(cx, obj, &length))
+ return JS_FALSE;
+
+ /*
+ * First, get or compute our callee, so that we error out consistently
+ * when passed a non-callable object.
+ */
+ callable = js_ValueToCallableObject(cx, &argv[0], 0);
+ if (!callable)
+ return JS_FALSE;
+
+ /*
+ * Set our initial return condition, used for zero-length array cases
+ * (and pre-size our map return to match our known length, for all cases).
+ */
+#ifdef __GNUC__ /* quell GCC overwarning */
+ newlen = 0;
+ newarr = NULL;
+ ok = JS_TRUE;
+#endif
+ switch (mode) {
+ case MAP:
+ case FILTER:
+ newlen = (mode == MAP) ? length : 0;
+ newarr = js_NewArrayObject(cx, newlen, NULL);
+ if (!newarr)
+ return JS_FALSE;
+ *rval = OBJECT_TO_JSVAL(newarr);
+ break;
+ case SOME:
+ *rval = JSVAL_FALSE;
+ break;
+ case EVERY:
+ *rval = JSVAL_TRUE;
+ break;
+ case FOREACH:
+ break;
+ }
+
+ if (length == 0)
+ return JS_TRUE;
+
+ if (argc > 1) {
+ if (!js_ValueToObject(cx, argv[1], &thisp))
+ return JS_FALSE;
+ argv[1] = OBJECT_TO_JSVAL(thisp);
+ } else {
+ thisp = NULL;
+ }
+
+ /* We call with 3 args (value, index, array), plus room for rval. */
+ origsp = js_AllocStack(cx, 2 + 3 + 1, &mark);
+ if (!origsp)
+ return JS_FALSE;
+
+ /* Lift current frame to include our args. */
+ fp = cx->fp;
+ oldsp = fp->sp;
+
+ for (i = 0; i < length; i++) {
+ jsid id;
+ jsval rval2;
+
+ ok = IndexToExistingId(cx, obj, i, &id);
+ if (!ok)
+ break;
+ if (id == JSID_HOLE)
+ continue;
+ ok = OBJ_GET_PROPERTY(cx, obj, id, vp);
+ if (!ok)
+ break;
+
+ /*
+ * Push callable and 'this', then args. We must do this for every
+ * iteration around the loop since js_Invoke uses origsp[0] for rval
+ * storage and some native functions use origsp[1] for local rooting.
+ */
+ sp = origsp;
+ *sp++ = OBJECT_TO_JSVAL(callable);
+ *sp++ = OBJECT_TO_JSVAL(thisp);
+ *sp++ = *vp;
+ *sp++ = INT_TO_JSVAL(i);
+ *sp++ = OBJECT_TO_JSVAL(obj);
+
+ /* Do the call. */
+ fp->sp = sp;
+ ok = js_Invoke(cx, 3, JSINVOKE_INTERNAL);
+ rval2 = fp->sp[-1];
+ fp->sp = oldsp;
+ if (!ok)
+ break;
+
+ if (mode > MAP) {
+ if (rval2 == JSVAL_NULL) {
+ b = JS_FALSE;
+ } else if (JSVAL_IS_BOOLEAN(rval2)) {
+ b = JSVAL_TO_BOOLEAN(rval2);
+ } else {
+ ok = js_ValueToBoolean(cx, rval2, &b);
+ if (!ok)
+ goto out;
+ }
+ }
+
+ switch (mode) {
+ case FOREACH:
+ break;
+ case MAP:
+ /*
+ * We reconstruct id once again when it is a GC thing since scripts
+ * can trigger GC that collects it. See bug 341956.
+ */
+ if (i > JSVAL_INT_MAX) {
+ ok = IndexToId(cx, i, &id);
+ if (!ok)
+ goto out;
+ }
+ ok = OBJ_SET_PROPERTY(cx, newarr, id, &rval2);
+ if (!ok)
+ goto out;
+ break;
+ case FILTER:
+ if (!b)
+ break;
+ /* Filter passed *vp, push as result. */
+ ok = IndexToId(cx, newlen++, &id);
+ if (!ok)
+ goto out;
+ ok = OBJ_SET_PROPERTY(cx, newarr, id, vp);
+ if (!ok)
+ goto out;
+ break;
+ case SOME:
+ if (b) {
+ *rval = JSVAL_TRUE;
+ goto out;
+ }
+ break;
+ case EVERY:
+ if (!b) {
+ *rval = JSVAL_FALSE;
+ goto out;
+ }
+ break;
+ }
+ }
+
+ out:
+ js_FreeStack(cx, mark);
+ if (ok && mode == FILTER)
+ ok = js_SetLengthProperty(cx, newarr, newlen);
+ return ok;
+}
+
+static JSBool
+array_forEach(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+ jsval *rval)
+{
+ return array_extra(cx, obj, argc, argv, rval, FOREACH);
+}
+
+static JSBool
+array_map(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+ jsval *rval)
+{
+ return array_extra(cx, obj, argc, argv, rval, MAP);
+}
+
+static JSBool
+array_filter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+ jsval *rval)
+{
+ return array_extra(cx, obj, argc, argv, rval, FILTER);
+}
+
+static JSBool
+array_some(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+ jsval *rval)
+{
+ return array_extra(cx, obj, argc, argv, rval, SOME);
+}
+
+static JSBool
+array_every(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+ jsval *rval)
+{
+ return array_extra(cx, obj, argc, argv, rval, EVERY);
+}
+#endif
static JSFunctionSpec array_methods[] = {
#if JS_HAS_TOSOURCE
@@ -1343,22 +1806,32 @@ static JSFunctionSpec array_methods[] = {
/* Perl-ish methods. */
#if JS_HAS_SOME_PERL_FUN
- {"join", array_join, 1,0,0},
- {"reverse", array_reverse, 0,0,0},
- {"sort", array_sort, 1,0,0},
+ {"join", array_join, 1,JSFUN_GENERIC_NATIVE,0},
+ {"reverse", array_reverse, 0,JSFUN_GENERIC_NATIVE,2},
+ {"sort", array_sort, 1,JSFUN_GENERIC_NATIVE,2},
#endif
#if JS_HAS_MORE_PERL_FUN
- {"push", array_push, 1,0,0},
- {"pop", array_pop, 0,0,0},
- {"shift", array_shift, 0,0,0},
- {"unshift", array_unshift, 1,0,0},
- {"splice", array_splice, 1,0,0},
+ {"push", array_push, 1,JSFUN_GENERIC_NATIVE,0},
+ {"pop", array_pop, 0,JSFUN_GENERIC_NATIVE,0},
+ {"shift", array_shift, 0,JSFUN_GENERIC_NATIVE,1},
+ {"unshift", array_unshift, 1,JSFUN_GENERIC_NATIVE,1},
+ {"splice", array_splice, 2,JSFUN_GENERIC_NATIVE,1},
#endif
/* Python-esque sequence methods. */
#if JS_HAS_SEQUENCE_OPS
- {"concat", array_concat, 0,0,0},
- {"slice", array_slice, 0,0,0},
+ {"concat", array_concat, 1,JSFUN_GENERIC_NATIVE,1},
+ {"slice", array_slice, 2,JSFUN_GENERIC_NATIVE,1},
+#endif
+
+#if JS_HAS_ARRAY_EXTRAS
+ {"indexOf", array_indexOf, 1,JSFUN_GENERIC_NATIVE,0},
+ {"lastIndexOf", array_lastIndexOf, 1,JSFUN_GENERIC_NATIVE,0},
+ {"forEach", array_forEach, 1,JSFUN_GENERIC_NATIVE,1},
+ {"map", array_map, 1,JSFUN_GENERIC_NATIVE,1},
+ {"filter", array_filter, 1,JSFUN_GENERIC_NATIVE,1},
+ {"some", array_some, 1,JSFUN_GENERIC_NATIVE,1},
+ {"every", array_every, 1,JSFUN_GENERIC_NATIVE,1},
#endif
{0,0,0,0,0}
@@ -1381,7 +1854,7 @@ Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
if (argc == 0) {
length = 0;
vector = NULL;
- } else if (cx->version == JSVERSION_1_2) {
+ } else if (JS_VERSION_IS_1_2(cx)) {
length = (jsuint) argc;
vector = argv;
} else if (argc > 1) {