mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug 603318 - make dense array slow during array growth, not during the GC. r=bhackett
This commit is contained in:
parent
96c95689e6
commit
f110aa58b7
185
js/src/jit-test/tests/basic/testDenseToSlowArray.js
Normal file
185
js/src/jit-test/tests/basic/testDenseToSlowArray.js
Normal file
@ -0,0 +1,185 @@
|
||||
// test dense -> slow array transitions during the recording and on trace
|
||||
// for various array functions and property accessors
|
||||
|
||||
function test_set_elem() {
|
||||
|
||||
function f() {
|
||||
var bag = [];
|
||||
for (var i = 0; i != 100; ++i) {
|
||||
var a = [0];
|
||||
a[100*100] = i;
|
||||
bag.push(a);
|
||||
}
|
||||
|
||||
for (var i = 0; i != 100; ++i) {
|
||||
var a = [0];
|
||||
a[200 + i] = i;
|
||||
bag.push(a);
|
||||
}
|
||||
return bag;
|
||||
}
|
||||
|
||||
var bag = f();
|
||||
|
||||
for (var i = 0; i != 100; ++i) {
|
||||
var a = bag[i];
|
||||
assertEq(a.length, 100 * 100 + 1);
|
||||
assertEq(a[100*100], i);
|
||||
assertEq(a[0], 0);
|
||||
assertEq(1 + i in a, false);
|
||||
}
|
||||
|
||||
for (var i = 0; i != 100; ++i) {
|
||||
var a = bag[100 + i];
|
||||
assertEq(a.length, 200 + i + 1);
|
||||
assertEq(a[200 + i], i);
|
||||
assertEq(a[0], 0);
|
||||
assertEq(1 + i in a, false);
|
||||
}
|
||||
}
|
||||
|
||||
function test_reverse() {
|
||||
|
||||
function prepare_arays() {
|
||||
var bag = [];
|
||||
var base_index = 245;
|
||||
for (var i = 0; i != 50; ++i) {
|
||||
var a = [1, 2, 3, 4, 5];
|
||||
a.length = i + base_index;
|
||||
bag.push(a);
|
||||
}
|
||||
return bag;
|
||||
}
|
||||
|
||||
function test(bag) {
|
||||
for (var i = 0; i != bag.length; ++i) {
|
||||
var a = bag[i];
|
||||
a.reverse();
|
||||
a[0] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
var bag = prepare_arays();
|
||||
test(bag);
|
||||
for (var i = 0; i != bag.length; ++i) {
|
||||
var a = bag[i];
|
||||
assertEq(a[0], 1);
|
||||
for (var j = 1; j <= 5; ++j) {
|
||||
assertEq(a[a.length - j], j);
|
||||
}
|
||||
for (var j = 1; j < a.length - 5; ++j) {
|
||||
assertEq(j in a, false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function test_push() {
|
||||
|
||||
function prepare_arays() {
|
||||
var bag = [];
|
||||
var base_index = 245;
|
||||
for (var i = 0; i != 50; ++i) {
|
||||
var a = [0];
|
||||
a.length = i + base_index;
|
||||
bag.push(a);
|
||||
}
|
||||
return bag;
|
||||
}
|
||||
|
||||
function test(bag) {
|
||||
for (var i = 0; i != bag.length; ++i) {
|
||||
var a = bag[i];
|
||||
a.push(2);
|
||||
a[0] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
var bag = prepare_arays();
|
||||
test(bag);
|
||||
for (var i = 0; i != bag.length; ++i) {
|
||||
var a = bag[i];
|
||||
assertEq(a[0], 1);
|
||||
assertEq(a[a.length - 1], 2);
|
||||
for (var j = 1; j < a.length - 1; ++j) {
|
||||
assertEq(j in a, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function test_unshift() {
|
||||
|
||||
function prepare_arays() {
|
||||
var bag = [];
|
||||
var base_index = 245;
|
||||
for (var i = 0; i != 50; ++i) {
|
||||
var a = [0];
|
||||
a.length = i + base_index;
|
||||
bag.push(a);
|
||||
}
|
||||
return bag;
|
||||
}
|
||||
|
||||
function test(bag) {
|
||||
for (var i = 0; i != bag.length; ++i) {
|
||||
var a = bag[i];
|
||||
a.unshift(1);
|
||||
a[2] = 2;
|
||||
}
|
||||
}
|
||||
|
||||
var bag = prepare_arays();
|
||||
test(bag);
|
||||
for (var i = 0; i != bag.length; ++i) {
|
||||
var a = bag[i];
|
||||
assertEq(a[0], 1);
|
||||
assertEq(a[1], 0);
|
||||
assertEq(a[2], 2);
|
||||
for (var j = 3; j < a.length; ++j) {
|
||||
assertEq(j in a, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function test_splice() {
|
||||
|
||||
function prepare_arays() {
|
||||
var bag = [];
|
||||
var base_index = 245;
|
||||
for (var i = 0; i != 50; ++i) {
|
||||
var a = [1, 2];
|
||||
a.length = i + base_index;
|
||||
bag.push(a);
|
||||
}
|
||||
return bag;
|
||||
}
|
||||
|
||||
function test(bag) {
|
||||
for (var i = 0; i != bag.length; ++i) {
|
||||
var a = bag[i];
|
||||
a.splice(1, 0, "a", "b", "c");
|
||||
a[2] = 100;
|
||||
}
|
||||
}
|
||||
|
||||
var bag = prepare_arays();
|
||||
test(bag);
|
||||
for (var i = 0; i != bag.length; ++i) {
|
||||
var a = bag[i];
|
||||
assertEq(a[0], 1);
|
||||
assertEq(a[1], "a");
|
||||
assertEq(a[2], 100);
|
||||
assertEq(a[3], "c");
|
||||
assertEq(a[4], 2);
|
||||
for (var j = 5; j < a.length; ++j) {
|
||||
assertEq(j in a, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test_set_elem();
|
||||
test_reverse();
|
||||
test_push();
|
||||
test_unshift();
|
||||
test_splice();
|
||||
|
@ -62,9 +62,9 @@
|
||||
*
|
||||
* Arrays are converted to use js_SlowArrayClass when any of these conditions
|
||||
* are met:
|
||||
* - the load factor (COUNT / capacity) is less than 0.25, and there are
|
||||
* more than MIN_SPARSE_INDEX slots total
|
||||
* - a property is set that is not indexed (and not "length"); or
|
||||
* - there are more than MIN_SPARSE_INDEX slots total
|
||||
* - the load factor (COUNT / capacity) is less than 0.25
|
||||
* - a property is set that is not indexed (and not "length")
|
||||
* - a property is defined that has non-default property attributes.
|
||||
*
|
||||
* Dense arrays do not track property creation order, so unlike other native
|
||||
@ -115,30 +115,6 @@ using namespace js::gc;
|
||||
#define MAXINDEX 4294967295u
|
||||
#define MAXSTR "4294967295"
|
||||
|
||||
/*
|
||||
* Use the limit on number of object slots for sanity and consistency (see the
|
||||
* assertion in JSObject::makeDenseArraySlow).
|
||||
*/
|
||||
static inline bool
|
||||
INDEX_TOO_BIG(jsuint index)
|
||||
{
|
||||
return index >= JSObject::NSLOTS_LIMIT;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
INDEX_TOO_SPARSE(JSObject *array, jsuint index)
|
||||
{
|
||||
/* Small arrays with less than 256 elements are dense, no matter what. */
|
||||
if (index < 256)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Otherwise if the index becomes too large or is more than 256 past
|
||||
* the current capacity, we have to slowify.
|
||||
*/
|
||||
return INDEX_TOO_BIG(index) || (index > array->getDenseArrayCapacity() + 256);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ENSURE_SLOW_ARRAY(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
@ -310,6 +286,34 @@ BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom,
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
bool
|
||||
JSObject::willBeSparseDenseArray(uintN requiredCapacity, uintN newElementsHint)
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
JS_ASSERT(requiredCapacity > MIN_SPARSE_INDEX);
|
||||
|
||||
uintN cap = numSlots();
|
||||
JS_ASSERT(requiredCapacity >= cap);
|
||||
|
||||
if (requiredCapacity >= JSObject::NSLOTS_LIMIT)
|
||||
return true;
|
||||
|
||||
uintN minimalDenseCount = requiredCapacity / 4;
|
||||
if (newElementsHint >= minimalDenseCount)
|
||||
return false;
|
||||
minimalDenseCount -= newElementsHint;
|
||||
|
||||
if (minimalDenseCount > cap)
|
||||
return true;
|
||||
|
||||
Value *elems = getDenseArrayElements();
|
||||
for (uintN i = 0; i < cap; i++) {
|
||||
if (!elems[i].isMagic(JS_ARRAY_HOLE) && !--minimalDenseCount)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ReallyBigIndexToId(JSContext* cx, jsdouble index, jsid* idp)
|
||||
{
|
||||
@ -439,19 +443,23 @@ SetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, const Value &v)
|
||||
|
||||
if (obj->isDenseArray()) {
|
||||
/* Predicted/prefetched code should favor the remains-dense case. */
|
||||
if (index <= jsuint(-1)) {
|
||||
JSObject::EnsureDenseResult result = JSObject::ED_SPARSE;
|
||||
do {
|
||||
if (index > jsuint(-1))
|
||||
break;
|
||||
jsuint idx = jsuint(index);
|
||||
if (!INDEX_TOO_SPARSE(obj, idx)) {
|
||||
JS_ASSERT(idx + 1 > idx);
|
||||
if (!obj->ensureDenseArrayElements(cx, idx + 1))
|
||||
return JS_FALSE;
|
||||
if (idx >= obj->getArrayLength())
|
||||
obj->setArrayLength(idx + 1);
|
||||
obj->setDenseArrayElement(idx, v);
|
||||
return JS_TRUE;
|
||||
}
|
||||
}
|
||||
result = obj->ensureDenseArrayElements(cx, idx, 1);
|
||||
if (result != JSObject::ED_OK)
|
||||
break;
|
||||
if (idx >= obj->getArrayLength())
|
||||
obj->setArrayLength(idx + 1);
|
||||
obj->setDenseArrayElement(idx, v);
|
||||
return true;
|
||||
} while (false);
|
||||
|
||||
if (result == JSObject::ED_FAILED)
|
||||
return false;
|
||||
JS_ASSERT(result == JSObject::ED_SPARSE);
|
||||
if (!obj->makeDenseArraySlow(cx))
|
||||
return JS_FALSE;
|
||||
}
|
||||
@ -474,13 +482,7 @@ js_EnsureDenseArrayCapacity(JSContext *cx, JSObject *obj, jsint i)
|
||||
Class *origObjClasp = obj->clasp;
|
||||
#endif
|
||||
jsuint u = jsuint(i);
|
||||
jsuint capacity = obj->getDenseArrayCapacity();
|
||||
if (u < capacity)
|
||||
return true;
|
||||
if (INDEX_TOO_SPARSE(obj, u))
|
||||
return false;
|
||||
|
||||
JSBool ret = obj->ensureDenseArrayElements(cx, u + 1);
|
||||
JSBool ret = (obj->ensureDenseArrayElements(cx, u, 1) == JSObject::ED_OK);
|
||||
|
||||
/* Partially check the CallInfo's storeAccSet is correct. */
|
||||
JS_ASSERT(obj->clasp == origObjClasp);
|
||||
@ -801,20 +803,29 @@ array_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool stric
|
||||
if (!obj->isDenseArray())
|
||||
return js_SetProperty(cx, obj, id, vp, strict);
|
||||
|
||||
if (!js_IdIsIndex(id, &i) || js_PrototypeHasIndexedProperties(cx, obj) ||
|
||||
INDEX_TOO_SPARSE(obj, i)) {
|
||||
if (!obj->makeDenseArraySlow(cx))
|
||||
return false;
|
||||
return js_SetProperty(cx, obj, id, vp, strict);
|
||||
}
|
||||
do {
|
||||
if (!js_IdIsIndex(id, &i))
|
||||
break;
|
||||
if (js_PrototypeHasIndexedProperties(cx, obj))
|
||||
break;
|
||||
|
||||
if (!obj->ensureDenseArrayElements(cx, i + 1))
|
||||
JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, i, 1);
|
||||
if (result != JSObject::ED_OK) {
|
||||
if (result == JSObject::ED_FAILED)
|
||||
return false;
|
||||
JS_ASSERT(result == JSObject::ED_SPARSE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= obj->getArrayLength())
|
||||
obj->setArrayLength(i + 1);
|
||||
obj->setDenseArrayElement(i, *vp);
|
||||
return true;
|
||||
} while (false);
|
||||
|
||||
if (!obj->makeDenseArraySlow(cx))
|
||||
return false;
|
||||
|
||||
if (i >= obj->getArrayLength())
|
||||
obj->setArrayLength(i + 1);
|
||||
obj->setDenseArrayElement(i, *vp);
|
||||
return true;
|
||||
return js_SetProperty(cx, obj, id, vp, strict);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -861,7 +872,7 @@ array_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
|
||||
return JS_TRUE;
|
||||
|
||||
isIndex = js_IdIsIndex(id, &i);
|
||||
if (!isIndex || attrs != JSPROP_ENUMERATE || !obj->isDenseArray() || INDEX_TOO_SPARSE(obj, i)) {
|
||||
if (!isIndex || attrs != JSPROP_ENUMERATE) {
|
||||
if (!ENSURE_SLOW_ARRAY(cx, obj))
|
||||
return JS_FALSE;
|
||||
return js_DefineProperty(cx, obj, id, value, getter, setter, attrs);
|
||||
@ -915,20 +926,9 @@ array_trace(JSTracer *trc, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(obj->isDenseArray());
|
||||
|
||||
size_t holes = 0;
|
||||
uint32 capacity = obj->getDenseArrayCapacity();
|
||||
for (uint32 i = 0; i < capacity; i++) {
|
||||
Value v = obj->getDenseArrayElement(i);
|
||||
if (v.isMagic(JS_ARRAY_HOLE))
|
||||
++holes;
|
||||
else
|
||||
MarkValue(trc, obj->getDenseArrayElement(i), "dense_array_elems");
|
||||
}
|
||||
|
||||
if (IS_GC_MARKING_TRACER(trc) && holes > MIN_SPARSE_INDEX && holes > capacity / 4 * 3) {
|
||||
/* This might fail, in which case we don't slowify it. */
|
||||
static_cast<GCMarker *>(trc)->arraysToSlowify.append(obj);
|
||||
}
|
||||
for (uint32 i = 0; i < capacity; i++)
|
||||
MarkValue(trc, obj->getDenseArrayElement(i), "dense_array_elems");
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -1379,22 +1379,28 @@ InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Valu
|
||||
* Optimize for dense arrays so long as adding the given set of elements
|
||||
* wouldn't otherwise make the array slow.
|
||||
*/
|
||||
if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
|
||||
start <= MAXINDEX - count && !INDEX_TOO_BIG(start + count)) {
|
||||
do {
|
||||
if (!obj->isDenseArray())
|
||||
break;
|
||||
if (js_PrototypeHasIndexedProperties(cx, obj))
|
||||
break;
|
||||
|
||||
JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, start, count);
|
||||
if (result != JSObject::ED_OK) {
|
||||
if (result == JSObject::ED_FAILED)
|
||||
return false;
|
||||
JS_ASSERT(result == JSObject::ED_SPARSE);
|
||||
break;
|
||||
}
|
||||
jsuint newlen = start + count;
|
||||
JS_ASSERT(jsdouble(start) + count == jsdouble(newlen));
|
||||
if (!obj->ensureDenseArrayElements(cx, newlen))
|
||||
return JS_FALSE;
|
||||
|
||||
if (newlen > obj->getArrayLength())
|
||||
obj->setArrayLength(newlen);
|
||||
|
||||
JS_ASSERT(count < uint32(-1) / sizeof(Value));
|
||||
memcpy(obj->getDenseArrayElements() + start, vector, sizeof(jsval) * count);
|
||||
JS_ASSERT_IF(count != 0, !obj->getDenseArrayElement(newlen - 1).isMagic(JS_ARRAY_HOLE));
|
||||
return JS_TRUE;
|
||||
}
|
||||
return true;
|
||||
} while (false);
|
||||
|
||||
Value* end = vector + count;
|
||||
while (vector != end && start < MAXINDEX) {
|
||||
@ -1436,7 +1442,9 @@ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector
|
||||
obj->setArrayLength(length);
|
||||
if (!vector || !length)
|
||||
return true;
|
||||
if (!obj->ensureDenseArrayElements(cx, length))
|
||||
|
||||
/* Avoid ensureDenseArrayElements to skip sparse array checks there. */
|
||||
if (!obj->ensureSlots(cx, length))
|
||||
return false;
|
||||
memcpy(obj->getDenseArrayElements(), vector, length * sizeof(Value));
|
||||
return true;
|
||||
@ -1470,7 +1478,12 @@ array_reverse(JSContext *cx, uintN argc, Value *vp)
|
||||
return JS_FALSE;
|
||||
vp->setObject(*obj);
|
||||
|
||||
if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj)) {
|
||||
do {
|
||||
if (!obj->isDenseArray())
|
||||
break;
|
||||
if (js_PrototypeHasIndexedProperties(cx, obj))
|
||||
break;
|
||||
|
||||
/* An empty array or an array with no elements is already reversed. */
|
||||
if (len == 0 || obj->getDenseArrayCapacity() == 0)
|
||||
return JS_TRUE;
|
||||
@ -1484,8 +1497,13 @@ array_reverse(JSContext *cx, uintN argc, Value *vp)
|
||||
* holes in the array at its start) and ensure that the capacity is
|
||||
* sufficient to hold all the elements in the array if it were full.
|
||||
*/
|
||||
if (!obj->ensureDenseArrayElements(cx, len))
|
||||
return JS_FALSE;
|
||||
JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, len, 0);
|
||||
if (result != JSObject::ED_OK) {
|
||||
if (result == JSObject::ED_FAILED)
|
||||
return false;
|
||||
JS_ASSERT(result == JSObject::ED_SPARSE);
|
||||
break;
|
||||
}
|
||||
|
||||
uint32 lo = 0, hi = len - 1;
|
||||
for (; lo < hi; lo++, hi--) {
|
||||
@ -1500,7 +1518,7 @@ array_reverse(JSContext *cx, uintN argc, Value *vp)
|
||||
* holes).
|
||||
*/
|
||||
return JS_TRUE;
|
||||
}
|
||||
} while (false);
|
||||
|
||||
AutoValueRooter tvr(cx);
|
||||
for (jsuint i = 0, half = len / 2; i < half; i++) {
|
||||
@ -2005,21 +2023,27 @@ static JSBool
|
||||
array_push1_dense(JSContext* cx, JSObject* obj, const Value &v, Value *rval)
|
||||
{
|
||||
uint32 length = obj->getArrayLength();
|
||||
if (INDEX_TOO_SPARSE(obj, length)) {
|
||||
if (!obj->makeDenseArraySlow(cx))
|
||||
return JS_FALSE;
|
||||
Value tmp = v;
|
||||
return array_push_slowly(cx, obj, 1, &tmp, rval);
|
||||
}
|
||||
do {
|
||||
JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, 1);
|
||||
if (result != JSObject::ED_OK) {
|
||||
if (result == JSObject::ED_FAILED)
|
||||
return false;
|
||||
JS_ASSERT(result == JSObject::ED_SPARSE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!obj->ensureDenseArrayElements(cx, length + 1))
|
||||
return JS_FALSE;
|
||||
obj->setArrayLength(length + 1);
|
||||
obj->setArrayLength(length + 1);
|
||||
|
||||
JS_ASSERT(obj->getDenseArrayElement(length).isMagic(JS_ARRAY_HOLE));
|
||||
obj->setDenseArrayElement(length, v);
|
||||
rval->setNumber(obj->getArrayLength());
|
||||
return JS_TRUE;
|
||||
JS_ASSERT(obj->getDenseArrayElement(length).isMagic(JS_ARRAY_HOLE));
|
||||
obj->setDenseArrayElement(length, v);
|
||||
rval->setNumber(obj->getArrayLength());
|
||||
return true;
|
||||
} while (false);
|
||||
|
||||
if (!obj->makeDenseArraySlow(cx))
|
||||
return false;
|
||||
Value tmp = v;
|
||||
return array_push_slowly(cx, obj, 1, &tmp, rval);
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE JSBool
|
||||
@ -2036,8 +2060,13 @@ ArrayCompPushImpl(JSContext *cx, JSObject *obj, const Value &v)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (!obj->ensureDenseArrayElements(cx, length + 1))
|
||||
return JS_FALSE;
|
||||
/*
|
||||
* Array comprehension cannot add holes to the array and never leaks
|
||||
* the array before it is fully initialized. So we can use ensureSlots
|
||||
* instead of ensureDenseArrayElements.
|
||||
*/
|
||||
if (!obj->ensureSlots(cx, length + 1))
|
||||
return false;
|
||||
}
|
||||
obj->setArrayLength(length + 1);
|
||||
obj->setDenseArrayElement(length, v);
|
||||
@ -2195,16 +2224,27 @@ array_unshift(JSContext *cx, uintN argc, Value *vp)
|
||||
/* Slide up the array to make room for argc at the bottom. */
|
||||
argv = JS_ARGV(cx, vp);
|
||||
if (length > 0) {
|
||||
if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
|
||||
!INDEX_TOO_SPARSE(obj, unsigned(newlen + argc))) {
|
||||
JS_ASSERT(newlen + argc == length + argc);
|
||||
if (!obj->ensureDenseArrayElements(cx, length + argc))
|
||||
return JS_FALSE;
|
||||
bool optimized = false;
|
||||
do {
|
||||
if (!obj->isDenseArray())
|
||||
break;
|
||||
if (js_PrototypeHasIndexedProperties(cx, obj))
|
||||
break;
|
||||
JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, argc);
|
||||
if (result != JSObject::ED_OK) {
|
||||
if (result == JSObject::ED_FAILED)
|
||||
return false;
|
||||
JS_ASSERT(result == JSObject::ED_SPARSE);
|
||||
break;
|
||||
}
|
||||
Value *elems = obj->getDenseArrayElements();
|
||||
memmove(elems + argc, elems, length * sizeof(jsval));
|
||||
for (uint32 i = 0; i < argc; i++)
|
||||
obj->setDenseArrayElement(i, MagicValue(JS_ARRAY_HOLE));
|
||||
} else {
|
||||
optimized = true;
|
||||
} while (false);
|
||||
|
||||
if (!optimized) {
|
||||
last = length;
|
||||
jsdouble upperIndex = last + argc;
|
||||
AutoValueRooter tvr(cx);
|
||||
@ -2324,12 +2364,23 @@ array_splice(JSContext *cx, uintN argc, Value *vp)
|
||||
if (argc > count) {
|
||||
delta = (jsuint)argc - count;
|
||||
last = length;
|
||||
if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
|
||||
length <= obj->getDenseArrayCapacity() &&
|
||||
(length == 0 || !obj->getDenseArrayElement(length - 1).isMagic(JS_ARRAY_HOLE))) {
|
||||
if (!obj->ensureDenseArrayElements(cx, length + delta))
|
||||
return JS_FALSE;
|
||||
|
||||
bool optimized = false;
|
||||
do {
|
||||
if (!obj->isDenseArray())
|
||||
break;
|
||||
if (js_PrototypeHasIndexedProperties(cx, obj))
|
||||
break;
|
||||
if (length > obj->getDenseArrayCapacity())
|
||||
break;
|
||||
if (length != 0 && obj->getDenseArrayElement(length - 1).isMagic(JS_ARRAY_HOLE))
|
||||
break;
|
||||
JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, length, delta);
|
||||
if (result != JSObject::ED_OK) {
|
||||
if (result == JSObject::ED_FAILED)
|
||||
return false;
|
||||
JS_ASSERT(result == JSObject::ED_SPARSE);
|
||||
break;
|
||||
}
|
||||
Value *arraybeg = obj->getDenseArrayElements();
|
||||
Value *srcbeg = arraybeg + last - 1;
|
||||
Value *srcend = arraybeg + end - 1;
|
||||
@ -2338,7 +2389,10 @@ array_splice(JSContext *cx, uintN argc, Value *vp)
|
||||
*dst = *src;
|
||||
|
||||
obj->setArrayLength(obj->getArrayLength() + delta);
|
||||
} else {
|
||||
optimized = true;
|
||||
} while (false);
|
||||
|
||||
if (!optimized) {
|
||||
/* (uint) end could be 0, so we can't use a vanilla >= test. */
|
||||
while (last-- > end) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
|
||||
@ -2979,7 +3033,9 @@ js_NewPreallocatedArray(JSContext* cx, JSObject* proto, int32 len)
|
||||
JSObject *obj = js_NewEmptyArray(cx, proto, len);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
if (!obj->ensureDenseArrayElements(cx, len))
|
||||
|
||||
/* Avoid ensureDenseArrayElements to skip sparse array checks there. */
|
||||
if (!obj->ensureSlots(cx, len))
|
||||
return NULL;
|
||||
return obj;
|
||||
}
|
||||
|
@ -46,6 +46,46 @@
|
||||
#include "jspubtd.h"
|
||||
#include "jsobj.h"
|
||||
|
||||
/* Small arrays are dense, no matter what. */
|
||||
const uintN MIN_SPARSE_INDEX = 256;
|
||||
|
||||
inline JSObject::EnsureDenseResult
|
||||
JSObject::ensureDenseArrayElements(JSContext *cx, uintN index, uintN extra)
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
uintN currentCapacity = numSlots();
|
||||
|
||||
uintN requiredCapacity;
|
||||
if (extra == 1) {
|
||||
/* Optimize for the common case. */
|
||||
if (index < currentCapacity)
|
||||
return ED_OK;
|
||||
requiredCapacity = index + 1;
|
||||
if (requiredCapacity == 0) {
|
||||
/* Overflow. */
|
||||
return ED_SPARSE;
|
||||
}
|
||||
} else {
|
||||
requiredCapacity = index + extra;
|
||||
if (requiredCapacity < index) {
|
||||
/* Overflow. */
|
||||
return ED_SPARSE;
|
||||
}
|
||||
if (requiredCapacity <= currentCapacity)
|
||||
return ED_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* We use the extra argument also as a hint about number of non-hole
|
||||
* elements to be inserted.
|
||||
*/
|
||||
if (requiredCapacity > MIN_SPARSE_INDEX &&
|
||||
willBeSparseDenseArray(requiredCapacity, extra)) {
|
||||
return ED_SPARSE;
|
||||
}
|
||||
return growSlots(cx, requiredCapacity) ? ED_OK : ED_FAILED;
|
||||
}
|
||||
|
||||
extern JSBool
|
||||
js_StringIsIndex(JSString *str, jsuint *indexp);
|
||||
|
||||
@ -144,9 +184,6 @@ js_NewArrayObject(JSContext *cx, jsuint length, const js::Value *vector);
|
||||
extern JSObject *
|
||||
js_NewSlowArrayObject(JSContext *cx);
|
||||
|
||||
/* Minimum size at which a dense array can be made sparse. */
|
||||
const uint32 MIN_SPARSE_INDEX = 256;
|
||||
|
||||
extern JSBool
|
||||
js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);
|
||||
|
||||
|
@ -6809,12 +6809,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
}
|
||||
#endif /* JS_HAS_GENERATORS */
|
||||
|
||||
/*
|
||||
* Use the slower NEWINIT for arrays in scripts containing sharps, and when
|
||||
* the array length exceeds MIN_SPARSE_INDEX and can be slowified during GC.
|
||||
* :FIXME: bug 607825 handle slowify case.
|
||||
*/
|
||||
if (cg->hasSharps() || pn->pn_count >= MIN_SPARSE_INDEX) {
|
||||
/* Use the slower NEWINIT for arrays in scripts containing sharps. */
|
||||
if (cg->hasSharps()) {
|
||||
if (!EmitNewInit(cx, cg, JSProto_Array, pn, sharpnum))
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
|
@ -1374,16 +1374,6 @@ GCMarker::markDelayedChildren()
|
||||
JS_ASSERT(!unmarkedArenaStackTop);
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::slowifyArrays()
|
||||
{
|
||||
while (!arraysToSlowify.empty()) {
|
||||
JSObject *obj = arraysToSlowify.back();
|
||||
arraysToSlowify.popBack();
|
||||
if (obj->isMarked())
|
||||
obj->makeDenseArraySlow(context);
|
||||
}
|
||||
}
|
||||
} /* namespace js */
|
||||
|
||||
static void
|
||||
@ -2243,9 +2233,6 @@ MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM)
|
||||
*/
|
||||
js_SweepScriptFilenames(rt);
|
||||
|
||||
/* Slowify arrays we have accumulated. */
|
||||
gcmarker.slowifyArrays();
|
||||
|
||||
/*
|
||||
* Destroy arenas after we finished the sweeping so finalizers can safely
|
||||
* use js_IsAboutToBeFinalized().
|
||||
|
@ -983,8 +983,6 @@ struct GCMarker : public JSTracer {
|
||||
void dumpConservativeRoots();
|
||||
#endif
|
||||
|
||||
js::Vector<JSObject *, 0, js::SystemAllocPolicy> arraysToSlowify;
|
||||
|
||||
public:
|
||||
explicit GCMarker(JSContext *cx);
|
||||
~GCMarker();
|
||||
@ -1005,8 +1003,6 @@ struct GCMarker : public JSTracer {
|
||||
void delayMarkingChildren(void *thing);
|
||||
|
||||
JS_FRIEND_API(void) markDelayedChildren();
|
||||
|
||||
void slowifyArrays();
|
||||
};
|
||||
|
||||
void
|
||||
|
@ -5911,7 +5911,8 @@ BEGIN_CASE(JSOP_NEWARRAY)
|
||||
unsigned count = GET_UINT24(regs.pc);
|
||||
JSObject *obj = js_NewArrayObject(cx, count, NULL);
|
||||
|
||||
if (!obj || !obj->ensureDenseArrayElements(cx, count))
|
||||
/* Avoid ensureDenseArrayElements to skip sparse array checks there. */
|
||||
if (!obj || !obj->ensureSlots(cx, count))
|
||||
goto error;
|
||||
|
||||
PUSH_OBJECT(*obj);
|
||||
|
@ -750,9 +750,24 @@ struct JSObject : js::gc::Cell {
|
||||
inline const js::Value &getDenseArrayElement(uintN idx);
|
||||
inline js::Value* addressOfDenseArrayElement(uintN idx);
|
||||
inline void setDenseArrayElement(uintN idx, const js::Value &val);
|
||||
inline bool ensureDenseArrayElements(JSContext *cx, uintN cap);
|
||||
inline void shrinkDenseArrayElements(JSContext *cx, uintN cap);
|
||||
|
||||
/*
|
||||
* ensureDenseArrayElements ensures that the dense array can hold at least
|
||||
* index + extra elements. It returns ED_OK on success, ED_FAILED on
|
||||
* failure to grow the array, ED_SPARSE when the array is too sparse to
|
||||
* grow (this includes the case of index + extra overflow). In the last
|
||||
* two cases the array is kept intact.
|
||||
*/
|
||||
enum EnsureDenseResult { ED_OK, ED_FAILED, ED_SPARSE };
|
||||
inline EnsureDenseResult ensureDenseArrayElements(JSContext *cx, uintN index, uintN extra);
|
||||
|
||||
/*
|
||||
* Check if after growing the dense array will be too sparse.
|
||||
* newElementsHint is an estimated number of elements to be added.
|
||||
*/
|
||||
bool willBeSparseDenseArray(uintN requiredCapacity, uintN newElementsHint);
|
||||
|
||||
JSBool makeDenseArraySlow(JSContext *cx);
|
||||
|
||||
/*
|
||||
|
@ -329,13 +329,6 @@ JSObject::setDenseArrayElement(uintN idx, const js::Value &val)
|
||||
setSlot(idx, val);
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSObject::ensureDenseArrayElements(JSContext *cx, uintN cap)
|
||||
{
|
||||
JS_ASSERT(isDenseArray());
|
||||
return ensureSlots(cx, cap);
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::shrinkDenseArrayElements(JSContext *cx, uintN cap)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user