mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 04:27:37 +00:00
Backed out changeset e1587f23d2f0, bug 701560 - compilation errors
This commit is contained in:
parent
477ce89a9e
commit
223554f33c
170
js/src/ds/Sort.h
170
js/src/ds/Sort.h
@ -1,170 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=99 ft=cpp:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is SpiderMonkey JavaScript engine.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef Sort_h__
|
||||
#define Sort_h__
|
||||
|
||||
#include "jstypes.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename T>
|
||||
JS_ALWAYS_INLINE void
|
||||
CopyNonEmptyArray(T *dst, const T *src, size_t nelems)
|
||||
{
|
||||
JS_ASSERT(nelems != 0);
|
||||
const T *end = src + nelems;
|
||||
do {
|
||||
*dst++ = *src++;
|
||||
} while (src != end);
|
||||
}
|
||||
|
||||
/* Helper function for MergeSort. */
|
||||
template<typename T, typename Comparator>
|
||||
JS_ALWAYS_INLINE bool
|
||||
MergeArrayRuns(T *dst, const T *src, size_t run1, size_t run2, Comparator c)
|
||||
{
|
||||
JS_ASSERT(run1 >= 1);
|
||||
JS_ASSERT(run2 >= 1);
|
||||
|
||||
/* Copy runs already in sorted order. */
|
||||
const T *b = src + run1;
|
||||
bool lessOrEqual;
|
||||
if (!c(b[-1], b[0], &lessOrEqual))
|
||||
return false;
|
||||
|
||||
if (!lessOrEqual) {
|
||||
/* Runs are not already sorted, merge them. */
|
||||
for (const T *a = src;;) {
|
||||
if (!c(*a, *b, &lessOrEqual))
|
||||
return false;
|
||||
if (lessOrEqual) {
|
||||
*dst++ = *a++;
|
||||
if (!--run1) {
|
||||
src = b;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
*dst++ = *b++;
|
||||
if (!--run2) {
|
||||
src = a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CopyNonEmptyArray(dst, src, run1 + run2);
|
||||
return true;
|
||||
}
|
||||
|
||||
} /* namespace detail */
|
||||
|
||||
/*
|
||||
* Sort the array using the merge sort algorithm. The scratch should point to
|
||||
* a temporary storage that can hold nelems elements.
|
||||
*
|
||||
* The comparator must provide the () operator with the following signature:
|
||||
*
|
||||
* bool operator()(const T& a, const T& a, bool *lessOrEqualp);
|
||||
*
|
||||
* It should return true on success and sets lessOrEqualp to the result of
|
||||
* a <= b operation. If it returns false, the sort terminates immediately with
|
||||
* the false result. In this case the content of the array and scratch is
|
||||
* arbitrary.
|
||||
*/
|
||||
template<typename T, typename Comparator>
|
||||
bool
|
||||
MergeSort(T *array, size_t nelems, T *scratch, Comparator c)
|
||||
{
|
||||
const size_t INS_SORT_LIMIT = 3;
|
||||
|
||||
if (nelems <= 1)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Apply insertion sort to small chunks to reduce the number of merge
|
||||
* passes needed.
|
||||
*/
|
||||
for (size_t lo = 0; lo < nelems; lo += INS_SORT_LIMIT) {
|
||||
size_t hi = lo + INS_SORT_LIMIT;
|
||||
if (hi >= nelems)
|
||||
hi = nelems;
|
||||
for (size_t i = lo + 1; i != hi; i++) {
|
||||
for (size_t j = i; ;) {
|
||||
bool lessOrEqual;
|
||||
if (!c(array[j - 1], array[j], &lessOrEqual))
|
||||
return false;
|
||||
if (lessOrEqual)
|
||||
break;
|
||||
T tmp = array[j - 1];
|
||||
array[j - 1] = array[j];
|
||||
array[j] = tmp;
|
||||
if (--j == lo)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T *vec1 = array;
|
||||
T *vec2 = scratch;
|
||||
for (size_t run = INS_SORT_LIMIT; run < nelems; run *= 2) {
|
||||
for (size_t lo = 0; lo < nelems; lo += 2 * run) {
|
||||
size_t hi = lo + run;
|
||||
if (hi >= nelems) {
|
||||
detail::CopyNonEmptyArray(vec2 + lo, vec1 + lo, nelems - lo);
|
||||
break;
|
||||
}
|
||||
size_t run2 = (run <= nelems - hi) ? run : nelems - hi;
|
||||
if (!detail::MergeArrayRuns(vec2 + lo, vec1 + lo, run, run2, c))
|
||||
return false;
|
||||
}
|
||||
T *swap = vec1;
|
||||
vec1 = vec2;
|
||||
vec2 = swap;
|
||||
}
|
||||
if (vec1 == scratch)
|
||||
detail::CopyNonEmptyArray(array, scratch, nelems);
|
||||
return true;
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif
|
@ -131,8 +131,6 @@
|
||||
|
||||
#include "vm/ArgumentsObject.h"
|
||||
|
||||
#include "ds/Sort.h"
|
||||
|
||||
#include "jsarrayinlines.h"
|
||||
#include "jsatominlines.h"
|
||||
#include "jscntxtinlines.h"
|
||||
@ -190,8 +188,8 @@ namespace js {
|
||||
* only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
|
||||
* to 2^32-1."
|
||||
*
|
||||
* This means the largest allowed index is actually 2^32-2 (4294967294).
|
||||
*
|
||||
* This means the largest allowed index is actually 2^32-2 (4294967294).
|
||||
*
|
||||
* In our implementation, it would be sufficient to check for JSVAL_IS_INT(id)
|
||||
* except that by using signed 31-bit integers we miss the top half of the
|
||||
* valid range. This function checks the string representation itself; note
|
||||
@ -209,7 +207,7 @@ StringIsArrayIndex(JSLinearString *str, jsuint *indexp)
|
||||
if (length == 0 || length > (sizeof("4294967294") - 1) || !JS7_ISDEC(*s))
|
||||
return false;
|
||||
|
||||
uint32 c = 0, previous = 0;
|
||||
uint32 c = 0, previous = 0;
|
||||
uint32 index = JS7_UNDEC(*s++);
|
||||
|
||||
/* Don't allow leading zeros. */
|
||||
@ -226,13 +224,13 @@ StringIsArrayIndex(JSLinearString *str, jsuint *indexp)
|
||||
}
|
||||
|
||||
/* Make sure we didn't overflow. */
|
||||
if (previous < (MAX_ARRAY_INDEX / 10) || (previous == (MAX_ARRAY_INDEX / 10) &&
|
||||
if (previous < (MAX_ARRAY_INDEX / 10) || (previous == (MAX_ARRAY_INDEX / 10) &&
|
||||
c <= (MAX_ARRAY_INDEX % 10))) {
|
||||
JS_ASSERT(index <= MAX_ARRAY_INDEX);
|
||||
*indexp = index;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1982,104 +1980,239 @@ array_reverse(JSContext *cx, uintN argc, Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
typedef struct MSortArgs {
|
||||
size_t elsize;
|
||||
JSComparator cmp;
|
||||
void *arg;
|
||||
JSBool isValue;
|
||||
} MSortArgs;
|
||||
|
||||
inline bool
|
||||
CompareStringValues(JSContext *cx, const Value &a, const Value &b, bool *lessOrEqualp)
|
||||
/* Helper function for js_MergeSort. */
|
||||
static JSBool
|
||||
MergeArrays(MSortArgs *msa, void *src, void *dest, size_t run1, size_t run2)
|
||||
{
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
void *arg, *a, *b, *c;
|
||||
size_t elsize, runtotal;
|
||||
int cmp_result;
|
||||
JSComparator cmp;
|
||||
JSBool isValue;
|
||||
|
||||
JSString *astr = a.toString();
|
||||
JSString *bstr = b.toString();
|
||||
int32 result;
|
||||
if (!CompareStrings(cx, astr, bstr, &result))
|
||||
return false;
|
||||
runtotal = run1 + run2;
|
||||
|
||||
*lessOrEqualp = (result <= 0);
|
||||
return true;
|
||||
elsize = msa->elsize;
|
||||
cmp = msa->cmp;
|
||||
arg = msa->arg;
|
||||
isValue = msa->isValue;
|
||||
|
||||
#define CALL_CMP(a, b) \
|
||||
if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE;
|
||||
|
||||
/* Copy runs already in sorted order. */
|
||||
b = (char *)src + run1 * elsize;
|
||||
a = (char *)b - elsize;
|
||||
CALL_CMP(a, b);
|
||||
if (cmp_result <= 0) {
|
||||
memcpy(dest, src, runtotal * elsize);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
#define COPY_ONE(p,q,n) \
|
||||
(isValue ? (void)(*(Value*)p = *(Value*)q) : (void)memcpy(p, q, n))
|
||||
|
||||
a = src;
|
||||
c = dest;
|
||||
for (; runtotal != 0; runtotal--) {
|
||||
JSBool from_a = run2 == 0;
|
||||
if (!from_a && run1 != 0) {
|
||||
CALL_CMP(a,b);
|
||||
from_a = cmp_result <= 0;
|
||||
}
|
||||
|
||||
if (from_a) {
|
||||
COPY_ONE(c, a, elsize);
|
||||
run1--;
|
||||
a = (char *)a + elsize;
|
||||
} else {
|
||||
COPY_ONE(c, b, elsize);
|
||||
run2--;
|
||||
b = (char *)b + elsize;
|
||||
}
|
||||
c = (char *)c + elsize;
|
||||
}
|
||||
#undef COPY_ONE
|
||||
#undef CALL_CMP
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
struct SortComparatorStrings {
|
||||
JSContext *const cx;
|
||||
|
||||
SortComparatorStrings(JSContext *cx)
|
||||
: cx(cx) {}
|
||||
|
||||
bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) {
|
||||
return CompareStringValues(cx, a, b, lessOrEqualp);
|
||||
}
|
||||
};
|
||||
|
||||
struct StringValuePair {
|
||||
Value str;
|
||||
Value v;
|
||||
};
|
||||
|
||||
struct SortComparatorStringValuePairs {
|
||||
JSContext *const cx;
|
||||
|
||||
SortComparatorStringValuePairs(JSContext *cx)
|
||||
: cx(cx) {}
|
||||
|
||||
bool operator()(const StringValuePair &a, const StringValuePair &b, bool *lessOrEqualp) {
|
||||
return CompareStringValues(cx, a.str, b.str, lessOrEqualp);
|
||||
}
|
||||
};
|
||||
|
||||
struct SortComparatorFunction {
|
||||
JSContext *const cx;
|
||||
const Value &fval;
|
||||
InvokeArgsGuard &ag;
|
||||
|
||||
SortComparatorFunction(JSContext *cx, const Value &fval, InvokeArgsGuard &ag)
|
||||
: cx(cx), fval(fval), ag(ag) { }
|
||||
|
||||
bool JS_REQUIRES_STACK operator()(const Value &a, const Value &b, bool *lessOrEqualp);
|
||||
};
|
||||
|
||||
/*
|
||||
* This sort is stable, i.e. sequence of equal elements is preserved.
|
||||
* See also bug #224128.
|
||||
*/
|
||||
bool
|
||||
SortComparatorFunction::operator()(const Value &a, const Value &b, bool *lessOrEqualp)
|
||||
js_MergeSort(void *src, size_t nel, size_t elsize,
|
||||
JSComparator cmp, void *arg, void *tmp,
|
||||
JSMergeSortElemType elemType)
|
||||
{
|
||||
void *swap, *vec1, *vec2;
|
||||
MSortArgs msa;
|
||||
size_t i, j, lo, hi, run;
|
||||
int cmp_result;
|
||||
|
||||
JS_ASSERT_IF(JS_SORTING_VALUES, elsize == sizeof(Value));
|
||||
bool isValue = elemType == JS_SORTING_VALUES;
|
||||
|
||||
/* Avoid memcpy overhead for word-sized and word-aligned elements. */
|
||||
#define COPY_ONE(p,q,n) \
|
||||
(isValue ? (void)(*(Value*)p = *(Value*)q) : (void)memcpy(p, q, n))
|
||||
#define CALL_CMP(a, b) \
|
||||
if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE;
|
||||
#define INS_SORT_INT 4
|
||||
|
||||
/*
|
||||
* Apply insertion sort to small chunks to reduce the number of merge
|
||||
* passes needed.
|
||||
*/
|
||||
for (lo = 0; lo < nel; lo += INS_SORT_INT) {
|
||||
hi = lo + INS_SORT_INT;
|
||||
if (hi >= nel)
|
||||
hi = nel;
|
||||
for (i = lo + 1; i < hi; i++) {
|
||||
vec1 = (char *)src + i * elsize;
|
||||
vec2 = (char *)vec1 - elsize;
|
||||
for (j = i; j > lo; j--) {
|
||||
CALL_CMP(vec2, vec1);
|
||||
/* "<=" instead of "<" insures the sort is stable */
|
||||
if (cmp_result <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Swap elements, using "tmp" as tmp storage */
|
||||
COPY_ONE(tmp, vec2, elsize);
|
||||
COPY_ONE(vec2, vec1, elsize);
|
||||
COPY_ONE(vec1, tmp, elsize);
|
||||
vec1 = vec2;
|
||||
vec2 = (char *)vec1 - elsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef CALL_CMP
|
||||
#undef COPY_ONE
|
||||
|
||||
msa.elsize = elsize;
|
||||
msa.cmp = cmp;
|
||||
msa.arg = arg;
|
||||
msa.isValue = isValue;
|
||||
|
||||
vec1 = src;
|
||||
vec2 = tmp;
|
||||
for (run = INS_SORT_INT; run < nel; run *= 2) {
|
||||
for (lo = 0; lo < nel; lo += 2 * run) {
|
||||
hi = lo + run;
|
||||
if (hi >= nel) {
|
||||
memcpy((char *)vec2 + lo * elsize, (char *)vec1 + lo * elsize,
|
||||
(nel - lo) * elsize);
|
||||
break;
|
||||
}
|
||||
if (!MergeArrays(&msa, (char *)vec1 + lo * elsize,
|
||||
(char *)vec2 + lo * elsize, run,
|
||||
hi + run > nel ? nel - hi : run)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
swap = vec1;
|
||||
vec1 = vec2;
|
||||
vec2 = swap;
|
||||
}
|
||||
if (src != vec1)
|
||||
memcpy(src, tmp, nel * elsize);
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
struct CompareArgs
|
||||
{
|
||||
JSContext *context;
|
||||
InvokeArgsGuard args;
|
||||
Value fval;
|
||||
|
||||
CompareArgs(JSContext *cx, Value fval)
|
||||
: context(cx), fval(fval)
|
||||
{}
|
||||
};
|
||||
|
||||
static JS_REQUIRES_STACK JSBool
|
||||
sort_compare(void *arg, const void *a, const void *b, int *result)
|
||||
{
|
||||
const Value *av = (const Value *)a, *bv = (const Value *)b;
|
||||
CompareArgs *ca = (CompareArgs *) arg;
|
||||
JSContext *cx = ca->context;
|
||||
|
||||
/*
|
||||
* array_sort deals with holes and undefs on its own and they should not
|
||||
* come here.
|
||||
*/
|
||||
JS_ASSERT(!a.isMagic() && !a.isUndefined());
|
||||
JS_ASSERT(!a.isMagic() && !b.isUndefined());
|
||||
JS_ASSERT(!av->isMagic() && !av->isUndefined());
|
||||
JS_ASSERT(!av->isMagic() && !bv->isUndefined());
|
||||
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
return JS_FALSE;
|
||||
|
||||
InvokeArgsGuard &ag = ca->args;
|
||||
if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 2, &ag))
|
||||
return false;
|
||||
return JS_FALSE;
|
||||
|
||||
ag.setCallee(fval);
|
||||
ag.setCallee(ca->fval);
|
||||
ag.thisv() = UndefinedValue();
|
||||
ag[0] = a;
|
||||
ag[1] = b;
|
||||
ag[0] = *av;
|
||||
ag[1] = *bv;
|
||||
|
||||
if (!Invoke(cx, ag))
|
||||
return false;
|
||||
return JS_FALSE;
|
||||
|
||||
jsdouble cmp;
|
||||
if (!ToNumber(cx, ag.rval(), &cmp))
|
||||
return false;
|
||||
return JS_FALSE;
|
||||
|
||||
/* Clamp cmp to -1, 0, 1. */
|
||||
*result = 0;
|
||||
if (!JSDOUBLE_IS_NaN(cmp) && cmp != 0)
|
||||
*result = cmp > 0 ? 1 : -1;
|
||||
|
||||
/*
|
||||
* XXX eport some kind of error here if cmp is NaN? 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.
|
||||
* XXX else 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.
|
||||
*/
|
||||
*lessOrEqualp = (JSDOUBLE_IS_NaN(cmp) || cmp <= 0);
|
||||
return true;
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
} /* namespace anonymous */
|
||||
typedef JSBool (JS_REQUIRES_STACK *JSRedComparator)(void*, const void*,
|
||||
const void*, int *);
|
||||
|
||||
static inline JS_IGNORE_STACK JSComparator
|
||||
comparator_stack_cast(JSRedComparator func)
|
||||
{
|
||||
return func;
|
||||
}
|
||||
|
||||
static int
|
||||
sort_compare_strings(void *arg, const void *a, const void *b, int *result)
|
||||
{
|
||||
JSContext *cx = (JSContext *)arg;
|
||||
JSString *astr = ((const Value *)a)->toString();
|
||||
JSString *bstr = ((const Value *)b)->toString();
|
||||
return JS_CHECK_OPERATION_LIMIT(cx) && CompareStrings(cx, astr, bstr, result);
|
||||
}
|
||||
|
||||
JSBool
|
||||
js::array_sort(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
jsuint len, newlen, i, undefs;
|
||||
size_t elemsize;
|
||||
JSString *str;
|
||||
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
Value fval;
|
||||
if (args.length() > 0 && !args[0].isUndefined()) {
|
||||
@ -2095,8 +2228,6 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp)
|
||||
JSObject *obj = ToObject(cx, &args.thisv());
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
jsuint len;
|
||||
if (!js_GetLengthProperty(cx, obj, &len))
|
||||
return false;
|
||||
if (len == 0) {
|
||||
@ -2126,12 +2257,23 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp)
|
||||
* access the tail of vec corresponding to properties that do not
|
||||
* exist, allowing OS to avoiding committing RAM. See bug 330812.
|
||||
*/
|
||||
size_t n, undefs;
|
||||
{
|
||||
AutoValueVector vec(cx);
|
||||
if (!vec.reserve(2 * size_t(len)))
|
||||
Value *vec = (Value *) cx->malloc_(2 * size_t(len) * sizeof(Value));
|
||||
if (!vec)
|
||||
return false;
|
||||
|
||||
DEFINE_LOCAL_CLASS_OF_STATIC_FUNCTION(AutoFreeVector) {
|
||||
JSContext *const cx;
|
||||
Value *&vec;
|
||||
public:
|
||||
AutoFreeVector(JSContext *cx, Value *&vec) : cx(cx), vec(vec) { }
|
||||
~AutoFreeVector() {
|
||||
cx->free_(vec);
|
||||
}
|
||||
} free_(cx, vec);
|
||||
|
||||
AutoArrayRooter tvr(cx, 0, 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
|
||||
@ -2141,60 +2283,94 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp)
|
||||
* undefs.
|
||||
*/
|
||||
undefs = 0;
|
||||
newlen = 0;
|
||||
bool allStrings = true;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
|
||||
/* Clear vec[newlen] before including it in the rooted set. */
|
||||
JSBool hole;
|
||||
Value v;
|
||||
if (!GetElement(cx, obj, i, &hole, &v))
|
||||
vec[newlen].setNull();
|
||||
tvr.changeLength(newlen + 1);
|
||||
if (!GetElement(cx, obj, i, &hole, &vec[newlen]))
|
||||
return false;
|
||||
|
||||
if (hole)
|
||||
continue;
|
||||
if (v.isUndefined()) {
|
||||
|
||||
if (vec[newlen].isUndefined()) {
|
||||
++undefs;
|
||||
continue;
|
||||
}
|
||||
vec.infallibleAppend(v);
|
||||
allStrings = allStrings && v.isString();
|
||||
|
||||
allStrings = allStrings && vec[newlen].isString();
|
||||
|
||||
++newlen;
|
||||
}
|
||||
|
||||
n = vec.length();
|
||||
if (n == 0) {
|
||||
if (newlen == 0) {
|
||||
args.rval().setObject(*obj);
|
||||
return true; /* The array has only holes and undefs. */
|
||||
}
|
||||
|
||||
JS_ALWAYS_TRUE(vec.resize(n * 2));
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
Value *mergesort_tmp = vec + newlen;
|
||||
MakeRangeGCSafe(mergesort_tmp, newlen);
|
||||
tvr.changeLength(newlen * 2);
|
||||
|
||||
/* Here len == 2 * (n + undefs + number_of_holes). */
|
||||
/* Here len == 2 * (newlen + undefs + number_of_holes). */
|
||||
if (fval.isNull()) {
|
||||
/*
|
||||
* Sort using the default comparator converting all elements to
|
||||
* strings.
|
||||
*/
|
||||
if (allStrings) {
|
||||
if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorStrings(cx)))
|
||||
return false;
|
||||
elemsize = sizeof(Value);
|
||||
} 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. For that we move the
|
||||
* original values to the odd indexes in vec, put the string
|
||||
* conversion results in the even indexes and do the merge sort
|
||||
* over resulting string-value pairs using an extra allocated
|
||||
* scratch space.
|
||||
* 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(Value) 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 Value.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
size_t i = n;
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
if (size_t(newlen) > size_t(-1) / (4 * sizeof(Value))) {
|
||||
js_ReportAllocationOverflow(cx);
|
||||
return false;
|
||||
}
|
||||
#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;
|
||||
const Value &v = vec[i];
|
||||
JSString *str = js_ValueToString(cx, v);
|
||||
str = js_ValueToString(cx, v);
|
||||
if (!str)
|
||||
return false;
|
||||
// Copying v must come first, because the following line overwrites v
|
||||
@ -2203,30 +2379,40 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp)
|
||||
vec[2 * i].setString(str);
|
||||
} while (i != 0);
|
||||
|
||||
AutoValueVector extraScratch(cx);
|
||||
if (!extraScratch.resize(n * 2))
|
||||
return false;
|
||||
if (!MergeSort(reinterpret_cast<StringValuePair *>(vec.begin()), n,
|
||||
reinterpret_cast<StringValuePair *>(extraScratch.begin()),
|
||||
SortComparatorStringValuePairs(cx))) {
|
||||
JS_ASSERT(tvr.array == vec);
|
||||
vec = (Value *) cx->realloc_(vec, 4 * size_t(newlen) * sizeof(Value));
|
||||
if (!vec) {
|
||||
vec = tvr.array; /* N.B. AutoFreeVector */
|
||||
return false;
|
||||
}
|
||||
|
||||
mergesort_tmp = vec + 2 * newlen;
|
||||
MakeRangeGCSafe(mergesort_tmp, 2 * newlen);
|
||||
tvr.changeArray(vec, newlen * 4);
|
||||
elemsize = 2 * sizeof(Value);
|
||||
}
|
||||
if (!js_MergeSort(vec, size_t(newlen), elemsize,
|
||||
sort_compare_strings, cx, mergesort_tmp,
|
||||
JS_SORTING_GENERIC)) {
|
||||
return false;
|
||||
}
|
||||
if (!allStrings) {
|
||||
/*
|
||||
* We want to unroot the cached results of toString calls
|
||||
* before the operation callback has a chance to run the GC.
|
||||
* So we do not call JS_CHECK_OPERATION_LIMIT in the loop.
|
||||
* 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 != n);
|
||||
} while (++i != newlen);
|
||||
}
|
||||
} else {
|
||||
InvokeArgsGuard args;
|
||||
if (!MergeSort(vec.begin(), n, vec.begin() + n,
|
||||
SortComparatorFunction(cx, fval, args)))
|
||||
{
|
||||
CompareArgs ca(cx, fval);
|
||||
if (!js_MergeSort(vec, size_t(newlen), sizeof(Value),
|
||||
comparator_stack_cast(sort_compare),
|
||||
&ca, mergesort_tmp,
|
||||
JS_SORTING_VALUES)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -2236,20 +2422,22 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp)
|
||||
* unroot it now to make the job of a potential GC under
|
||||
* InitArrayElements easier.
|
||||
*/
|
||||
vec.resize(n);
|
||||
if (!InitArrayElements(cx, obj, 0, n, vec.begin(), false))
|
||||
tvr.changeLength(newlen);
|
||||
if (!InitArrayElements(cx, obj, 0, newlen, vec, false))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set undefs that sorted after the rest of elements. */
|
||||
while (undefs != 0) {
|
||||
--undefs;
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx) || !SetArrayElement(cx, obj, n++, UndefinedValue()))
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
|
||||
!SetArrayElement(cx, obj, newlen++, UndefinedValue())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Re-create any holes that sorted to the end of the array. */
|
||||
while (len > n) {
|
||||
while (len > newlen) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx) || DeleteArrayElement(cx, obj, --len, true) < 0)
|
||||
return false;
|
||||
}
|
||||
@ -2398,7 +2586,7 @@ array_pop_dense(JSContext *cx, JSObject* obj, CallArgs &args)
|
||||
}
|
||||
|
||||
index--;
|
||||
|
||||
|
||||
JSBool hole;
|
||||
Value elt;
|
||||
if (!GetElement(cx, obj, index, &hole, &elt))
|
||||
@ -3204,7 +3392,7 @@ array_readonlyCommon(JSContext *cx, CallArgs &args)
|
||||
args.rval() = Behavior::lateExitValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* ES5 15.4.4.16. */
|
||||
static JSBool
|
||||
array_every(JSContext *cx, uintN argc, Value *vp)
|
||||
@ -3407,7 +3595,7 @@ class ArrayReduceRightBehavior
|
||||
{
|
||||
*start = len - 1;
|
||||
*step = -1;
|
||||
/*
|
||||
/*
|
||||
* We rely on (well defined) unsigned integer underflow to check our
|
||||
* end condition after visiting the full range (including 0).
|
||||
*/
|
||||
|
@ -178,6 +178,31 @@ GetElements(JSContext *cx, JSObject *aobj, jsuint length, js::Value *vp);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* JS-specific merge sort function.
|
||||
*/
|
||||
typedef JSBool (*JSComparator)(void *arg, const void *a, const void *b,
|
||||
int *result);
|
||||
|
||||
enum JSMergeSortElemType {
|
||||
JS_SORTING_VALUES,
|
||||
JS_SORTING_GENERIC
|
||||
};
|
||||
|
||||
/*
|
||||
* NB: vec is the array to be sorted, tmp is temporary space at least as big
|
||||
* as vec. Both should be GC-rooted if appropriate.
|
||||
*
|
||||
* isValue should true iff vec points to an array of js::Value
|
||||
*
|
||||
* The sorted result is in vec. vec may be in an inconsistent state if the
|
||||
* comparator function cmp returns an error inside a comparison, so remember
|
||||
* to check the return value of this function.
|
||||
*/
|
||||
extern bool
|
||||
js_MergeSort(void *vec, size_t nel, size_t elsize, JSComparator cmp,
|
||||
void *arg, void *tmp, JSMergeSortElemType elemType);
|
||||
|
||||
/* Natives exposed for optimization by the interpreter and JITs. */
|
||||
namespace js {
|
||||
|
||||
|
@ -69,8 +69,6 @@
|
||||
#include "jsscript.h"
|
||||
#include "jsstr.h"
|
||||
|
||||
#include "ds/Sort.h"
|
||||
|
||||
#include "frontend/BytecodeEmitter.h"
|
||||
#include "frontend/TokenStream.h"
|
||||
#include "vm/Debugger.h"
|
||||
@ -1341,19 +1339,26 @@ IsInitializerOp(unsigned char op)
|
||||
return op == JSOP_NEWINIT || op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT;
|
||||
}
|
||||
|
||||
struct TableEntry {
|
||||
typedef struct TableEntry {
|
||||
jsval key;
|
||||
ptrdiff_t offset;
|
||||
JSAtom *label;
|
||||
jsint order; /* source order for stable tableswitch sort */
|
||||
};
|
||||
} TableEntry;
|
||||
|
||||
inline bool
|
||||
CompareTableEntries(const TableEntry &a, const TableEntry &b, bool *lessOrEqualp)
|
||||
static JSBool
|
||||
CompareOffsets(void *arg, const void *v1, const void *v2, int *result)
|
||||
{
|
||||
*lessOrEqualp = (a.offset != b.offset) ? a.offset <= b.offset : a.order <= b.order;
|
||||
return true;
|
||||
};
|
||||
ptrdiff_t offset_diff;
|
||||
const TableEntry *te1 = (const TableEntry *) v1,
|
||||
*te2 = (const TableEntry *) v2;
|
||||
|
||||
offset_diff = te1->offset - te2->offset;
|
||||
*result = (offset_diff == 0 ? te1->order - te2->order
|
||||
: offset_diff < 0 ? -1
|
||||
: 1);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static ptrdiff_t
|
||||
SprintDoubleValue(Sprinter *sp, jsval v, JSOp *opp)
|
||||
@ -4329,7 +4334,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
|
||||
len = js_GetSrcNoteOffset(sn, 0);
|
||||
jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN : JUMPX_OFFSET_LEN;
|
||||
jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN
|
||||
: JUMPX_OFFSET_LEN;
|
||||
pc2 = pc;
|
||||
off = GetJumpOffset(pc, pc2);
|
||||
pc2 += jmplen;
|
||||
@ -4342,7 +4348,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
if (n == 0) {
|
||||
table = NULL;
|
||||
j = 0;
|
||||
ok = true;
|
||||
ok = JS_TRUE;
|
||||
} else {
|
||||
table = (TableEntry *)
|
||||
cx->malloc_((size_t)n * sizeof *table);
|
||||
@ -4368,16 +4374,19 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
cx->malloc_((size_t)j * sizeof *table);
|
||||
if (tmp) {
|
||||
VOUCH_DOES_NOT_REQUIRE_STACK();
|
||||
MergeSort(table, size_t(j), tmp, CompareTableEntries);
|
||||
Foreground::free_(tmp);
|
||||
ok = true;
|
||||
ok = js_MergeSort(table, (size_t)j, sizeof(TableEntry),
|
||||
CompareOffsets, NULL, tmp,
|
||||
JS_SORTING_GENERIC);
|
||||
cx->free_(tmp);
|
||||
} else {
|
||||
ok = false;
|
||||
ok = JS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok)
|
||||
ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off, false);
|
||||
if (ok) {
|
||||
ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off,
|
||||
JS_FALSE);
|
||||
}
|
||||
cx->free_(table);
|
||||
if (!ok)
|
||||
return NULL;
|
||||
|
Loading…
Reference in New Issue
Block a user