aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/jsruntime/qv4argumentsobject.cpp10
-rw-r--r--src/qml/jsruntime/qv4arraydata.cpp321
-rw-r--r--src/qml/jsruntime/qv4arraydata_p.h12
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp29
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp4
-rw-r--r--src/qml/jsruntime/qv4object.cpp90
-rw-r--r--src/qml/jsruntime/qv4object_p.h34
-rw-r--r--src/qml/jsruntime/qv4objectproto.cpp16
-rw-r--r--src/qml/jsruntime/qv4property_p.h24
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp8
-rw-r--r--src/qml/jsruntime/qv4sparsearray.cpp14
-rw-r--r--src/qml/jsruntime/qv4sparsearray_p.h2
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp2
-rw-r--r--tests/manual/v4/sparsearraytest.js38
14 files changed, 376 insertions, 228 deletions
diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp
index 5094bafa66..fccfd50307 100644
--- a/src/qml/jsruntime/qv4argumentsobject.cpp
+++ b/src/qml/jsruntime/qv4argumentsobject.cpp
@@ -69,6 +69,7 @@ ArgumentsObject::ArgumentsObject(CallContext *context)
arrayData->setLength(context->callData->argc);
fullyCreated = true;
} else {
+ hasAccessorProperty = 1;
Q_ASSERT(CalleePropertyIndex == internalClass->find(context->engine->id_callee));
memberData[CalleePropertyIndex].value = context->function->asReturnedValue();
}
@@ -92,14 +93,13 @@ void ArgumentsObject::fullyCreate()
uint numAccessors = qMin((int)context->function->formalParameterCount, context->realArgumentCount);
uint argCount = qMin(context->realArgumentCount, context->callData->argc);
arrayReserve(argCount);
+ initSparseArray();
arrayData->ensureAttributes();
context->engine->requireArgumentsAccessors(numAccessors);
for (uint i = 0; i < (uint)numAccessors; ++i) {
mappedArguments.append(context->callData->args[i]);
- arrayData->data[i] = context->engine->argumentsAccessors.at(i);
- arrayData->setAttributes(i, Attr_Accessor);
+ arraySet(i, context->engine->argumentsAccessors.at(i), Attr_Accessor);
}
- arrayData->setLength(numAccessors);
arrayData->put(numAccessors, context->callData->args + numAccessors, argCount - numAccessors);
for (uint i = numAccessors; i < argCount; ++i)
arrayData->setAttributes(i, Attr_Data);
@@ -124,6 +124,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const
map = *pd;
mapAttrs = arrayData->attributes(index);
arrayData->setAttributes(index, Attr_Data);
+ pd = arrayData->getProperty(index);
pd->value = mappedArguments.at(index);
}
@@ -139,8 +140,9 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const
map.setter()->call(callData);
if (attrs.isWritable()) {
- *pd = map;
arrayData->setAttributes(index, mapAttrs);
+ pd = arrayData->getProperty(index);
+ *pd = map;
}
}
diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp
index 0ec36bd9db..09e9a7f28f 100644
--- a/src/qml/jsruntime/qv4arraydata.cpp
+++ b/src/qml/jsruntime/qv4arraydata.cpp
@@ -82,8 +82,8 @@ void ArrayData::getHeadRoom(ArrayData *d)
Q_ASSERT(d);
Q_ASSERT(!d->offset);
d->offset = qMax(d->len >> 2, (uint)16);
- Property *newArray = new Property[d->offset + d->alloc];
- memcpy(newArray + d->offset, d->data, d->len*sizeof(Property));
+ SafeValue *newArray = new SafeValue[d->offset + d->alloc];
+ memcpy(newArray + d->offset, d->data, d->len*sizeof(SafeValue));
delete [] d->data;
d->data = newArray + d->offset;
if (d->attrs) {
@@ -102,9 +102,9 @@ void ArrayData::reserve(ArrayData *d, uint n)
return;
d->alloc = qMax(n, 2*d->alloc);
- Property *newArrayData = new Property[d->alloc + d->offset];
+ SafeValue *newArrayData = new SafeValue[d->alloc + d->offset];
if (d->data) {
- memcpy(newArrayData + d->offset, d->data, sizeof(Property)*d->len);
+ memcpy(newArrayData + d->offset, d->data, sizeof(SafeValue)*d->len);
delete [] (d->data - d->offset);
}
d->data = newArrayData + d->offset;
@@ -144,14 +144,19 @@ ReturnedValue ArrayData::get(const ArrayData *d, uint index)
{
if (index >= d->len)
return Primitive::emptyValue().asReturnedValue();
- return d->data[index].value.asReturnedValue();
+ return d->data[index].asReturnedValue();
}
bool ArrayData::put(ArrayData *d, uint index, ValueRef value)
{
- Q_ASSERT(!d->attrs || !d->attrs->isAccessor());
+ Q_ASSERT(index >= d->len || !d->attrs || !d->attrs[index].isAccessor());
// ### honour attributes
- d->data[index].value = value;
+ d->data[index] = value;
+ if (index >= d->len) {
+ if (d->attrs)
+ d->attrs[index] = Attr_Data;
+ d->len = index;
+ }
return true;
}
@@ -161,12 +166,12 @@ bool ArrayData::del(ArrayData *d, uint index)
return true;
if (!d->attrs || d->attrs[index].isConfigurable()) {
- d->data[index].value = Primitive::emptyValue();
+ d->data[index] = Primitive::emptyValue();
if (d->attrs)
d->attrs[index] = Attr_Data;
return true;
}
- if (d->data[index].value.isEmpty())
+ if (d->data[index].isEmpty())
return true;
return false;
}
@@ -192,7 +197,7 @@ void ArrayData::push_front(ArrayData *d, SafeValue *values, uint n)
--d->data;
++d->len;
++d->alloc;
- d->data->value = values[i].asReturnedValue();
+ *d->data = values[i].asReturnedValue();
}
}
@@ -203,7 +208,7 @@ ReturnedValue ArrayData::pop_front(ArrayData *d)
if (!d->len)
return Encode::undefined();
- ReturnedValue v = d->data[0].value.isEmpty() ? Encode::undefined() : d->data[0].value.asReturnedValue();
+ ReturnedValue v = d->data[0].isEmpty() ? Encode::undefined() : d->data[0].asReturnedValue();
++d->offset;
++d->data;
--d->len;
@@ -214,14 +219,14 @@ ReturnedValue ArrayData::pop_front(ArrayData *d)
uint ArrayData::truncate(ArrayData *d, uint newLen)
{
if (d->attrs) {
- Property *it = d->data + d->len;
- const Property *begin = d->data + newLen;
+ SafeValue *it = d->data + d->len;
+ const SafeValue *begin = d->data + newLen;
while (--it >= begin) {
- if (!it->value.isEmpty() && !d->attrs[it - d->data].isConfigurable()) {
+ if (!it->isEmpty() && !d->attrs[it - d->data].isConfigurable()) {
newLen = it - d->data + 1;
break;
}
- it->value = Primitive::emptyValue();
+ *it = Primitive::emptyValue();
}
}
d->len = newLen;
@@ -233,9 +238,9 @@ bool ArrayData::putArray(ArrayData *d, uint index, SafeValue *values, uint n)
if (index + n > d->alloc)
reserve(d, index + n + 1);
for (uint i = d->len; i < index; ++i)
- d->data[i].value = Primitive::emptyValue();
+ d->data[i] = Primitive::emptyValue();
for (uint i = 0; i < n; ++i)
- d->data[index + i].value = values[i];
+ d->data[index + i] = values[i];
d->len = qMax(d->len, index + n);
return true;
}
@@ -244,8 +249,17 @@ void SparseArrayData::free(ArrayData *d, uint idx)
{
Q_ASSERT(d && d->type == ArrayData::Sparse);
SparseArrayData *dd = static_cast<SparseArrayData *>(d);
- Property &pd = dd->data[idx];
- pd.value.uint_32 = dd->freeList;
+ SafeValue *v = dd->data + idx;
+ if (dd->attrs && dd->attrs[idx].isAccessor()) {
+ // double slot, free both. Order is important, so we have a double slot for allocation again afterwards.
+ v[1].tag = Value::Empty_Type;
+ v[1].uint_32 = dd->freeList;
+ v[0].tag = Value::Empty_Type;
+ v[0].uint_32 = idx + 1;
+ } else {
+ v->tag = Value::Empty_Type;
+ v->uint_32 = dd->freeList;
+ }
dd->freeList = idx;
if (dd->attrs)
dd->attrs[idx].clear();
@@ -266,12 +280,13 @@ void SparseArrayData::reserve(ArrayData *d, uint n)
return;
SparseArrayData *dd = static_cast<SparseArrayData *>(d);
+ uint oldAlloc = dd->alloc;
// ### FIXME
dd->len = dd->alloc;
dd->alloc = qMax(n, 2*dd->alloc);
- Property *newArrayData = new Property[dd->alloc];
+ SafeValue *newArrayData = new SafeValue[dd->alloc];
if (dd->data) {
- memcpy(newArrayData, dd->data, sizeof(Property)*dd->len);
+ memcpy(newArrayData, dd->data, sizeof(SafeValue)*dd->len);
delete [] dd->data;
}
dd->data = newArrayData;
@@ -281,21 +296,41 @@ void SparseArrayData::reserve(ArrayData *d, uint n)
delete [] dd->attrs;
dd->attrs = newAttrs;
}
- for (uint i = dd->freeList; i < dd->alloc; ++i)
- dd->data[i].value = Primitive::fromInt32(i + 1);
+ for (uint i = oldAlloc; i < dd->alloc; ++i)
+ dd->data[i] = Primitive::fromInt32(i + 1);
}
-uint SparseArrayData::allocate(ArrayData *d)
+// double slots are required for accessor properties
+uint SparseArrayData::allocate(ArrayData *d, bool doubleSlot)
{
Q_ASSERT(d->type == ArrayData::Sparse);
SparseArrayData *dd = static_cast<SparseArrayData *>(d);
- uint idx = dd->freeList;
- if (dd->alloc == dd->freeList)
- reserve(d, d->alloc + 1);
- dd->freeList = dd->data[dd->freeList].value.uint_32;
- if (dd->attrs)
- dd->attrs[idx].setType(PropertyAttributes::Data);
- return idx;
+ if (doubleSlot) {
+ uint *last = &dd->freeList;
+ while (1) {
+ if (*last + 1 >= dd->alloc) {
+ reserve(d, d->alloc + 2);
+ last = &dd->freeList;
+ }
+
+ if (dd->data[*last].uint_32 == (*last + 1)) {
+ // found two slots in a row
+ uint idx = *last;
+ *last = dd->data[*last + 1].uint_32;
+ d->attrs[idx] = Attr_Accessor;
+ return idx;
+ }
+ last = &dd->data[*last].uint_32;
+ }
+ } else {
+ if (dd->alloc == dd->freeList)
+ reserve(d, d->alloc + 2);
+ uint idx = dd->freeList;
+ dd->freeList = dd->data[idx].uint_32;
+ if (dd->attrs)
+ dd->attrs[idx] = Attr_Data;
+ return idx;
+ }
}
ReturnedValue SparseArrayData::get(const ArrayData *d, uint index)
@@ -303,16 +338,21 @@ ReturnedValue SparseArrayData::get(const ArrayData *d, uint index)
SparseArrayNode *n = static_cast<const SparseArrayData *>(d)->sparse->findNode(index);
if (!n)
return Primitive::emptyValue().asReturnedValue();
- return d->data[n->value].value.asReturnedValue();
+ return d->data[n->value].asReturnedValue();
}
bool SparseArrayData::put(ArrayData *d, uint index, ValueRef value)
{
- // ### honour attributes
+ if (value->isEmpty())
+ return true;
+
SparseArrayNode *n = static_cast<SparseArrayData *>(d)->sparse->insert(index);
+ Q_ASSERT(n->value == UINT_MAX || !d->attrs || !d->attrs[n->value].isAccessor());
if (n->value == UINT_MAX)
n->value = allocate(d);
- d->data[n->value].value = value;
+ d->data[n->value] = value;
+ if (d->attrs)
+ d->attrs[n->value] = Attr_Data;
return true;
}
@@ -324,22 +364,43 @@ bool SparseArrayData::del(ArrayData *d, uint index)
return true;
uint pidx = n->value;
- Q_ASSERT(!dd->data[pidx].value.isEmpty());
+ Q_ASSERT(!dd->data[pidx].isEmpty());
- if (!dd->attrs || dd->attrs[pidx].isConfigurable()) {
- d->data[pidx].value.int_32 = static_cast<SparseArrayData *>(d)->freeList;
- static_cast<SparseArrayData *>(d)->freeList = pidx;
- static_cast<SparseArrayData *>(d)->sparse->erase(n);
- return true;
+ bool isAccessor = false;
+ if (dd->attrs) {
+ if (!dd->attrs[pidx].isConfigurable())
+ return false;
+
+ isAccessor = dd->attrs[pidx].isAccessor();
+ dd->attrs[pidx] = Attr_Data;
}
- return false;
+
+ if (isAccessor) {
+ // free up both indices
+ d->data[pidx + 1].tag = Value::Undefined_Type;
+ d->data[pidx + 1].uint_32 = static_cast<SparseArrayData *>(d)->freeList;
+ d->data[pidx].tag = Value::Undefined_Type;
+ d->data[pidx].uint_32 = pidx + 1;
+ } else {
+ d->data[pidx].tag = Value::Undefined_Type;
+ d->data[pidx].uint_32 = static_cast<SparseArrayData *>(d)->freeList;
+ }
+
+ static_cast<SparseArrayData *>(d)->freeList = pidx;
+ static_cast<SparseArrayData *>(d)->sparse->erase(n);
+ return true;
}
void SparseArrayData::setAttribute(ArrayData *d, uint index, PropertyAttributes attrs)
{
SparseArrayNode *n = static_cast<SparseArrayData *>(d)->sparse->insert(index);
if (n->value == UINT_MAX)
- n->value = allocate(d);
+ n->value = allocate(d, attrs.isAccessor());
+ else if (attrs.isAccessor() != d->attrs[n->value].isAccessor()) {
+ // need to convert the slot
+ free(d, n->value);
+ n->value = allocate(d, attrs.isAccessor());
+ }
d->attrs[n->value] = attrs;
}
@@ -356,7 +417,7 @@ void SparseArrayData::push_front(ArrayData *d, SafeValue *values, uint n)
Q_ASSERT(!d->attrs);
for (int i = n - 1; i >= 0; --i) {
uint idx = allocate(d);
- d->data[idx].value = values[i];
+ d->data[idx] = values[i];
static_cast<SparseArrayData *>(d)->sparse->push_front(idx);
}
}
@@ -367,7 +428,7 @@ ReturnedValue SparseArrayData::pop_front(ArrayData *d)
uint idx = static_cast<SparseArrayData *>(d)->sparse->pop_front();
ReturnedValue v;
if (idx != UINT_MAX) {
- v = d->data[idx].value.asReturnedValue();
+ v = d->data[idx].asReturnedValue();
SparseArrayData::free(d, idx);
} else {
v = Encode::undefined();
@@ -381,16 +442,13 @@ uint SparseArrayData::truncate(ArrayData *d, uint newLen)
if (begin != static_cast<SparseArrayData *>(d)->sparse->end()) {
SparseArrayNode *it = static_cast<SparseArrayData *>(d)->sparse->end()->previousNode();
while (1) {
- Property &pd = d->data[it->value];
if (d->attrs) {
if (!d->attrs[it->value].isConfigurable()) {
newLen = it->key() + 1;
break;
}
}
- pd.value.tag = Value::Empty_Type;
- pd.value.int_32 = static_cast<SparseArrayData *>(d)->freeList;
- static_cast<SparseArrayData *>(d)->freeList = it->value;
+ free(d, it->value);
bool brk = (it == begin);
SparseArrayNode *prev = it->previousNode();
static_cast<SparseArrayData *>(d)->sparse->erase(it);
@@ -415,7 +473,7 @@ uint ArrayData::append(Object *o, const ArrayObject *otherObj, uint n)
{
ArrayData *d = o->arrayData;
if (!n)
- return d->len;
+ return o->getLength();
const ArrayData *other = otherObj->arrayData;
@@ -424,39 +482,37 @@ uint ArrayData::append(Object *o, const ArrayObject *otherObj, uint n)
d = o->arrayData;
}
- uint oldSize = d->len;
+ uint oldSize = o->getLength();
// ### copy attributes as well!
if (d->type == ArrayData::Sparse) {
if (other->isSparse()) {
- for (const SparseArrayNode *it = static_cast<const SparseArrayData *>(other)->sparse->begin();
- it != static_cast<const SparseArrayData *>(other)->sparse->end(); it = it->nextNode())
- // ### accessor properties
- o->arraySet(d->len + it->key(), other->data[it->value].value);
- } else {
- d->vtable->reserve(d, oldSize + n);
- memcpy(d->data + oldSize, other->data, n*sizeof(Property));
- if (d->attrs)
- std::fill(d->attrs + oldSize, d->attrs + oldSize + n, PropertyAttributes(Attr_Data));
- for (uint i = 0; i < n; ++i) {
- SparseArrayNode *n = static_cast<SparseArrayData *>(d)->sparse->insert(d->len + i);
- n->value = oldSize + i;
+ if (otherObj->hasAccessorProperty && other->hasAttributes()) {
+ for (const SparseArrayNode *it = static_cast<const SparseArrayData *>(other)->sparse->begin();
+ it != static_cast<const SparseArrayData *>(other)->sparse->end(); it = it->nextNode())
+ o->arraySet(oldSize + it->key(), *reinterpret_cast<Property *>(other->data + it->value), other->attrs[it->value]);
+ } else {
+ for (const SparseArrayNode *it = static_cast<const SparseArrayData *>(other)->sparse->begin();
+ it != static_cast<const SparseArrayData *>(other)->sparse->end(); it = it->nextNode())
+ o->arraySet(oldSize + it->key(), other->data[it->value]);
}
+ } else {
+ d->put(oldSize, other->data, n);
}
} else if (other->length()) {
d->vtable->reserve(d, oldSize + other->length());
if (oldSize > d->len) {
for (uint i = d->len; i < oldSize; ++i)
- d->data[i].value = Primitive::emptyValue();
+ d->data[i] = Primitive::emptyValue();
}
if (other->attrs) {
for (uint i = 0; i < other->len; ++i) {
bool exists;
- d->data[oldSize + i].value = const_cast<ArrayObject *>(otherObj)->getIndexed(i, &exists);
+ d->data[oldSize + i] = const_cast<ArrayObject *>(otherObj)->getIndexed(i, &exists);
d->len = oldSize + i + 1;
o->arrayData->setAttributes(oldSize + i, Attr_Data);
if (!exists)
- d->data[oldSize + i].value = Primitive::emptyValue();
+ d->data[oldSize + i] = Primitive::emptyValue();
}
} else {
d->len = oldSize + other->len;
@@ -469,95 +525,134 @@ uint ArrayData::append(Object *o, const ArrayObject *otherObj, uint n)
return oldSize + n;
}
-Property *ArrayData::insert(Object *o, uint index)
+Property *ArrayData::insert(Object *o, uint index, bool isAccessor)
{
Property *pd;
if (o->arrayData->type != ArrayData::Sparse && (index < 0x1000 || index < o->arrayData->len + (o->arrayData->len >> 2))) {
+ Q_ASSERT(!isAccessor);
if (index >= o->arrayData->alloc)
o->arrayReserve(index + 1);
if (index >= o->arrayData->len) {
// mark possible hole in the array
for (uint i = o->arrayData->len; i < index; ++i)
- o->arrayData->data[i].value = Primitive::emptyValue();
+ o->arrayData->data[i] = Primitive::emptyValue();
o->arrayData->len = index + 1;
}
- pd = o->arrayData->data + index;
+ pd = reinterpret_cast<Property *>(o->arrayData->data + index);
} else {
o->initSparseArray();
SparseArrayNode *n = static_cast<SparseArrayData *>(o->arrayData)->sparse->insert(index);
if (n->value == UINT_MAX)
- n->value = SparseArrayData::allocate(o->arrayData);
- pd = o->arrayData->data + n->value;
+ n->value = SparseArrayData::allocate(o->arrayData, isAccessor);
+ pd = reinterpret_cast<Property *>(o->arrayData->data + n->value);
}
return pd;
}
void ArrayData::markObjects(ExecutionEngine *e)
{
- if (type == ArrayData::Simple) {
- for (uint i = 0; i < len; ++i)
- data[i].value.mark(e);
- return;
- } else {
- for (uint i = 0; i < len; ++i) {
- const Property &pd = data[i];
- if (attrs && attrs[i].isAccessor()) {
- if (pd.getter())
- pd.getter()->mark(e);
- if (pd.setter())
- pd.setter()->mark(e);
- } else {
- pd.value.mark(e);
- }
- }
- }
-
+ for (uint i = 0; i < len; ++i)
+ data[i].mark(e);
}
void ArrayData::sort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint len)
{
+ if (!len)
+ return;
+
ArrayData *d = thisObject->arrayData;
- if (!d || !d->len)
+ if (!d || (!d->len && d->type != ArrayData::Sparse))
return;
- if (d->type == ArrayData::Sparse) {
- context->throwUnimplemented(QStringLiteral("Object::sort unimplemented for sparse arrays"));
+ if (!(comparefn->isUndefined() || comparefn->asObject())) {
+ context->throwTypeError();
return;
}
- if (len > d->len)
- len = d->len;
-
// The spec says the sorting goes through a series of get,put and delete operations.
// this implies that the attributes don't get sorted around.
- // behavior of accessor properties is implementation defined. We simply turn them all
- // into data properties and then sort. This is in line with the sentence above.
- if (d->attrs) {
+
+ if (d->type == ArrayData::Sparse) {
+ // since we sort anyway, we can simply iterate over the entries in the sparse
+ // array and append them one by one to a regular one.
+ SparseArrayData *sparse = static_cast<SparseArrayData *>(d);
+
+ if (!sparse->sparse->nEntries())
+ return;
+
+ thisObject->arrayData = new ArrayData;
+ d = thisObject->arrayData;
+ d->vtable->reserve(d, sparse->sparse->nEntries());
+
+ SparseArrayNode *n = sparse->sparse->begin();
+ uint i = 0;
+ if (sparse->attrs) {
+ d->ensureAttributes();
+ while (n != sparse->sparse->end()) {
+ if (n->value >= len)
+ break;
+
+ PropertyAttributes a = sparse->attrs ? sparse->attrs[n->value] : Attr_Data;
+ d->data[i] = thisObject->getValue(reinterpret_cast<Property *>(sparse->data + n->value), a);
+ d->attrs[i] = a.isAccessor() ? Attr_Data : a;
+
+ n = n->nextNode();
+ ++i;
+ }
+ } else {
+ while (n != sparse->sparse->end()) {
+ if (n->value >= len)
+ break;
+ d->data[i] = sparse->data[n->value];
+ n = n->nextNode();
+ ++i;
+ }
+ }
+ d->len = i;
+ if (len > i)
+ len = i;
+ if (n != sparse->sparse->end()) {
+ // have some entries outside the sort range that we need to ignore when sorting
+ thisObject->initSparseArray();
+ d = thisObject->arrayData;
+ while (n != sparse->sparse->end()) {
+ PropertyAttributes a = sparse->attrs ? sparse->attrs[n->value] : Attr_Data;
+ thisObject->arraySet(n->value, *reinterpret_cast<Property *>(sparse->data + n->value), a);
+
+ n = n->nextNode();
+ }
+
+ }
+
+ sparse->ArrayData::free();
+ } else {
+ if (len > d->len)
+ len = d->len;
+
+ // sort empty values to the end
for (uint i = 0; i < len; i++) {
- if (d->data[i].value.isEmpty()) {
+ if (d->data[i].isEmpty()) {
while (--len > i)
- if (!d->data[len].value.isEmpty())
+ if (!d->data[len].isEmpty())
break;
- d->data[i].value = thisObject->getValue(d->data + len, d->attrs ? d->attrs[len] : Attr_Data);
- if (d->attrs)
- d->attrs[i] = Attr_Data;
- d->data[len].value = Primitive::emptyValue();
- } else if (d->attrs && d->attrs[i].isAccessor()) {
- d->data[i].value = thisObject->getValue(d->data + i, d->attrs[i]);
- d->attrs[i] = Attr_Data;
+ Q_ASSERT(!d->attrs || !d->attrs[len].isAccessor());
+ d->data[i] = d->data[len];
+ d->data[len] = Primitive::emptyValue();
}
}
- }
- if (!(comparefn->isUndefined() || comparefn->asObject())) {
- context->throwTypeError();
- return;
+ if (!len)
+ return;
}
+
ArrayElementLessThan lessThan(context, thisObject, comparefn);
- if (!len)
- return;
- Property *begin = d->data;
+ SafeValue *begin = d->data;
std::sort(begin, begin + len, lessThan);
+
+#ifdef CHECK_SPARSE_ARRAYS
+ thisObject->initSparseArray();
+#endif
+
}
diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h
index 98968fada2..3e0901061c 100644
--- a/src/qml/jsruntime/qv4arraydata_p.h
+++ b/src/qml/jsruntime/qv4arraydata_p.h
@@ -95,7 +95,7 @@ struct Q_QML_EXPORT ArrayData
uint alloc;
uint type;
PropertyAttributes *attrs;
- Property *data;
+ SafeValue *data;
bool isSparse() const { return this && type == Sparse; }
@@ -170,7 +170,7 @@ struct Q_QML_EXPORT ArrayData
static void sort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint dataLen);
static uint append(Object *o, const ArrayObject *otherObj, uint n);
- static Property *insert(Object *o, uint index);
+ static Property *insert(Object *o, uint index, bool isAccessor = false);
void markObjects(ExecutionEngine *e);
static void getHeadRoom(ArrayData *d);
@@ -200,7 +200,7 @@ struct Q_QML_EXPORT SparseArrayData : public ArrayData
uint freeList;
SparseArray *sparse;
- static uint allocate(ArrayData *d);
+ static uint allocate(ArrayData *d, bool doubleSlot = false);
static void free(ArrayData *d, uint idx);
static void freeData(ArrayData *d);
@@ -224,14 +224,14 @@ inline Property *ArrayData::getProperty(uint index) const
if (!this)
return 0;
if (type != Sparse) {
- if (index >= len || data[index].value.isEmpty())
+ if (index >= len || data[index].isEmpty())
return 0;
- return data + index;
+ return reinterpret_cast<Property *>(data + index);
} else {
SparseArrayNode *n = static_cast<const SparseArrayData *>(this)->sparse->findNode(index);
if (!n)
return 0;
- return data + n->value;
+ return reinterpret_cast<Property *>(data + n->value);
}
}
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index eb8a5301de..4ea979d16e 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -613,7 +613,7 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx)
ScopedValue value(scope);
- if ((instance->arrayData && instance->arrayType() != ArrayData::Simple) || instance->protoHasArray()) {
+ if (instance->hasAccessorProperty || (instance->arrayType() >= ArrayData::Sparse) || instance->protoHasArray()) {
// lets be safe and slow
for (uint i = fromIndex; i < len; ++i) {
bool exists;
@@ -625,31 +625,22 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx)
}
} else if (!instance->arrayData) {
return Encode(-1);
- } else if (instance->arrayType() == ArrayData::Sparse) {
- for (SparseArrayNode *n = static_cast<SparseArrayData *>(instance->arrayData)->sparse->lowerBound(fromIndex);
- n != static_cast<SparseArrayData *>(instance->arrayData)->sparse->end() && n->key() < len; n = n->nextNode()) {
- value = instance->getValue(instance->arrayData->data + n->value,
- instance->arrayData->attrs ? instance->arrayData->attrs[n->value] : Attr_Data);
- if (scope.hasException())
- return Encode::undefined();
- if (__qmljs_strict_equal(value, searchValue))
- return Encode(n->key());
- }
} else {
+ Q_ASSERT(instance->arrayType() == ArrayData::Simple || instance->arrayType() == ArrayData::Complex);
if (len > instance->arrayData->length())
len = instance->arrayData->length();
- Property *pd = instance->arrayData->data;
- Property *end = pd + len;
- pd += fromIndex;
- while (pd < end) {
- if (!pd->value.isEmpty()) {
- value = instance->getValue(pd, instance->arrayData->attributes(pd - instance->arrayData->data));
+ SafeValue *val = instance->arrayData->data;
+ SafeValue *end = val + len;
+ val += fromIndex;
+ while (val < end) {
+ if (!val->isEmpty()) {
+ value = *val;
if (scope.hasException())
return Encode::undefined();
if (__qmljs_strict_equal(value, searchValue))
- return Encode((uint)(pd - instance->arrayData->data));
+ return Encode((uint)(val - instance->arrayData->data));
}
- ++pd;
+ ++val;
}
}
return Encode(-1);
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index daa3d5b0de..858d625725 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -351,8 +351,8 @@ ReturnedValue FunctionPrototype::method_apply(CallContext *ctx)
callData->args[i] = arr->getIndexed(i);
} else {
int alen = qMin(len, arr->arrayData->length());
- for (int i = 0; i < alen; ++i)
- callData->args[i] = arr->arrayData->data[i].value;
+ if (alen)
+ memcpy(callData->args, arr->arrayData->data, alen*sizeof(SafeValue));
for (quint32 i = alen; i < len; ++i)
callData->args[i] = Primitive::undefinedValue();
}
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index 4d02d46135..f3a4b8a5aa 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -229,14 +229,9 @@ void Object::markObjects(Managed *that, ExecutionEngine *e)
} else {
for (uint i = 0; i < o->internalClass->size; ++i) {
const Property &pd = o->memberData[i];
- if (o->internalClass->propertyData[i].isAccessor()) {
- if (pd.getter())
- pd.getter()->mark(e);
- if (pd.setter())
- pd.setter()->mark(e);
- } else {
- pd.value.mark(e);
- }
+ pd.value.mark(e);
+ if (o->internalClass->propertyData[i].isAccessor())
+ pd.set.mark(e);
}
}
if (o->arrayData)
@@ -562,7 +557,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, StringRef name, uin
while (it->arrayNode != o->sparseEnd()) {
int k = it->arrayNode->key();
uint pidx = it->arrayNode->value;
- Property *p = o->arrayData->data + pidx;
+ Property *p = reinterpret_cast<Property *>(o->arrayData->data + pidx);
it->arrayNode = it->arrayNode->nextNode();
PropertyAttributes a = o->arrayData->attributes(k);
if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) {
@@ -578,14 +573,14 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, StringRef name, uin
}
// dense arrays
while (it->arrayIndex < o->arrayData->length()) {
- Property *p = o->arrayData->data + it->arrayIndex;
+ SafeValue *val = o->arrayData->data + it->arrayIndex;
PropertyAttributes a = o->arrayData->attributes(it->arrayIndex);
++it->arrayIndex;
- if (!p->value.isEmpty()
+ if (!val->isEmpty()
&& (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable())) {
*index = it->arrayIndex - 1;
*attrs = a;
- *pd = *p;
+ pd->value = *val;
return;
}
}
@@ -874,6 +869,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, const StringRef name,
Scope scope(ctx);
Property *current;
PropertyAttributes *cattrs;
+ uint memberIndex;
if (isArrayObject() && name->equals(ctx->engine->id_length)) {
assert(ArrayObject::LengthPropertyIndex == internalClass->find(ctx->engine->id_length));
@@ -904,11 +900,9 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, const StringRef name,
}
// Clause 1
- {
- uint member = internalClass->find(name.getPointer());
- current = (member < UINT_MAX) ? memberData + member : 0;
- cattrs = internalClass->propertyData.constData() + member;
- }
+ memberIndex = internalClass->find(name.getPointer());
+ current = (memberIndex < UINT_MAX) ? memberData + memberIndex : 0;
+ cattrs = internalClass->propertyData.constData() + memberIndex;
if (!current) {
// clause 3
@@ -921,7 +915,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, const StringRef name,
return true;
}
- return __defineOwnProperty__(ctx, current, name, p, attrs);
+ return __defineOwnProperty__(ctx, memberIndex, name, p, attrs);
reject:
if (ctx->strictMode)
ctx->throwTypeError();
@@ -972,24 +966,28 @@ bool Object::defineOwnProperty2(ExecutionContext *ctx, uint index, const Propert
return true;
}
- return __defineOwnProperty__(ctx, current, StringRef::null(), p, attrs);
+ return __defineOwnProperty__(ctx, index, StringRef::null(), p, attrs);
reject:
if (ctx->strictMode)
ctx->throwTypeError();
return false;
}
-bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, const StringRef member, const Property &p, PropertyAttributes attrs)
+bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const StringRef member, const Property &p, PropertyAttributes attrs)
{
// clause 5
if (attrs.isEmpty())
return true;
+ Property *current;
PropertyAttributes cattrs;
- if (!member.isNull())
- cattrs = internalClass->propertyData[current - memberData];
- else
- cattrs = arrayData->attrs ? arrayData->attrs[current - arrayData->data] : Attr_Data;
+ if (!member.isNull()) {
+ current = memberData + index;
+ cattrs = internalClass->propertyData[index];
+ } else {
+ current = arrayData->getProperty(index);
+ cattrs = arrayData->attributes(index);
+ }
// clause 6
if (p.isSubset(attrs, *current, cattrs))
@@ -1016,12 +1014,23 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, con
// 9b
cattrs.setType(PropertyAttributes::Accessor);
cattrs.clearWritable();
+ if (member.isNull()) {
+ // need to convert the array and the slot
+ initSparseArray();
+ arrayData->setAttributes(index, cattrs);
+ current = arrayData->getProperty(index);
+ }
current->setGetter(0);
current->setSetter(0);
} else {
// 9c
cattrs.setType(PropertyAttributes::Data);
cattrs.setWritable(false);
+ if (member.isNull()) {
+ // need to convert the array and the slot
+ arrayData->setAttributes(index, cattrs);
+ current = arrayData->getProperty(index);
+ }
current->value = Primitive::undefinedValue();
}
} else if (cattrs.isData() && attrs.isData()) { // clause 10
@@ -1030,11 +1039,11 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, con
goto reject;
}
} else { // clause 10
- assert(cattrs.isAccessor() && attrs.isAccessor());
+ Q_ASSERT(cattrs.isAccessor() && attrs.isAccessor());
if (!cattrs.isConfigurable()) {
- if (p.getter() && !(current->getter() == p.getter() || (!current->getter() && (quintptr)p.getter() == 0x1)))
+ if (!p.value.isEmpty() && current->value.val != p.value.val)
goto reject;
- if (p.setter() && !(current->setter() == p.setter() || (!current->setter() && (quintptr)p.setter() == 0x1)))
+ if (!p.set.isEmpty() && current->set.val != p.set.val)
goto reject;
}
}
@@ -1045,9 +1054,9 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, con
if (!member.isNull()) {
internalClass = internalClass->changeMember(member.getPointer(), cattrs);
} else {
- arrayData->setAttributes(current - arrayData->data, cattrs);
+ arrayData->setAttributes(index, cattrs);
}
- if (attrs.isAccessor())
+ if (cattrs.isAccessor())
hasAccessorProperty = 1;
return true;
reject:
@@ -1078,6 +1087,13 @@ void Object::copyArrayData(Object *other)
for (uint i = 0; i < len; ++i) {
arraySet(i, (v = other->getIndexed(i)));
}
+ } else if (!other->arrayData) {
+ ;
+ } else if (other->hasAccessorProperty && other->arrayData->attrs && other->arrayData->isSparse()){
+ // do it the slow way
+ for (const SparseArrayNode *it = static_cast<const SparseArrayData *>(other->arrayData)->sparse->begin();
+ it != static_cast<const SparseArrayData *>(other->arrayData)->sparse->end(); it = it->nextNode())
+ arraySet(it->key(), *reinterpret_cast<Property *>(other->arrayData->data + it->value), other->arrayData->attrs[it->value]);
} else {
Q_ASSERT(!arrayData && other->arrayData);
if (other->arrayType() == ArrayData::Sparse) {
@@ -1087,11 +1103,11 @@ void Object::copyArrayData(Object *other)
dd->sparse = new SparseArray(*od->sparse);
dd->freeList = od->freeList;
arrayData = dd;
+ other->arrayData->len = other->arrayData->alloc;
}
arrayReserve(other->arrayData->len);
arrayData->len = other->arrayData->len;
- // ### correctly deal with accessor properties
- memcpy(arrayData->data, other->arrayData->data, arrayData->len*sizeof(Property));
+ memcpy(arrayData->data, other->arrayData->data, arrayData->len*sizeof(SafeValue));
arrayData->offset = 0;
}
@@ -1152,21 +1168,25 @@ void Object::initSparseArray()
uint *lastFree = &data->freeList;
for (uint i = 0; i < oldOffset; ++i) {
*lastFree = i;
- lastFree = &data->data[i].value.uint_32;
+ data->data[i].tag = Value::Empty_Type;
+ lastFree = &data->data[i].uint_32;
}
for (uint i = 0; i < data->len; ++i) {
- if (!data->data[i + oldOffset].value.isEmpty()) {
+ if (!data->data[i + oldOffset].isEmpty()) {
SparseArrayNode *n = data->sparse->insert(i);
n->value = i + oldOffset;
} else {
*lastFree = i + oldOffset;
- lastFree = &data->data[i + oldOffset].value.uint_32;
+ data->data[i + oldOffset].tag = Value::Empty_Type;
+ lastFree = &data->data[i + oldOffset].uint_32;
}
}
for (uint i = data->len + oldOffset; i < data->alloc; ++i) {
*lastFree = i;
- lastFree = &data->data[i].value.uint_32;
+ data->data[i].tag = Value::Empty_Type;
+ lastFree = &data->data[i].uint_32;
}
+ *lastFree = data->alloc;
arrayData = data;
}
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index f99191a24c..de44d5cfd3 100644
--- a/src/qml/jsruntime/qv4object_p.h
+++ b/src/qml/jsruntime/qv4object_p.h
@@ -136,7 +136,7 @@ struct Q_QML_EXPORT Object: Managed {
bool hasOwnProperty(const StringRef name) const;
bool hasOwnProperty(uint index) const;
- bool __defineOwnProperty__(ExecutionContext *ctx, Property *current, const StringRef member, const Property &p, PropertyAttributes attrs);
+ bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const StringRef member, const Property &p, PropertyAttributes attrs);
bool __defineOwnProperty__(ExecutionContext *ctx, const StringRef name, const Property &p, PropertyAttributes attrs);
bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs);
bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs);
@@ -209,17 +209,15 @@ public:
void arrayCreate() {
if (!arrayData)
arrayData = new ArrayData;
+#ifdef CHECK_SPARSE_ARRAYS
+ initSparseArray();
+#endif
}
void initSparseArray();
SparseArrayNode *sparseBegin() { return arrayType() == ArrayData::Sparse ? static_cast<SparseArrayData *>(arrayData)->sparse->begin() : 0; }
SparseArrayNode *sparseEnd() { return arrayType() == ArrayData::Sparse ? static_cast<SparseArrayData *>(arrayData)->sparse->end() : 0; }
- inline Property *arrayInsert(uint index) {
- arrayCreate();
- return ArrayData::insert(this, index);
- }
-
inline bool protoHasArray() {
Scope scope(engine());
Scoped<Object> p(scope, this);
@@ -344,7 +342,6 @@ inline void Object::setArrayLengthUnchecked(uint l)
inline void Object::push_back(const ValueRef v)
{
arrayCreate();
- Q_ASSERT(!arrayData->isSparse());
uint idx = getLength();
arrayReserve(idx + 1);
@@ -355,12 +352,21 @@ inline void Object::push_back(const ValueRef v)
inline void Object::arraySet(uint index, const Property &p, PropertyAttributes attributes)
{
- if (attributes.isAccessor())
+ // ### Clean up
+ arrayCreate();
+ if (attributes.isAccessor()) {
hasAccessorProperty = 1;
-
- Property *pd = arrayInsert(index);
- *pd = p;
+ initSparseArray();
+ } else if (index > 0x1000 && index > 2*arrayData->alloc) {
+ initSparseArray();
+ } else {
+ arrayData->vtable->reserve(arrayData, index + 1);
+ }
arrayData->setAttributes(index, attributes);
+ Property *pd = ArrayData::insert(this, index, attributes.isAccessor());
+ pd->value = p.value;
+ if (attributes.isAccessor())
+ pd->set = p.set;
if (isArrayObject() && index >= getLength())
setArrayLengthUnchecked(index + 1);
}
@@ -368,7 +374,11 @@ inline void Object::arraySet(uint index, const Property &p, PropertyAttributes a
inline void Object::arraySet(uint index, ValueRef value)
{
- Property *pd = arrayInsert(index);
+ arrayCreate();
+ if (index > 0x1000 && index > 2*arrayData->alloc) {
+ initSparseArray();
+ }
+ Property *pd = ArrayData::insert(this, index);
pd->value = value ? *value : Primitive::undefinedValue();
if (isArrayObject() && index >= getLength())
setArrayLengthUnchecked(index + 1);
diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp
index 410f243a3c..1096aa1b2d 100644
--- a/src/qml/jsruntime/qv4objectproto.cpp
+++ b/src/qml/jsruntime/qv4objectproto.cpp
@@ -597,8 +597,8 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, const ValueRef
}
attrs->clear();
- desc->setGetter(0);
- desc->setSetter(0);
+ desc->value = Primitive::emptyValue();
+ desc->set = Primitive::emptyValue();
ScopedValue tmp(scope);
if (o->hasProperty(ctx->engine->id_enumerable))
@@ -610,10 +610,8 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, const ValueRef
if (o->hasProperty(ctx->engine->id_get)) {
ScopedValue get(scope, o->get(ctx->engine->id_get));
FunctionObject *f = get->asFunctionObject();
- if (f) {
- desc->setGetter(f);
- } else if (get->isUndefined()) {
- desc->setGetter((FunctionObject *)0x1);
+ if (f || get->isUndefined()) {
+ desc->value = get;
} else {
ctx->throwTypeError();
return;
@@ -624,10 +622,8 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, const ValueRef
if (o->hasProperty(ctx->engine->id_set)) {
ScopedValue set(scope, o->get(ctx->engine->id_set));
FunctionObject *f = set->asFunctionObject();
- if (f) {
- desc->setSetter(f);
- } else if (set->isUndefined()) {
- desc->setSetter((FunctionObject *)0x1);
+ if (f || set->isUndefined()) {
+ desc->set = set;
} else {
ctx->throwTypeError();
return;
diff --git a/src/qml/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h
index 4b0896b264..6381fe7687 100644
--- a/src/qml/jsruntime/qv4property_p.h
+++ b/src/qml/jsruntime/qv4property_p.h
@@ -62,10 +62,10 @@ struct Property {
}
if (attrs->type() == PropertyAttributes::Accessor) {
attrs->clearWritable();
- if (value.managed() == (Managed *)0x1)
- value = Primitive::fromManaged(0);
- if (set.managed() == (Managed *)0x1)
- set = Primitive::fromManaged(0);
+ if (value.isEmpty())
+ value = Primitive::undefinedValue();
+ if (set.isEmpty())
+ set = Primitive::undefinedValue();
}
attrs->resolve();
}
@@ -91,8 +91,8 @@ struct Property {
inline bool isSubset(const PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs) const;
inline void merge(PropertyAttributes &attrs, const Property &other, PropertyAttributes otherAttrs);
- inline FunctionObject *getter() const { return reinterpret_cast<FunctionObject *>(value.managed()); }
- inline FunctionObject *setter() const { return reinterpret_cast<FunctionObject *>(set.managed()); }
+ inline FunctionObject *getter() const { return reinterpret_cast<FunctionObject *>(value.asManaged()); }
+ inline FunctionObject *setter() const { return reinterpret_cast<FunctionObject *>(set.asManaged()); }
inline void setGetter(FunctionObject *g) { value = Primitive::fromManaged(reinterpret_cast<Managed *>(g)); }
inline void setSetter(FunctionObject *s) { set = Primitive::fromManaged(reinterpret_cast<Managed *>(s)); }
};
@@ -110,9 +110,9 @@ inline bool Property::isSubset(const PropertyAttributes &attrs, const Property &
if (attrs.type() == PropertyAttributes::Data && !value.sameValue(other.value))
return false;
if (attrs.type() == PropertyAttributes::Accessor) {
- if (value.managed() != other.value.managed())
+ if (value.asManaged() != other.value.asManaged())
return false;
- if (set.managed() != other.set.managed())
+ if (set.asManaged() != other.set.asManaged())
return false;
}
return true;
@@ -128,10 +128,10 @@ inline void Property::merge(PropertyAttributes &attrs, const Property &other, Pr
attrs.setWritable(otherAttrs.isWritable());
if (otherAttrs.type() == PropertyAttributes::Accessor) {
attrs.setType(PropertyAttributes::Accessor);
- if (other.value.managed())
- value = (other.value.managed() == (Managed *)0x1) ? Primitive::fromManaged(0).asReturnedValue() : other.value.asReturnedValue();
- if (other.set.managed())
- set = (other.set.managed() == (Managed *)0x1) ? Primitive::fromManaged(0).asReturnedValue() : other.set.asReturnedValue();
+ if (!other.value.isEmpty())
+ value = other.value;
+ if (!other.set.isEmpty())
+ set = other.set;
} else if (otherAttrs.type() == PropertyAttributes::Data){
attrs.setType(PropertyAttributes::Data);
value = other.value;
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 4c7ec6602d..a8b453309d 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -1120,8 +1120,8 @@ void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const ValueRef
uint idx = name->asArrayIndex();
Property pd;
- pd.setGetter(getter ? getter->asFunctionObject() : 0);
- pd.setSetter(setter ? setter->asFunctionObject() : 0);
+ pd.value = getter;
+ pd.set = setter;
if (idx != UINT_MAX) {
o->arraySet(idx, pd, Attr_Accessor);
} else {
@@ -1139,9 +1139,9 @@ ReturnedValue __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx,
if (klass->propertyData[i].isData())
o->memberData[i].value = *args++;
else {
- o->memberData[i].setGetter(args->asFunctionObject());
+ o->memberData[i].value = *args;
args++;
- o->memberData[i].setSetter(args->asFunctionObject());
+ o->memberData[i].set = *args;
args++;
}
}
diff --git a/src/qml/jsruntime/qv4sparsearray.cpp b/src/qml/jsruntime/qv4sparsearray.cpp
index 97dd695067..7169f5c20e 100644
--- a/src/qml/jsruntime/qv4sparsearray.cpp
+++ b/src/qml/jsruntime/qv4sparsearray.cpp
@@ -53,13 +53,13 @@
using namespace QV4;
-bool ArrayElementLessThan::operator()(const Property &p1, const Property &p2) const
+bool ArrayElementLessThan::operator()(const SafeValue &v1, const SafeValue &v2) const
{
Scope scope(m_context);
- if (p1.value.isUndefined() || p1.value.isEmpty())
+ if (v1.isUndefined() || v1.isEmpty())
return false;
- if (p2.value.isUndefined() || p2.value.isEmpty())
+ if (v2.isUndefined() || v2.isEmpty())
return true;
ScopedObject o(scope, m_comparefn);
if (o) {
@@ -67,14 +67,14 @@ bool ArrayElementLessThan::operator()(const Property &p1, const Property &p2) co
ScopedValue result(scope);
ScopedCallData callData(scope, 2);
callData->thisObject = Primitive::undefinedValue();
- callData->args[0] = p1.value;
- callData->args[1] = p2.value;
+ callData->args[0] = v1;
+ callData->args[1] = v2;
result = __qmljs_call_value(m_context, m_comparefn, callData);
return result->toNumber() < 0;
}
- ScopedString p1s(scope, p1.value.toString(m_context));
- ScopedString p2s(scope, p2.value.toString(m_context));
+ ScopedString p1s(scope, v1.toString(m_context));
+ ScopedString p2s(scope, v2.toString(m_context));
return p1s->toQString() < p2s->toQString();
}
diff --git a/src/qml/jsruntime/qv4sparsearray_p.h b/src/qml/jsruntime/qv4sparsearray_p.h
index f2ad167a17..6c2808b6b1 100644
--- a/src/qml/jsruntime/qv4sparsearray_p.h
+++ b/src/qml/jsruntime/qv4sparsearray_p.h
@@ -68,7 +68,7 @@ public:
inline ArrayElementLessThan(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn)
: m_context(context), thisObject(thisObject), m_comparefn(comparefn) {}
- bool operator()(const Property &v1, const Property &v2) const;
+ bool operator()(const SafeValue &v1, const SafeValue &v2) const;
private:
ExecutionContext *m_context;
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index dfd093caed..b9ec4202eb 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -140,7 +140,7 @@ void StringObject::advanceIterator(Managed *m, ObjectIterator *it, StringRef nam
name = (String *)0;
StringObject *s = static_cast<StringObject *>(m);
uint slen = s->value.stringValue()->toQString().length();
- if (it->arrayIndex < slen) {
+ if (it->arrayIndex <= slen) {
while (it->arrayIndex < slen) {
*index = it->arrayIndex;
++it->arrayIndex;
diff --git a/tests/manual/v4/sparsearraytest.js b/tests/manual/v4/sparsearraytest.js
index 921a750472..3d7adfe903 100644
--- a/tests/manual/v4/sparsearraytest.js
+++ b/tests/manual/v4/sparsearraytest.js
@@ -1,10 +1,16 @@
var max
for (max = 2; max < 100; ++max) {
var arr = [];
- arr[10000000] = -1
+ // force a sparse array
+ Object.defineProperty(arr, "0", {
+ get: function () {
+ return 0;
+ },
+ configurable: true
+ });
var i;
var j;
- for (i = 0; i < max; ++i)
+ for (i = 1; i < max; ++i)
arr[i] = i;
for (i = 1; i < max; i += 2) {
delete arr[i];
@@ -19,3 +25,31 @@ for (max = 2; max < 100; ++max) {
}
}
}
+
+for (max = 2; max < 100; ++max) {
+ var arr = [];
+ // force a sparse array
+ Object.defineProperty(arr, "0", {
+ get: function () {
+ return 0;
+ },
+ configurable: true
+ });
+
+ var i;
+ var j;
+ for (i = 1; i < max; ++i)
+ arr[i] = i;
+ for (i = 0; i < max; i += 2) {
+ delete arr[i];
+ for (j = 0; j < max; ++j) {
+ if (j <= i && !(j %2)) {
+ if (arr[j] != undefined)
+ throw "err1 " + i + " " + j + " " + arr[j]
+ } else {
+ if (arr[j] != j)
+ throw "err2 " + j
+ }
+ }
+ }
+}