diff options
| author | Bob Jamison <ishmalius@gmail.com> | 2007-03-05 10:34:59 +0000 |
|---|---|---|
| committer | ishmal <ishmal@users.sourceforge.net> | 2007-03-05 10:34:59 +0000 |
| commit | 33837efd4b94c4ebb80f95b3d9dbb6efd5499a98 (patch) | |
| tree | de482d7687b3bffa97cc78608ba75a8ad7e52b60 /src/dom/js/jsarray.c | |
| parent | Adding optional dialog preview. Implments RFE [ 1435276 ] switch preview on/o... (diff) | |
| download | inkscape-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.c | 955 |
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) { |
