This commit is contained in:
Jeff Walden
2010-03-26 21:53:40 -07:00
50 changed files with 1339 additions and 992 deletions

View File

@@ -231,7 +231,7 @@ js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
return JS_TRUE;
}
AutoValueRooter tvr(cx, JSVAL_NULL);
JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), tvr.addr()))
return JS_FALSE;
@@ -405,7 +405,7 @@ EnsureCapacity(JSContext *cx, JSObject *obj, uint32 newcap,
static bool
ReallyBigIndexToId(JSContext* cx, jsdouble index, jsid* idp)
{
AutoValueRooter dval(cx);
JSAutoTempValueRooter dval(cx);
if (!js_NewDoubleInRootedValue(cx, index, dval.addr()) ||
!js_ValueToStringId(cx, dval.value(), idp)) {
return JS_FALSE;
@@ -450,7 +450,7 @@ GetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole,
return JS_TRUE;
}
AutoIdRooter idr(cx);
JSAutoTempIdRooter idr(cx);
*hole = JS_FALSE;
if (!IndexToId(cx, obj, index, hole, idr.addr()))
@@ -505,7 +505,7 @@ SetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, jsval v)
return JS_FALSE;
}
AutoIdRooter idr(cx);
JSAutoTempIdRooter idr(cx);
if (!IndexToId(cx, obj, index, NULL, idr.addr(), JS_TRUE))
return JS_FALSE;
@@ -531,7 +531,7 @@ DeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index)
return JS_TRUE;
}
AutoIdRooter idr(cx);
JSAutoTempIdRooter idr(cx);
if (!IndexToId(cx, obj, index, NULL, idr.addr()))
return JS_FALSE;
@@ -573,7 +573,7 @@ JSBool
js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
{
JSErrorReporter older = JS_SetErrorReporter(cx, NULL);
AutoValueRooter tvr(cx, JSVAL_NULL);
JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
jsid id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
JSBool ok = obj->getProperty(cx, id, tvr.addr());
JS_SetErrorReporter(cx, older);
@@ -626,6 +626,8 @@ array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
jsuint newlen, oldlen, gap, index;
jsval junk;
JSTempValueRooter tvr;
JSBool ok;
if (!obj->isArray()) {
jsid lengthId = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
@@ -673,19 +675,24 @@ array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
return false;
/* Protect iter against GC under JSObject::deleteProperty. */
AutoValueRooter tvr(cx, iter);
JS_PUSH_TEMP_ROOT_OBJECT(cx, iter, &tvr);
gap = oldlen - newlen;
for (;;) {
if (!JS_CHECK_OPERATION_LIMIT(cx) || !JS_NextProperty(cx, iter, &id))
return false;
ok = (JS_CHECK_OPERATION_LIMIT(cx) &&
JS_NextProperty(cx, iter, &id));
if (!ok)
break;
if (JSVAL_IS_VOID(id))
break;
if (js_IdIsIndex(id, &index) && index - newlen < gap &&
!obj->deleteProperty(cx, id, &junk)) {
return false;
if (js_IdIsIndex(id, &index) && index - newlen < gap) {
ok = obj->deleteProperty(cx, id, &junk);
if (!ok)
break;
}
}
JS_POP_TEMP_ROOT(cx, &tvr);
if (!ok)
return false;
}
obj->fslots[JSSLOT_ARRAY_LENGTH] = newlen;
@@ -1508,7 +1515,7 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
return true;
}
AutoValueRooter tvr(cx, obj);
JSAutoTempValueRooter tvr(cx, obj);
/* After this point, all paths exit through the 'out' label. */
MUST_FLOW_THROUGH("out");
@@ -1642,7 +1649,7 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, jsva
#ifdef DEBUG_jwalden
{
/* Verify that overwriteType and writeType were accurate. */
AutoIdRooter idr(cx);
JSAutoTempIdRooter idr(cx, JSVAL_ZERO);
for (jsuint i = 0; i < count; i++) {
JS_ASSERT_IF(vectorType == SourceVectorAllValues, vector[i] != JSVAL_HOLE);
@@ -1708,12 +1715,12 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, jsva
JS_ASSERT(start == MAXINDEX);
jsval tmp[2] = {JSVAL_NULL, JSVAL_NULL};
AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(tmp), tmp);
JSAutoTempValueRooter tvr(cx, JS_ARRAY_LENGTH(tmp), tmp);
if (!js_NewDoubleInRootedValue(cx, MAXINDEX, &tmp[0]))
return JS_FALSE;
jsdouble *dp = JSVAL_TO_DOUBLE(tmp[0]);
JS_ASSERT(*dp == MAXINDEX);
AutoIdRooter idr(cx);
JSAutoTempIdRooter idr(cx);
do {
tmp[1] = *vector++;
if (!js_ValueToStringId(cx, tmp[0], idr.addr()) ||
@@ -1759,7 +1766,7 @@ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector,
static JSString* FASTCALL
Array_p_join(JSContext* cx, JSObject* obj, JSString *str)
{
AutoValueRooter tvr(cx);
JSAutoTempValueRooter tvr(cx);
if (!array_toString_sub(cx, obj, JS_FALSE, str, tvr.addr())) {
SetBuiltinError(cx);
return NULL;
@@ -1770,7 +1777,7 @@ Array_p_join(JSContext* cx, JSObject* obj, JSString *str)
static JSString* FASTCALL
Array_p_toString(JSContext* cx, JSObject* obj)
{
AutoValueRooter tvr(cx);
JSAutoTempValueRooter tvr(cx);
if (!array_toString_sub(cx, obj, JS_FALSE, NULL, tvr.addr())) {
SetBuiltinError(cx);
return NULL;
@@ -1842,7 +1849,7 @@ array_reverse(JSContext *cx, uintN argc, jsval *vp)
return JS_TRUE;
}
AutoValueRooter tvr(cx);
JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
for (jsuint i = 0, half = len / 2; i < half; i++) {
JSBool hole, hole2;
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
@@ -2092,28 +2099,40 @@ JS_STATIC_ASSERT(JSVAL_NULL == 0);
static JSBool
array_sort(JSContext *cx, uintN argc, jsval *vp)
{
jsval fval;
jsval *argv, fval, *vec, *mergesort_tmp, v;
JSObject *obj;
CompareArgs ca;
jsuint len, newlen, i, undefs;
JSTempValueRooter tvr;
JSBool hole;
JSBool ok;
size_t elemsize;
JSString *str;
jsval *argv = JS_ARGV(cx, vp);
/*
* Optimize the default compare function case if all of obj's elements
* have values of type string.
*/
JSBool all_strings;
argv = JS_ARGV(cx, vp);
if (argc > 0) {
if (JSVAL_IS_PRIMITIVE(argv[0])) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SORT_ARG);
return false;
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_SORT_ARG);
return JS_FALSE;
}
fval = argv[0]; /* non-default compare function */
} else {
fval = JSVAL_NULL;
}
JSObject *obj = JS_THIS_OBJECT(cx, vp);
obj = JS_THIS_OBJECT(cx, vp);
if (!obj || !js_GetLengthProperty(cx, obj, &len))
return false;
return JS_FALSE;
if (len == 0) {
*vp = OBJECT_TO_JSVAL(obj);
return true;
return JS_TRUE;
}
/*
@@ -2123,16 +2142,19 @@ array_sort(JSContext *cx, uintN argc, jsval *vp)
* malloc'd vector.
*/
#if JS_BITS_PER_WORD == 32
if (size_t(len) > size_t(-1) / (2 * sizeof(jsval))) {
if ((size_t)len > ~(size_t)0 / (2 * sizeof(jsval))) {
js_ReportAllocationOverflow(cx);
return false;
return JS_FALSE;
}
#endif
vec = (jsval *) cx->malloc(2 * (size_t) len * sizeof(jsval));
if (!vec)
return JS_FALSE;
/*
* Initialize vec as a root. We will clear elements of vec one by
* one while increasing the rooted amount of vec when we know that the
* property at the corresponding index exists and its value must be rooted.
* one while increasing tvr.count when we know that the property at
* the corresponding index exists and its value must be rooted.
*
* In this way when sorting a huge mostly sparse array we will not
* access the tail of vec corresponding to properties that do not
@@ -2140,196 +2162,204 @@ array_sort(JSContext *cx, uintN argc, jsval *vp)
*
* After this point control must flow through label out: to exit.
*/
{
jsval *vec = (jsval *) cx->malloc(2 * size_t(len) * sizeof(jsval));
if (!vec)
return false;
JS_PUSH_TEMP_ROOT(cx, 0, vec, &tvr);
struct AutoFreeVector {
AutoFreeVector(JSContext *cx, jsval *&vec) : cx(cx), vec(vec) { }
~AutoFreeVector() {
cx->free(vec);
}
JSContext * const cx;
jsval *&vec;
} free(cx, vec);
/*
* By ECMA 262, 15.4.4.11, a property that does not exist (which we
* call a "hole") is always greater than an existing property with
* value undefined and that is always greater than any other property.
* Thus to sort holes and undefs we simply count them, sort the rest
* of elements, append undefs after them and then make holes after
* undefs.
*/
undefs = 0;
newlen = 0;
all_strings = JS_TRUE;
for (i = 0; i < len; i++) {
ok = JS_CHECK_OPERATION_LIMIT(cx);
if (!ok)
goto out;
AutoArrayRooter tvr(cx, 0, vec);
/* Clear vec[newlen] before including it in the rooted set. */
vec[newlen] = JSVAL_NULL;
tvr.count = newlen + 1;
ok = GetArrayElement(cx, obj, i, &hole, &vec[newlen]);
if (!ok)
goto out;
/*
* By ECMA 262, 15.4.4.11, a property that does not exist (which we
* call a "hole") is always greater than an existing property with
* value undefined and that is always greater than any other property.
* Thus to sort holes and undefs we simply count them, sort the rest
* of elements, append undefs after them and then make holes after
* undefs.
*/
undefs = 0;
newlen = 0;
bool allStrings = true;
for (i = 0; i < len; i++) {
if (!JS_CHECK_OPERATION_LIMIT(cx))
return false;
if (hole)
continue;
/* Clear vec[newlen] before including it in the rooted set. */
JSBool hole;
vec[newlen] = JSVAL_NULL;
tvr.changeLength(newlen + 1);
if (!GetArrayElement(cx, obj, i, &hole, &vec[newlen]))
return false;
if (hole)
continue;
if (JSVAL_IS_VOID(vec[newlen])) {
++undefs;
continue;
}
allStrings = allStrings && JSVAL_IS_STRING(vec[newlen]);
++newlen;
if (JSVAL_IS_VOID(vec[newlen])) {
++undefs;
continue;
}
if (newlen == 0)
return true; /* The array has only holes and undefs. */
/* We know JSVAL_IS_STRING yields 0 or 1, so avoid a branch via &=. */
all_strings &= JSVAL_IS_STRING(vec[newlen]);
++newlen;
}
if (newlen == 0) {
/* The array has only holes and undefs. */
ok = JS_TRUE;
goto out;
}
/*
* The first newlen elements of vec are copied from the array object
* (above). The remaining newlen positions are used as GC-rooted scratch
* space for mergesort. We must clear the space before including it to
* the root set covered by tvr.count. We assume JSVAL_NULL==0 to optimize
* initialization using memset.
*/
mergesort_tmp = vec + newlen;
memset(mergesort_tmp, 0, newlen * sizeof(jsval));
tvr.count = newlen * 2;
/* Here len == 2 * (newlen + undefs + number_of_holes). */
if (fval == JSVAL_NULL) {
/*
* The first newlen elements of vec are copied from the array object
* (above). The remaining newlen positions are used as GC-rooted scratch
* space for mergesort. We must clear the space before including it to
* the root set covered by tvr.count. We assume JSVAL_NULL==0 to optimize
* initialization using memset.
* Sort using the default comparator converting all elements to
* strings.
*/
jsval *mergesort_tmp = vec + newlen;
PodZero(mergesort_tmp, newlen);
tvr.changeLength(newlen * 2);
/* Here len == 2 * (newlen + undefs + number_of_holes). */
if (fval == JSVAL_NULL) {
if (all_strings) {
elemsize = sizeof(jsval);
} else {
/*
* Sort using the default comparator converting all elements to
* strings.
* To avoid string conversion on each compare we do it only once
* prior to sorting. But we also need the space for the original
* values to recover the sorting result. To reuse
* sort_compare_strings we move the original values to the odd
* indexes in vec, put the string conversion results in the even
* indexes and pass 2 * sizeof(jsval) as an element size to the
* sorting function. In this way sort_compare_strings will only
* see the string values when it casts the compare arguments as
* pointers to jsval.
*
* This requires doubling the temporary storage including the
* scratch space for the merge sort. Since vec already contains
* the rooted scratch space for newlen elements at the tail, we
* can use it to rearrange and convert to strings first and try
* realloc only when we know that we successfully converted all
* the elements.
*/
if (allStrings) {
elemsize = sizeof(jsval);
} else {
/*
* To avoid string conversion on each compare we do it only once
* prior to sorting. But we also need the space for the original
* values to recover the sorting result. To reuse
* sort_compare_strings we move the original values to the odd
* indexes in vec, put the string conversion results in the even
* indexes and pass 2 * sizeof(jsval) as an element size to the
* sorting function. In this way sort_compare_strings will only
* see the string values when it casts the compare arguments as
* pointers to jsval.
*
* This requires doubling the temporary storage including the
* scratch space for the merge sort. Since vec already contains
* the rooted scratch space for newlen elements at the tail, we
* can use it to rearrange and convert to strings first and try
* realloc only when we know that we successfully converted all
* the elements.
*/
#if JS_BITS_PER_WORD == 32
if (size_t(newlen) > size_t(-1) / (4 * sizeof(jsval))) {
js_ReportAllocationOverflow(cx);
return false;
}
if ((size_t)newlen > ~(size_t)0 / (4 * sizeof(jsval))) {
js_ReportAllocationOverflow(cx);
ok = JS_FALSE;
goto out;
}
#endif
/*
* Rearrange and string-convert the elements of the vector from
* the tail here and, after sorting, move the results back
* starting from the start to prevent overwrite the existing
* elements.
*/
i = newlen;
do {
--i;
if (!JS_CHECK_OPERATION_LIMIT(cx))
return false;
jsval v = vec[i];
str = js_ValueToString(cx, v);
if (!str)
return false;
vec[2 * i] = STRING_TO_JSVAL(str);
vec[2 * i + 1] = v;
} while (i != 0);
JS_ASSERT(tvr.array == vec);
vec = (jsval *) cx->realloc(vec, 4 * size_t(newlen) * sizeof(jsval));
if (!vec) {
vec = tvr.array;
return false;
/*
* Rearrange and string-convert the elements of the vector from
* the tail here and, after sorting, move the results back
* starting from the start to prevent overwrite the existing
* elements.
*/
i = newlen;
do {
--i;
ok = JS_CHECK_OPERATION_LIMIT(cx);
if (!ok)
goto out;
v = vec[i];
str = js_ValueToString(cx, v);
if (!str) {
ok = JS_FALSE;
goto out;
}
mergesort_tmp = vec + 2 * newlen;
PodZero(mergesort_tmp, newlen * 2);
tvr.changeArray(vec, newlen * 4);
elemsize = 2 * sizeof(jsval);
}
if (!js_MergeSort(vec, size_t(newlen), elemsize,
sort_compare_strings, cx, mergesort_tmp)) {
return false;
}
if (!allStrings) {
/*
* We want to make the following loop fast and to unroot the
* cached results of toString invocations before the operation
* callback has a chance to run the GC. For this reason we do
* not call JS_CHECK_OPERATION_LIMIT in the loop.
*/
i = 0;
do {
vec[i] = vec[2 * i + 1];
} while (++i != newlen);
}
} else {
void *mark;
vec[2 * i] = STRING_TO_JSVAL(str);
vec[2 * i + 1] = v;
} while (i != 0);
LeaveTrace(cx);
CompareArgs ca;
ca.context = cx;
ca.fval = fval;
ca.elemroot = js_AllocStack(cx, 2 + 2, &mark);
if (!ca.elemroot)
return false;
bool ok = js_MergeSort(vec, size_t(newlen), sizeof(jsval),
comparator_stack_cast(sort_compare),
&ca, mergesort_tmp);
js_FreeStack(cx, mark);
if (!ok)
return false;
JS_ASSERT(tvr.u.array == vec);
vec = (jsval *) cx->realloc(vec,
4 * (size_t) newlen * sizeof(jsval));
if (!vec) {
vec = tvr.u.array;
ok = JS_FALSE;
goto out;
}
tvr.u.array = vec;
mergesort_tmp = vec + 2 * newlen;
PodZero(mergesort_tmp, newlen * 2);
tvr.count = newlen * 4;
elemsize = 2 * sizeof(jsval);
}
/*
* We no longer need to root the scratch space for the merge sort, so
* unroot it now to make the job of a potential GC under
* InitArrayElements easier.
*/
tvr.changeLength(newlen);
if (!InitArrayElements(cx, obj, 0, newlen, vec, TargetElementsMayContainValues,
SourceVectorAllValues)) {
return false;
ok = js_MergeSort(vec, (size_t) newlen, elemsize,
sort_compare_strings, cx, mergesort_tmp);
if (!ok)
goto out;
if (!all_strings) {
/*
* We want to make the following loop fast and to unroot the
* cached results of toString invocations before the operation
* callback has a chance to run the GC. For this reason we do
* not call JS_CHECK_OPERATION_LIMIT in the loop.
*/
i = 0;
do {
vec[i] = vec[2 * i + 1];
} while (++i != newlen);
}
} else {
void *mark;
LeaveTrace(cx);
ca.context = cx;
ca.fval = fval;
ca.elemroot = js_AllocStack(cx, 2 + 2, &mark);
if (!ca.elemroot) {
ok = JS_FALSE;
goto out;
}
ok = js_MergeSort(vec, (size_t) newlen, sizeof(jsval),
comparator_stack_cast(sort_compare),
&ca, mergesort_tmp);
js_FreeStack(cx, mark);
if (!ok)
goto out;
}
/*
* We no longer need to root the scratch space for the merge sort, so
* unroot it now to make the job of a potential GC under InitArrayElements
* easier.
*/
tvr.count = newlen;
ok = InitArrayElements(cx, obj, 0, newlen, vec, TargetElementsMayContainValues,
SourceVectorAllValues);
if (!ok)
goto out;
out:
JS_POP_TEMP_ROOT(cx, &tvr);
cx->free(vec);
if (!ok)
return JS_FALSE;
/* Set undefs that sorted after the rest of elements. */
while (undefs != 0) {
--undefs;
if (!JS_CHECK_OPERATION_LIMIT(cx) || !SetArrayElement(cx, obj, newlen++, JSVAL_VOID))
return false;
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
!SetArrayElement(cx, obj, newlen++, JSVAL_VOID)) {
return JS_FALSE;
}
}
/* Re-create any holes that sorted to the end of the array. */
while (len > newlen) {
if (!JS_CHECK_OPERATION_LIMIT(cx) || !DeleteArrayElement(cx, obj, --len))
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
!DeleteArrayElement(cx, obj, --len)) {
return JS_FALSE;
}
}
*vp = OBJECT_TO_JSVAL(obj);
return true;
return JS_TRUE;
}
/*
@@ -2403,7 +2433,7 @@ JS_DEFINE_CALLINFO_3(extern, BOOL, js_ArrayCompPush, CONTEXT, OBJECT, JSVAL, 0,
static jsval FASTCALL
Array_p_push1(JSContext* cx, JSObject* obj, jsval v)
{
AutoValueRooter tvr(cx, v);
JSAutoTempValueRooter tvr(cx, v);
if (obj->isDenseArray()
? array_push1_dense(cx, obj, v, tvr.addr())
: array_push_slowly(cx, obj, 1, tvr.addr(), tvr.addr())) {
@@ -2475,7 +2505,7 @@ array_pop_dense(JSContext *cx, JSObject* obj, jsval *vp)
static jsval FASTCALL
Array_p_pop(JSContext* cx, JSObject* obj)
{
AutoValueRooter tvr(cx);
JSAutoTempValueRooter tvr(cx);
if (obj->isDenseArray()
? array_pop_dense(cx, obj, tvr.addr())
: array_pop_slowly(cx, obj, tvr.addr())) {
@@ -2539,7 +2569,7 @@ array_shift(JSContext *cx, uintN argc, jsval *vp)
return JS_FALSE;
/* Slide down the array above the first element. */
AutoValueRooter tvr(cx);
JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
for (i = 0; i != length; i++) {
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
!GetArrayElement(cx, obj, i + 1, &hole, tvr.addr()) ||
@@ -2584,7 +2614,7 @@ array_unshift(JSContext *cx, uintN argc, jsval *vp)
} else {
last = length;
jsdouble upperIndex = last + argc;
AutoValueRooter tvr(cx);
JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
do {
--last, --upperIndex;
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
@@ -2674,7 +2704,7 @@ array_splice(JSContext *cx, uintN argc, jsval *vp)
argv++;
}
AutoValueRooter tvr(cx, JSVAL_NULL);
JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
/* If there are elements to remove, put them into the return value. */
if (count > 0) {
@@ -2813,7 +2843,7 @@ array_concat(JSContext *cx, uintN argc, jsval *vp)
length = 0;
}
AutoValueRooter tvr(cx, JSVAL_NULL);
JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
/* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */
for (i = 0; i <= argc; i++) {
@@ -2927,7 +2957,7 @@ array_slice(JSContext *cx, uintN argc, jsval *vp)
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(nobj);
AutoValueRooter tvr(cx);
JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
for (slot = begin; slot < end; slot++) {
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
!GetArrayElement(cx, obj, slot, &hole, tvr.addr())) {
@@ -3448,7 +3478,7 @@ js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector, JSBool holey)
JS_ASSERT(obj->getProto());
{
AutoValueRooter tvr(cx, obj);
JSAutoTempValueRooter tvr(cx, obj);
if (!InitArrayObject(cx, obj, length, vector, holey))
obj = NULL;
}
@@ -3567,7 +3597,7 @@ js_NewArrayObjectWithCapacity(JSContext *cx, jsuint capacity, jsval **vector)
if (!obj)
return NULL;
AutoValueRooter tvr(cx, obj);
JSAutoTempValueRooter tvr(cx, obj);
if (!EnsureCapacity(cx, obj, capacity, JS_FALSE))
obj = NULL;