Fix for bug 311583:

Not uneval(array) uses elision, not undefined, to represent array holes.
r=brendan


git-svn-id: svn://10.0.0.236/trunk@185000 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
igor%mir2.org 2005-11-20 01:12:58 +00:00
parent 1665c3e955
commit 957a3cea91

View File

@ -358,15 +358,27 @@ JSClass js_ArrayClass = {
JSCLASS_NO_OPTIONAL_MEMBERS
};
enum ArrayToSringOp {
TO_STRING,
TO_LOCALE_STRING,
TO_SOURCE
};
/*
* When op is TO_STRING or TO_LOCALE_STRING sep indicates a separator to use
* or "," when sep is NULL.
* When op is TO_SOURCE sep must be NULL.
*/
static JSBool
array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
jsval *rval, JSBool localeString)
array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToSringOp op,
JSString *sep, jsval *rval)
{
JSBool ok;
jsuint length, index;
jschar *chars, *ochars;
size_t nchars, growth, seplen, tmplen;
size_t nchars, growth, seplen, tmplen, extratail;
const jschar *sepstr;
jsid id;
JSString *str;
JSHashEntry *he;
JSObject *obj2;
@ -384,7 +396,7 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
he = js_EnterSharpObject(cx, obj, NULL, &chars);
if (!he)
return JS_FALSE;
if (literalize) {
if (op == TO_SOURCE) {
if (IS_SHARP(he)) {
#if JS_HAS_SHARP_VARS
nchars = js_strlen(chars);
@ -398,10 +410,11 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
}
/*
* Allocate 1 + 3 + 1 for "[", the worst-case closing ", ]", and the
* terminating 0.
* Always allocate 2 extra chars for closing ']' and terminating 0
* and then preallocate 1 + extratail to include starting '['.
*/
growth = (1 + 3 + 1) * sizeof(jschar);
extratail = 2;
growth = (1 + extratail) * sizeof(jschar);
if (!chars) {
nchars = 0;
chars = (jschar *) malloc(growth);
@ -418,17 +431,22 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
}
}
chars[nchars++] = '[';
JS_ASSERT(sep == NULL);
sepstr = NULL; /* indicates to use ", " as separator */
seplen = 2;
} else {
/*
* Free any sharp variable definition in chars. Normally, we would
* MAKE_SHARP(he) so that only the first sharp variable annotation is
* a definition, and all the rest are references, but in the current
* case of (!literalize), we don't need chars at all.
* case of (op != TO_SOURCE), we don't need chars at all.
*/
if (chars)
JS_free(cx, chars);
chars = NULL;
nchars = 0;
extratail = 1; /* allocate extra char for terminating 0 */
/* Return the empty string on a cycle as well as on empty join. */
if (IS_BUSY(he) || length == 0) {
@ -439,24 +457,56 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
/* Flag he as BUSY so we can distinguish a cycle from a join-point. */
MAKE_BUSY(he);
if (sep) {
sepstr = JSSTRING_CHARS(sep);
seplen = JSSTRING_LENGTH(sep);
} else {
sepstr = NULL; /* indicates to use "," as separator */
seplen = 1;
}
}
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 (op != TO_SOURCE) {
ok = JS_GetElement(cx, obj, index, &v);
} else {
ok = IndexToExistingId(cx, obj, index, &id);
if (!ok)
goto done;
if (id == JSID_HOLE) {
str = cx->runtime->emptyString;
/*
* For tail holes always append single "," and not ", "
* unless the version is JS1.2 where for extra compatibility
* the full ", " is added even in the tail case.
*/
if (index + 1 == length && !JS_VERSION_IS_1_2(cx))
seplen = 1;
goto got_str;
}
ok = OBJ_GET_PROPERTY(cx, obj, id, &v);
}
if (!ok)
goto done;
if ((!literalize || JS_VERSION_IS_1_2(cx)) &&
if ((op != TO_SOURCE || JS_VERSION_IS_1_2(cx)) &&
(JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v))) {
str = cx->runtime->emptyString;
if (op == TO_SOURCE) {
/*
* JS1.2 treats null and undefined in the same way as holes.
* It requires to add terminating ", " after empty string
* representing tail null or undefined.
*/
goto got_str;
}
} else {
if (localeString) {
if (op == TO_LOCALE_STRING) {
if (!js_ValueToObject(cx, v, &obj2) ||
!js_TryMethod(cx, obj2,
cx->runtime->atomState.toLocaleStringAtom,
@ -465,8 +515,11 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
} else {
str = js_ValueToString(cx, v);
}
} else if (op == TO_STRING) {
str = js_ValueToString(cx, v);
} else {
str = (literalize ? js_ValueToSource : js_ValueToString)(cx, v);
JS_ASSERT(op == TO_SOURCE);
str = js_ValueToSource(cx, v);
}
if (!str) {
ok = JS_FALSE;
@ -474,10 +527,14 @@ 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);
/* Do not append separator after the last element. */
if (index + 1 == length)
seplen = 0;
got_str:
/* Allocate 1 at end for closing bracket and zero. */
growth = (nchars + JSSTRING_LENGTH(str) + seplen + extratail)
* sizeof(jschar);
if (!chars) {
chars = (jschar *) malloc(growth);
if (!chars)
@ -490,26 +547,27 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
}
}
if (sepstr) {
js_strncpy(&chars[nchars], sepstr, seplen);
nchars += seplen;
}
sepstr = JSSTRING_CHARS(sep);
tmplen = JSSTRING_LENGTH(str);
js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen);
nchars += tmplen;
if (seplen) {
if (sepstr) {
js_strncpy(&chars[nchars], sepstr, seplen);
} else {
JS_ASSERT(seplen == 1 || seplen == 2);
chars[nchars] = ',';
if (seplen == 2)
chars[nchars + 1] = ' ';
}
nchars += seplen;
}
}
done:
if (literalize) {
if (chars) {
if (JSVAL_IS_VOID(v)) {
chars[nchars++] = ',';
chars[nchars++] = ' ';
}
if (op == TO_SOURCE) {
if (chars)
chars[nchars++] = ']';
}
} else {
CLEAR_BUSY(he);
}
@ -528,6 +586,7 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
return JS_FALSE;
}
chars[nchars] = 0;
JS_ASSERT((nchars + 1) * sizeof(jschar) == growth);
str = js_NewString(cx, chars, nchars, 0);
if (!str) {
free(chars);
@ -537,17 +596,12 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,
return JS_TRUE;
}
static jschar comma_space_ucstr[] = {',', ' ', 0};
static jschar comma_ucstr[] = {',', 0};
static JSString comma_space = {2, comma_space_ucstr};
static JSString comma = {1, comma_ucstr};
#if JS_HAS_TOSOURCE
static JSBool
array_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
return array_join_sub(cx, obj, &comma_space, JS_TRUE, rval, JS_FALSE);
return array_join_sub(cx, obj, TO_SOURCE, NULL, rval);
}
#endif
@ -555,15 +609,13 @@ static JSBool
array_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
JSBool literalize;
/*
* JS1.2 arrays convert to array literals, with a comma followed by a space
* between each element.
*/
literalize = JS_VERSION_IS_1_2(cx);
return array_join_sub(cx, obj, literalize ? &comma_space : &comma,
literalize, rval, JS_FALSE);
return array_join_sub(cx, obj,
(JS_VERSION_IS_1_2(cx) ? TO_SOURCE : TO_STRING),
NULL, rval);
}
static JSBool
@ -574,7 +626,7 @@ array_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
* Passing comma here as the separator. Need a way to get a
* locale-specific version.
*/
return array_join_sub(cx, obj, &comma, JS_FALSE, rval, JS_TRUE);
return array_join_sub(cx, obj, TO_LOCALE_STRING, NULL, rval);
}
static JSBool
@ -623,13 +675,15 @@ array_join(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSString *str;
if (JSVAL_IS_VOID(argv[0]))
return array_join_sub(cx, obj, &comma, JS_FALSE, rval, JS_FALSE);
str = js_ValueToString(cx, argv[0]);
if (!str)
return JS_FALSE;
argv[0] = STRING_TO_JSVAL(str);
return array_join_sub(cx, obj, str, JS_FALSE, rval, JS_FALSE);
if (JSVAL_IS_VOID(argv[0])) {
str = NULL;
} else {
str = js_ValueToString(cx, argv[0]);
if (!str)
return JS_FALSE;
argv[0] = STRING_TO_JSVAL(str);
}
return array_join_sub(cx, obj, TO_STRING, str, rval);
}
static JSBool