Rework array handling for JS objects

Split up ArrayData into two classes, one for regular
arrays, one for sparse arrays and cleanly separate
the two cases. Only create array data on demand.

Change-Id: I9ca8d0b53592174f213ba0f20caf93e77dba690a
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
This commit is contained in:
Lars Knoll 2013-12-16 09:16:57 +01:00 committed by The Qt Project
parent 5cf95512af
commit 459c9a2a88
36 changed files with 1307 additions and 811 deletions

View File

@ -281,7 +281,7 @@ static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx)
ScopedValue values(scope, ctx->callData->args[1]);
if (values->asArrayObject()) {
ScopedArrayObject array(scope, values);
quint32 size = array->arrayLength();
quint32 size = array->getLength();
QV4::ScopedValue v(scope);
for (quint32 ii = 0; ii < size; ++ii)
query.bindValue(ii, engine->toVariant((v = array->getIndexed(ii)), -1));

View File

@ -1015,7 +1015,7 @@ void InstructionSelection::setQObjectProperty(V4IR::Expr *source, V4IR::Expr *ta
void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target)
{
#if QT_POINTER_SIZE == 8
#if 0 // QT_POINTER_SIZE == 8
V4IR::Temp *tbase = base->asTemp();
V4IR::Temp *tindex = index->asTemp();
if (tbase && tindex &&
@ -1026,9 +1026,10 @@ void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR:
_as->urshift64(Assembler::TrustedImm32(QV4::Value::IsManaged_Shift), Assembler::ReturnValueRegister);
Assembler::Jump notManaged = _as->branch64(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm64(0));
// check whether we have an object with a simple array
Assembler::Address managedType(Assembler::ScratchRegister, qOffsetOf(QV4::Managed, flags));
// ### need to check we have an object first!
Assembler::Address managedType(Assembler::ScratchRegister, qOffsetOf(QV4::Object, arrayData) + qOffsetOf(QV4::ArrayData, flags));
_as->load8(managedType, Assembler::ReturnValueRegister);
_as->and32(Assembler::TrustedImm32(QV4::Managed::SimpleArray), Assembler::ReturnValueRegister);
_as->and32(Assembler::TrustedImm32(QV4::ArrayData::SimpleArray), Assembler::ReturnValueRegister);
Assembler::Jump notSimple = _as->branch32(Assembler::Equal, Assembler::ReturnValueRegister, Assembler::TrustedImm32(0));
bool needNegativeCheck = false;
@ -1069,12 +1070,12 @@ void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR:
// get data, ScratchRegister holds index
addr = _as->loadTempAddress(Assembler::ReturnValueRegister, tbase);
_as->load64(addr, Assembler::ReturnValueRegister);
Address dataLen(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayData) + qOffsetOf(Object::ArrayData, length));
Address dataLen(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayData) + qOffsetOf(ArrayData, length));
Assembler::Jump outOfRange;
if (needNegativeCheck)
outOfRange = _as->branch32(Assembler::LessThan, Assembler::ScratchRegister, Assembler::TrustedImm32(0));
Assembler::Jump outOfRange2 = _as->branch32(Assembler::GreaterThanOrEqual, Assembler::ScratchRegister, dataLen);
Address arrayData(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayData) + qOffsetOf(Object::ArrayData, data));
Address arrayData(Assembler::ReturnValueRegister, qOffsetOf(Object, arrayData) + qOffsetOf(ArrayData, data));
_as->load64(arrayData, Assembler::ReturnValueRegister);
Q_ASSERT(sizeof(Property) == (1<<4));
_as->lshift64(Assembler::TrustedImm32(4), Assembler::ScratchRegister);

View File

@ -14,6 +14,7 @@ SOURCES += \
$$PWD/qv4managed.cpp \
$$PWD/qv4internalclass.cpp \
$$PWD/qv4sparsearray.cpp \
$$PWD/qv4arraydata.cpp \
$$PWD/qv4arrayobject.cpp \
$$PWD/qv4argumentsobject.cpp \
$$PWD/qv4booleanobject.cpp \
@ -58,6 +59,7 @@ HEADERS += \
$$PWD/qv4managed_p.h \
$$PWD/qv4internalclass_p.h \
$$PWD/qv4sparsearray_p.h \
$$PWD/qv4arraydata_p.h \
$$PWD/qv4arrayobject_p.h \
$$PWD/qv4argumentsobject_p.h \
$$PWD/qv4booleanobject_p.h \

View File

@ -51,7 +51,7 @@ ArgumentsObject::ArgumentsObject(CallContext *context)
, context(context)
, fullyCreated(false)
{
flags &= ~SimpleArray;
setArrayType(ArrayData::Complex);
ExecutionEngine *v4 = context->engine;
Scope scope(v4);
@ -65,9 +65,8 @@ ArgumentsObject::ArgumentsObject(CallContext *context)
memberData[CallerPropertyIndex] = pd;
arrayReserve(context->callData->argc);
for (int i = 0; i < context->callData->argc; ++i)
arrayData.data[i].value = context->callData->args[i];
arrayData.length = context->callData->argc;
arrayData->put(0, context->callData->args, context->callData->argc);
arrayData->setLength(context->callData->argc);
fullyCreated = true;
} else {
Q_ASSERT(CalleePropertyIndex == internalClass->find(context->engine->id_callee));
@ -93,18 +92,18 @@ void ArgumentsObject::fullyCreate()
uint numAccessors = qMin((int)context->function->formalParameterCount, context->realArgumentCount);
uint argCount = qMin(context->realArgumentCount, context->callData->argc);
arrayReserve(argCount);
ensureArrayAttributes();
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.attributes[i] = Attr_Accessor;
arrayData->data[i] = context->engine->argumentsAccessors.at(i);
arrayData->setAttributes(i, Attr_Accessor);
}
for (uint i = numAccessors; i < argCount; ++i) {
arrayData.data[i] = Property::fromValue(context->callData->args[i]);
arrayData.attributes[i] = Attr_Data;
}
arrayData.length = argCount;
arrayData->setLength(numAccessors);
arrayData->put(numAccessors, context->callData->args + numAccessors, argCount - numAccessors);
for (uint i = numAccessors; i < argCount; ++i)
arrayData->setAttributes(i, Attr_Data);
arrayData->setLength(argCount);
fullyCreated = true;
}
@ -114,18 +113,17 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const
fullyCreate();
Scope scope(ctx);
uint pidx = propertyIndexFromArrayIndex(index);
Property *pd = arrayData.data + pidx;
Property *pd = arrayData->getProperty(index);
Property map;
PropertyAttributes mapAttrs;
bool isMapped = false;
if (pd && index < (uint)mappedArguments.size())
isMapped = arrayData.attributes && arrayData.attributes[pidx].isAccessor() && pd->getter() == context->engine->argumentsAccessors.at(index).getter();
isMapped = arrayData->attributes(index).isAccessor() && pd->getter() == context->engine->argumentsAccessors.at(index).getter();
if (isMapped) {
map = *pd;
mapAttrs = arrayData.attributes[pidx];
arrayData.attributes[pidx] = Attr_Data;
mapAttrs = arrayData->attributes(index);
arrayData->setAttributes(index, Attr_Data);
pd->value = mappedArguments.at(index);
}
@ -142,7 +140,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const
if (attrs.isWritable()) {
*pd = map;
arrayData.attributes[pidx] = mapAttrs;
arrayData->setAttributes(index, mapAttrs);
}
}

View File

@ -0,0 +1,563 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qv4arraydata_p.h"
#include "qv4object_p.h"
#include "qv4functionobject_p.h"
using namespace QV4;
const ArrayVTable ArrayData::static_vtbl =
{
ArrayData::Simple,
ArrayData::freeData,
ArrayData::reserve,
ArrayData::get,
ArrayData::put,
ArrayData::putArray,
ArrayData::del,
ArrayData::setAttribute,
ArrayData::attribute,
ArrayData::push_front,
ArrayData::pop_front,
ArrayData::truncate
};
const ArrayVTable SparseArrayData::static_vtbl =
{
ArrayData::Sparse,
SparseArrayData::freeData,
SparseArrayData::reserve,
SparseArrayData::get,
SparseArrayData::put,
SparseArrayData::putArray,
SparseArrayData::del,
SparseArrayData::setAttribute,
SparseArrayData::attribute,
SparseArrayData::push_front,
SparseArrayData::pop_front,
SparseArrayData::truncate
};
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));
delete [] d->data;
d->data = newArray + d->offset;
if (d->attrs) {
PropertyAttributes *newAttrs = new PropertyAttributes[d->offset + d->alloc];
memcpy(newAttrs + d->offset, d->attrs, d->len*sizeof(PropertyAttributes));
delete [] d->attrs;
d->attrs = newAttrs + d->offset;
}
}
void ArrayData::reserve(ArrayData *d, uint n)
{
if (n < 8)
n = 8;
if (n <= d->alloc)
return;
d->alloc = qMax(n, 2*d->alloc);
Property *newArrayData = new Property[d->alloc + d->offset];
if (d->data) {
memcpy(newArrayData + d->offset, d->data, sizeof(Property)*d->len);
delete [] (d->data - d->offset);
}
d->data = newArrayData + d->offset;
if (d->attrs) {
PropertyAttributes *newAttrs = new PropertyAttributes[d->alloc];
memcpy(newAttrs, d->attrs, sizeof(PropertyAttributes)*d->len);
delete [] (d->attrs - d->offset);
d->attrs = newAttrs;
}
}
void ArrayData::ensureAttributes()
{
if (attrs)
return;
if (type == Simple)
type = Complex;
attrs = new PropertyAttributes[alloc + offset];
attrs += offset;
for (uint i = 0; i < len; ++i)
attrs[i] = Attr_Data;
}
void ArrayData::freeData(ArrayData *d)
{
delete [] (d->data - d->offset);
if (d->attrs)
delete [] (d->attrs - d->offset);
delete d;
}
ReturnedValue ArrayData::get(const ArrayData *d, uint index)
{
if (index >= d->len)
return Primitive::emptyValue().asReturnedValue();
return d->data[index].value.asReturnedValue();
}
bool ArrayData::put(ArrayData *d, uint index, ValueRef value)
{
Q_ASSERT(!d->attrs || !d->attrs->isAccessor());
// ### honour attributes
d->data[index].value = value;
return true;
}
bool ArrayData::del(ArrayData *d, uint index)
{
if (index >= d->len)
return true;
if (!d->attrs || d->attrs[index].isConfigurable()) {
d->data[index].value = Primitive::emptyValue();
if (d->attrs)
d->attrs[index] = Attr_Data;
return true;
}
if (d->data[index].value.isEmpty())
return true;
return false;
}
void ArrayData::setAttribute(ArrayData *d, uint index, PropertyAttributes attrs)
{
d->attrs[index] = attrs;
}
PropertyAttributes ArrayData::attribute(const ArrayData *d, uint index)
{
return d->attrs[index];
}
void ArrayData::push_front(ArrayData *d, SafeValue *values, uint n)
{
Q_ASSERT(!d->attrs);
for (int i = n - 1; i >= 0; --i) {
if (!d->offset)
ArrayData::getHeadRoom(d);
--d->offset;
--d->data;
++d->len;
++d->alloc;
d->data->value = values[i].asReturnedValue();
}
}
ReturnedValue ArrayData::pop_front(ArrayData *d)
{
Q_ASSERT(!d->attrs);
if (!d->len)
return Encode::undefined();
ReturnedValue v = d->data[0].value.isEmpty() ? Encode::undefined() : d->data[0].value.asReturnedValue();
++d->offset;
++d->data;
--d->len;
--d->alloc;
return v;
}
uint ArrayData::truncate(ArrayData *d, uint newLen)
{
if (d->attrs) {
Property *it = d->data + d->len;
const Property *begin = d->data + newLen;
while (--it >= begin) {
if (!it->value.isEmpty() && !d->attrs[it - d->data].isConfigurable()) {
newLen = it - d->data + 1;
break;
}
it->value = Primitive::emptyValue();
}
}
d->len = newLen;
return newLen;
}
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();
for (uint i = 0; i < n; ++i)
d->data[index + i].value = values[i];
d->len = qMax(d->len, index + n);
return true;
}
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;
dd->freeList = idx;
if (dd->attrs)
dd->attrs[idx].clear();
}
void SparseArrayData::freeData(ArrayData *d)
{
delete static_cast<SparseArrayData *>(d)->sparse;
ArrayData::freeData(d);
}
void SparseArrayData::reserve(ArrayData *d, uint n)
{
if (n < 8)
n = 8;
if (n <= d->alloc)
return;
SparseArrayData *dd = static_cast<SparseArrayData *>(d);
// ### FIXME
dd->len = dd->alloc;
dd->alloc = qMax(n, 2*dd->alloc);
Property *newArrayData = new Property[dd->alloc];
if (dd->data) {
memcpy(newArrayData, dd->data, sizeof(Property)*dd->len);
delete [] dd->data;
}
dd->data = newArrayData;
if (dd->attrs) {
PropertyAttributes *newAttrs = new PropertyAttributes[dd->alloc];
memcpy(newAttrs, dd->attrs, sizeof(PropertyAttributes)*dd->len);
delete [] dd->attrs;
dd->attrs = newAttrs;
}
for (uint i = dd->freeList; i < dd->alloc; ++i)
dd->data[i].value = Primitive::fromInt32(i + 1);
}
uint SparseArrayData::allocate(ArrayData *d)
{
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;
}
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();
}
bool SparseArrayData::put(ArrayData *d, uint index, ValueRef value)
{
// ### honour attributes
SparseArrayNode *n = static_cast<SparseArrayData *>(d)->sparse->insert(index);
if (n->value == UINT_MAX)
n->value = allocate(d);
d->data[n->value].value = value;
return true;
}
bool SparseArrayData::del(ArrayData *d, uint index)
{
SparseArrayData *dd = static_cast<SparseArrayData *>(d);
SparseArrayNode *n = dd->sparse->findNode(index);
if (!n)
return true;
uint pidx = n->value;
Q_ASSERT(!dd->data[pidx].value.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;
}
return false;
}
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);
d->attrs[n->value] = attrs;
}
PropertyAttributes SparseArrayData::attribute(const ArrayData *d, uint index)
{
SparseArrayNode *n = static_cast<const SparseArrayData *>(d)->sparse->insert(index);
if (!n)
return PropertyAttributes();
return d->attrs[n->value];
}
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];
static_cast<SparseArrayData *>(d)->sparse->push_front(idx);
}
}
ReturnedValue SparseArrayData::pop_front(ArrayData *d)
{
Q_ASSERT(!d->attrs);
uint idx = static_cast<SparseArrayData *>(d)->sparse->pop_front();
ReturnedValue v;
if (idx != UINT_MAX) {
v = d->data[idx].value.asReturnedValue();
SparseArrayData::free(d, idx);
} else {
v = Encode::undefined();
}
return v;
}
uint SparseArrayData::truncate(ArrayData *d, uint newLen)
{
SparseArrayNode *begin = static_cast<SparseArrayData *>(d)->sparse->lowerBound(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;
bool brk = (it == begin);
SparseArrayNode *prev = it->previousNode();
static_cast<SparseArrayData *>(d)->sparse->erase(it);
if (brk)
break;
it = prev;
}
}
return newLen;
}
bool SparseArrayData::putArray(ArrayData *d, uint index, SafeValue *values, uint n)
{
for (uint i = 0; i < n; ++i)
put(d, index + i, values[i]);
d->len = qMax(d->len, index + n);
return true;
}
uint ArrayData::append(Object *o, const ArrayObject *otherObj, uint n)
{
ArrayData *d = o->arrayData;
if (!n)
return d->len;
const ArrayData *other = otherObj->arrayData;
if (other->isSparse()) {
o->initSparseArray();
d = o->arrayData;
}
uint oldSize = d->len;
// ### 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;
}
}
} 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();
}
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->len = oldSize + i + 1;
o->arrayData->setAttributes(oldSize + i, Attr_Data);
if (!exists)
d->data[oldSize + i].value = Primitive::emptyValue();
}
} else {
d->len = oldSize + other->len;
memcpy(d->data + oldSize, other->data, other->len*sizeof(Property));
if (d->attrs)
std::fill(d->attrs + oldSize, d->attrs + oldSize + other->len, PropertyAttributes(Attr_Data));
}
}
return oldSize + n;
}
Property *ArrayData::insert(Object *o, uint index)
{
Property *pd;
if (o->arrayData->type != ArrayData::Sparse && (index < 0x1000 || index < o->arrayData->len + (o->arrayData->len >> 2))) {
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->len = index + 1;
}
pd = 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;
}
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);
}
}
}
}
void ArrayData::sort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint len)
{
ArrayData *d = thisObject->arrayData;
if (!d || !d->len)
return;
if (d->type == ArrayData::Sparse) {
context->throwUnimplemented(QStringLiteral("Object::sort unimplemented for sparse arrays"));
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) {
for (uint i = 0; i < len; i++) {
if (d->data[i].value.isEmpty()) {
while (--len > i)
if (!d->data[len].value.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;
}
}
}
if (!(comparefn->isUndefined() || comparefn->asObject())) {
context->throwTypeError();
return;
}
ArrayElementLessThan lessThan(context, thisObject, comparefn);
if (!len)
return;
Property *begin = d->data;
std::sort(begin, begin + len, lessThan);
}

View File

@ -0,0 +1,242 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QV4ARRAYDATA_H
#define QV4ARRAYDATA_H
#include "qv4global_p.h"
#include "qv4managed_p.h"
#include "qv4property_p.h"
#include "qv4sparsearray_p.h"
QT_BEGIN_NAMESPACE
namespace QV4 {
struct ArrayData;
struct ArrayVTable
{
uint type;
void (*freeData)(ArrayData *d);
void (*reserve)(ArrayData *d, uint n);
ReturnedValue (*get)(const ArrayData *d, uint index);
bool (*put)(ArrayData *d, uint index, ValueRef value);
bool (*putArray)(ArrayData *d, uint index, SafeValue *values, uint n);
bool (*del)(ArrayData *d, uint index);
void (*setAttribute)(ArrayData *d, uint index, PropertyAttributes attrs);
PropertyAttributes (*attribute)(const ArrayData *d, uint index);
void (*push_front)(ArrayData *d, SafeValue *values, uint n);
ReturnedValue (*pop_front)(ArrayData *d);
uint (*truncate)(ArrayData *d, uint newLen);
};
struct Q_QML_EXPORT ArrayData
{
ArrayData()
: vtable(&static_vtbl)
, offset(0)
, len(0)
, alloc(0)
, type(0)
, attrs(0)
, data(0)
{
}
enum Type {
Simple = 0,
Complex = 1,
Sparse = 2,
Custom = 3
};
const ArrayVTable *vtable;
uint offset;
uint len;
uint alloc;
uint type;
PropertyAttributes *attrs;
Property *data;
bool isSparse() const { return this && type == Sparse; }
uint length() const {
return this ? len : 0;
}
void setLength(uint l) {
Q_ASSERT(this);
len = l;
}
bool hasAttributes() const {
return this && attrs;
}
void ensureAttributes();
PropertyAttributes attributes(int i) const {
Q_ASSERT(this);
return attrs ? vtable->attribute(this, i) : Attr_Data;
}
void setAttributes(uint i, PropertyAttributes a) {
Q_ASSERT(this);
if (attrs || a != Attr_Data) {
ensureAttributes();
a.resolve();
vtable->setAttribute(this, i, a);
}
}
bool isEmpty(uint i) const {
if (!this)
return true;
return (vtable->get(this, i) == Primitive::emptyValue().asReturnedValue());
}
inline void free() {
vtable->freeData(this);
}
inline void push_front(SafeValue *values, uint nValues) {
vtable->push_front(this, values, nValues);
}
inline ReturnedValue pop_front() {
return vtable->pop_front(this);
}
inline uint push_back(uint l, uint n, SafeValue *values) {
vtable->putArray(this, l, values, n);
return len;
}
inline bool deleteIndex(uint index) {
return vtable->del(this, index);
}
inline uint truncate(uint newLen) {
if (!this || len < newLen)
return newLen;
return vtable->truncate(this, newLen);
}
bool put(uint index, ValueRef value) {
return vtable->put(this, index, value);
}
bool put(uint index, SafeValue *values, uint n) {
return vtable->putArray(this, index, values, n);
}
ReturnedValue get(uint i) const {
if (!this)
return Primitive::emptyValue().asReturnedValue();
return vtable->get(this, i);
}
inline Property *getProperty(uint index) const;
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);
void markObjects(ExecutionEngine *e);
static void getHeadRoom(ArrayData *d);
static void reserve(ArrayData *d, uint n);
static void freeData(ArrayData *d);
static ReturnedValue get(const ArrayData *d, uint index);
static bool put(ArrayData *d, uint index, ValueRef value);
static bool putArray(ArrayData *d, uint index, SafeValue *values, uint n);
static bool del(ArrayData *d, uint index);
static void setAttribute(ArrayData *d, uint index, PropertyAttributes attrs);
static PropertyAttributes attribute(const ArrayData *d, uint index);
static void push_front(ArrayData *d, SafeValue *values, uint n);
static ReturnedValue pop_front(ArrayData *d);
static uint truncate(ArrayData *d, uint newLen);
static const ArrayVTable static_vtbl;
};
struct Q_QML_EXPORT SparseArrayData : public ArrayData
{
SparseArrayData()
: freeList(0)
, sparse(0)
{ vtable = &static_vtbl; }
uint freeList;
SparseArray *sparse;
static uint allocate(ArrayData *d);
static void free(ArrayData *d, uint idx);
static void freeData(ArrayData *d);
static void reserve(ArrayData *d, uint n);
static ReturnedValue get(const ArrayData *d, uint index);
static bool put(ArrayData *d, uint index, ValueRef value);
static bool putArray(ArrayData *d, uint index, SafeValue *values, uint n);
static bool del(ArrayData *d, uint index);
static void setAttribute(ArrayData *d, uint index, PropertyAttributes attrs);
static PropertyAttributes attribute(const ArrayData *d, uint index);
static void push_front(ArrayData *d, SafeValue *values, uint n);
static ReturnedValue pop_front(ArrayData *d);
static uint truncate(ArrayData *d, uint newLen);
static const ArrayVTable static_vtbl;
};
inline Property *ArrayData::getProperty(uint index) const
{
if (!this)
return 0;
if (type != Sparse) {
if (index >= len)
return 0;
return data + index;
} else {
SparseArrayNode *n = static_cast<const SparseArrayData *>(this)->sparse->findNode(index);
if (!n)
return 0;
return data + n->value;
}
}
}
QT_END_NAMESPACE
#endif

View File

@ -72,9 +72,8 @@ ReturnedValue ArrayCtor::construct(Managed *m, CallData *callData)
} else {
len = callData->argc;
a->arrayReserve(len);
for (unsigned int i = 0; i < len; ++i)
a->arrayData.data[i].value = callData->args[i];
a->arrayData.length = len;
a->arrayData->put(0, callData->args, len);
a->arrayData->setLength(len);
}
a->setArrayLengthUnchecked(len);
@ -122,15 +121,6 @@ void ArrayPrototype::init(ExecutionEngine *engine, ObjectRef ctor)
defineDefaultProperty(QStringLiteral("reduceRight"), method_reduceRight, 1);
}
uint ArrayPrototype::getLength(ExecutionContext *ctx, ObjectRef o)
{
if (o->isArrayObject())
return o->arrayLength();
Scope scope(ctx);
ScopedValue v(scope, o->get(ctx->engine->id_length));
return v->toUInt32();
}
ReturnedValue ArrayPrototype::method_isArray(CallContext *ctx)
{
bool isArray = ctx->callData->argc && ctx->callData->args[0].asArrayObject();
@ -180,15 +170,17 @@ ReturnedValue ArrayPrototype::method_concat(CallContext *ctx)
eltAsObj = ctx->callData->args[i];
elt = ctx->callData->args[i];
if (elt) {
result->arrayConcat(elt.getPointer());
uint n = elt->getLength();
uint newLen = ArrayData::append(result.getPointer(), elt.getPointer(), n);
result->setArrayLengthUnchecked(newLen);
} else if (eltAsObj && eltAsObj->isListType()) {
const uint startIndex = getLength(ctx, result);
for (int i = 0, len = getLength(ctx, eltAsObj); i < len; ++i) {
const uint startIndex = result->getLength();
for (int i = 0, len = eltAsObj->getLength(); i < len; ++i) {
entry = eltAsObj->getIndexed(i);
result->putIndexed(startIndex + i, entry);
}
} else {
result->arraySet(getLength(ctx, result), ctx->callData->args[i]);
result->arraySet(result->getLength(), ctx->callData->args[i]);
}
}
@ -218,7 +210,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx)
// ### FIXME
if (ArrayObject *a = self->asArrayObject()) {
ScopedValue e(scope);
for (uint i = 0; i < a->arrayLength(); ++i) {
for (uint i = 0; i < a->getLength(); ++i) {
if (i)
R += r4;
@ -260,7 +252,7 @@ ReturnedValue ArrayPrototype::method_pop(CallContext *ctx)
ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx));
if (!instance)
return Encode::undefined();
uint len = getLength(ctx, instance);
uint len = instance->getLength();
if (!len) {
if (!instance->isArrayObject())
@ -288,7 +280,10 @@ ReturnedValue ArrayPrototype::method_push(CallContext *ctx)
ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx));
if (!instance)
return Encode::undefined();
uint len = getLength(ctx, instance);
instance->arrayCreate();
uint len = instance->getLength();
if (len + ctx->callData->argc < len) {
// ughh...
@ -308,21 +303,8 @@ ReturnedValue ArrayPrototype::method_push(CallContext *ctx)
return Encode(newLen);
}
if (!instance->protoHasArray() && instance->arrayData.length <= len) {
for (int i = 0; i < ctx->callData->argc; ++i) {
if (!instance->arrayData.sparse) {
if (len >= instance->arrayData.alloc)
instance->arrayReserve(len + 1);
instance->arrayData.data[len].value = ctx->callData->args[i];
if (instance->arrayData.attributes)
instance->arrayData.attributes[len] = Attr_Data;
instance->arrayData.length = len + 1;
} else {
uint j = instance->allocArrayValue(ctx->callData->args[i]);
instance->arrayData.sparse->push_back(j, len);
}
++len;
}
if (!instance->protoHasArray() && instance->arrayData->length() <= len) {
len = instance->arrayData->push_back(len, ctx->callData->argc, ctx->callData->args);
} else {
for (int i = 0; i < ctx->callData->argc; ++i)
instance->putIndexed(len + i, ctx->callData->args[i]);
@ -342,7 +324,7 @@ ReturnedValue ArrayPrototype::method_reverse(CallContext *ctx)
ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx));
if (!instance)
return Encode::undefined();
uint length = getLength(ctx, instance);
uint length = instance->getLength();
int lo = 0, hi = length - 1;
@ -374,7 +356,10 @@ ReturnedValue ArrayPrototype::method_shift(CallContext *ctx)
ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx));
if (!instance)
return Encode::undefined();
uint len = getLength(ctx, instance);
instance->arrayCreate();
uint len = instance->getLength();
if (!len) {
if (!instance->isArrayObject())
@ -382,28 +367,14 @@ ReturnedValue ArrayPrototype::method_shift(CallContext *ctx)
return Encode::undefined();
}
Property *front = 0;
uint pidx = instance->propertyIndexFromArrayIndex(0);
if (pidx < UINT_MAX && !instance->arrayData.data[pidx].value.isEmpty())
front = instance->arrayData.data + pidx;
ScopedValue result(scope);
ScopedValue result(scope, front ? instance->getValue(front, instance->arrayData.attributes ? instance->arrayData.attributes[pidx] : Attr_Data) : Encode::undefined());
if (!instance->protoHasArray() && instance->arrayData.length <= len) {
if (!instance->arrayData.sparse) {
if (instance->arrayData.length) {
++instance->arrayData.offset;
++instance->arrayData.data;
--instance->arrayData.length;
--instance->arrayData.alloc;
if (instance->arrayData.attributes)
++instance->arrayData.attributes;
}
} else {
uint idx = instance->arrayData.sparse->pop_front();
instance->freeArrayValue(idx);
}
if (!instance->protoHasArray() && !instance->arrayData->hasAttributes() && instance->arrayData->length() <= len) {
result = instance->arrayData->pop_front();
} else {
result = instance->getIndexed(0);
if (scope.hasException())
return Encode::undefined();
ScopedValue v(scope);
// do it the slow way
for (uint k = 1; k < len; ++k) {
@ -415,6 +386,8 @@ ReturnedValue ArrayPrototype::method_shift(CallContext *ctx)
instance->putIndexed(k - 1, v);
else
instance->deleteIndexedProperty(k - 1);
if (scope.hasException())
return Encode::undefined();
}
instance->deleteIndexedProperty(len - 1);
if (scope.hasException())
@ -436,7 +409,7 @@ ReturnedValue ArrayPrototype::method_slice(CallContext *ctx)
return Encode::undefined();
Scoped<ArrayObject> result(scope, ctx->engine->newArrayObject());
uint len = getLength(ctx, o);
uint len = o->getLength();
double s = ScopedValue(scope, ctx->argument(0))->toInteger();
uint start;
if (s < 0)
@ -477,10 +450,10 @@ ReturnedValue ArrayPrototype::method_sort(CallContext *ctx)
if (!instance)
return Encode::undefined();
uint len = getLength(ctx, instance);
uint len = instance->getLength();
ScopedValue comparefn(scope, ctx->argument(0));
instance->arraySort(ctx, instance, comparefn, len);
ArrayData::sort(ctx, instance, comparefn, len);
return ctx->callData->thisObject.asReturnedValue();
}
@ -490,7 +463,7 @@ ReturnedValue ArrayPrototype::method_splice(CallContext *ctx)
ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx));
if (!instance)
return Encode::undefined();
uint len = getLength(ctx, instance);
uint len = instance->getLength();
Scoped<ArrayObject> newArray(scope, ctx->engine->newArrayObject());
@ -504,17 +477,21 @@ ReturnedValue ArrayPrototype::method_splice(CallContext *ctx)
uint deleteCount = (uint)qMin(qMax(ScopedValue(scope, ctx->argument(1))->toInteger(), 0.), (double)(len - start));
newArray->arrayReserve(deleteCount);
ScopedValue v(scope);
for (uint i = 0; i < deleteCount; ++i) {
newArray->arrayData.data[i].value = instance->getIndexed(start + i);
bool exists;
v = instance->getIndexed(start + i, &exists);
if (scope.hasException())
return Encode::undefined();
newArray->arrayData.length = i + 1;
if (exists) {
newArray->arrayData->put(i, v);
newArray->arrayData->setLength(i + 1);
}
}
newArray->setArrayLengthUnchecked(deleteCount);
uint itemCount = ctx->callData->argc < 2 ? 0 : ctx->callData->argc - 2;
ScopedValue v(scope);
if (itemCount < deleteCount) {
for (uint k = start; k < len - deleteCount; ++k) {
bool exists;
@ -568,32 +545,15 @@ ReturnedValue ArrayPrototype::method_unshift(CallContext *ctx)
ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx));
if (!instance)
return Encode::undefined();
uint len = getLength(ctx, instance);
ScopedValue v(scope);
if (!instance->protoHasArray() && instance->arrayData.length <= len) {
for (int i = ctx->callData->argc - 1; i >= 0; --i) {
v = ctx->argument(i);
instance->arrayCreate();
if (!instance->arrayData.sparse) {
if (!instance->arrayData.offset)
instance->getArrayHeadRoom();
uint len = instance->getLength();
--instance->arrayData.offset;
--instance->arrayData.data;
++instance->arrayData.length;
++instance->arrayData.alloc;
if (instance->arrayData.attributes) {
--instance->arrayData.attributes;
*instance->arrayData.attributes = Attr_Data;
}
instance->arrayData.data->value = v.asReturnedValue();
} else {
uint idx = instance->allocArrayValue(v);
instance->arrayData.sparse->push_front(idx);
}
}
if (!instance->protoHasArray() && !instance->arrayData->hasAttributes() && instance->arrayData->length() <= len) {
instance->arrayData->push_front(ctx->callData->args, ctx->callData->argc);
} else {
ScopedValue v(scope);
for (uint k = len; k > 0; --k) {
bool exists;
v = instance->getIndexed(k - 1, &exists);
@ -622,7 +582,7 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx)
ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx));
if (!instance)
return Encode::undefined();
uint len = getLength(ctx, instance);
uint len = instance->getLength();
if (!len)
return Encode(-1);
@ -651,7 +611,48 @@ ReturnedValue ArrayPrototype::method_indexOf(CallContext *ctx)
return Encode(-1);
}
return instance->arrayIndexOf(searchValue, fromIndex, len, ctx, instance.getPointer());
ScopedValue value(scope);
if ((instance->arrayData && instance->arrayType() != ArrayData::Simple) || instance->protoHasArray()) {
// lets be safe and slow
for (uint i = fromIndex; i < len; ++i) {
bool exists;
value = instance->getIndexed(i, &exists);
if (scope.hasException())
return Encode::undefined();
if (exists && __qmljs_strict_equal(value, searchValue))
return Encode(i);
}
} 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 {
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));
if (scope.hasException())
return Encode::undefined();
if (__qmljs_strict_equal(value, searchValue))
return Encode((uint)(pd - instance->arrayData->data));
}
++pd;
}
}
return Encode(-1);
}
ReturnedValue ArrayPrototype::method_lastIndexOf(CallContext *ctx)
@ -661,7 +662,7 @@ ReturnedValue ArrayPrototype::method_lastIndexOf(CallContext *ctx)
ScopedObject instance(scope, ctx->callData->thisObject.toObject(ctx));
if (!instance)
return Encode::undefined();
uint len = getLength(ctx, instance);
uint len = instance->getLength();
if (!len)
return Encode(-1);
@ -707,7 +708,7 @@ ReturnedValue ArrayPrototype::method_every(CallContext *ctx)
if (!instance)
return Encode::undefined();
uint len = getLength(ctx, instance);
uint len = instance->getLength();
Scoped<FunctionObject> callback(scope, ctx->argument(0));
if (!callback)
@ -741,7 +742,7 @@ ReturnedValue ArrayPrototype::method_some(CallContext *ctx)
if (!instance)
return Encode::undefined();
uint len = getLength(ctx, instance);
uint len = instance->getLength();
Scoped<FunctionObject> callback(scope, ctx->argument(0));
if (!callback)
@ -775,7 +776,7 @@ ReturnedValue ArrayPrototype::method_forEach(CallContext *ctx)
if (!instance)
return Encode::undefined();
uint len = getLength(ctx, instance);
uint len = instance->getLength();
Scoped<FunctionObject> callback(scope, ctx->argument(0));
if (!callback)
@ -806,7 +807,7 @@ ReturnedValue ArrayPrototype::method_map(CallContext *ctx)
if (!instance)
return Encode::undefined();
uint len = getLength(ctx, instance);
uint len = instance->getLength();
Scoped<FunctionObject> callback(scope, ctx->argument(0));
if (!callback)
@ -843,7 +844,7 @@ ReturnedValue ArrayPrototype::method_filter(CallContext *ctx)
if (!instance)
return Encode::undefined();
uint len = getLength(ctx, instance);
uint len = instance->getLength();
Scoped<FunctionObject> callback(scope, ctx->argument(0));
if (!callback)
@ -884,7 +885,7 @@ ReturnedValue ArrayPrototype::method_reduce(CallContext *ctx)
if (!instance)
return Encode::undefined();
uint len = getLength(ctx, instance);
uint len = instance->getLength();
Scoped<FunctionObject> callback(scope, ctx->argument(0));
if (!callback)
@ -934,7 +935,7 @@ ReturnedValue ArrayPrototype::method_reduceRight(CallContext *ctx)
if (!instance)
return Encode::undefined();
uint len = getLength(ctx, instance);
uint len = instance->getLength();
Scoped<FunctionObject> callback(scope, ctx->argument(0));
if (!callback)

View File

@ -64,8 +64,6 @@ struct ArrayPrototype: ArrayObject
void init(ExecutionEngine *engine, ObjectRef ctor);
static uint getLength(ExecutionContext *ctx, ObjectRef o);
static ReturnedValue method_isArray(CallContext *ctx);
static ReturnedValue method_toString(CallContext *ctx);
static ReturnedValue method_toLocaleString(CallContext *ctx);

View File

@ -77,6 +77,7 @@ const ManagedVTable ExecutionContext::static_vtbl =
0,
isEqualTo,
0,
0,
"ExecutionContext",
};

View File

@ -343,19 +343,19 @@ ReturnedValue FunctionPrototype::method_apply(CallContext *ctx)
if (!arg->isNullOrUndefined())
return ctx->throwTypeError();
} else {
len = ArrayPrototype::getLength(ctx, arr);
len = arr->getLength();
}
ScopedCallData callData(scope, len);
if (len) {
if (!(arr->flags & SimpleArray) || arr->protoHasArray() || arr->hasAccessorProperty) {
if (arr->arrayType() != ArrayData::Simple || arr->protoHasArray() || arr->hasAccessorProperty) {
for (quint32 i = 0; i < len; ++i)
callData->args[i] = arr->getIndexed(i);
} else {
int alen = qMin(len, arr->arrayData.length);
int alen = qMin(len, arr->arrayData->length());
for (int i = 0; i < alen; ++i)
callData->args[i] = arr->arrayData.data[i].value;
callData->args[i] = arr->arrayData->data[i].value;
for (quint32 i = alen; i < len; ++i)
callData->args[i] = Primitive::undefinedValue();
}

View File

@ -339,7 +339,7 @@ ReturnedValue JsonParser::parseArray()
}
}
DEBUG << "size =" << array->arrayLength();
DEBUG << "size =" << array->getLength();
END;
--nestingLevel;
@ -855,7 +855,7 @@ QString Stringify::JA(ArrayObjectRef a)
indent += gap;
QStringList partial;
uint len = a->arrayLength();
uint len = a->getLength();
ScopedValue v(scope);
for (uint i = 0; i < len; ++i) {
bool exists;
@ -925,7 +925,7 @@ ReturnedValue JsonObject::method_stringify(CallContext *ctx)
if (o) {
stringify.replacerFunction = o->asFunctionObject();
if (o->isArrayObject()) {
uint arrayLen = o->arrayLength();
uint arrayLen = o->getLength();
ScopedValue v(scope);
for (uint i = 0; i < arrayLen; ++i) {
v = o->getIndexed(i);
@ -1057,9 +1057,10 @@ QV4::ReturnedValue JsonObject::fromJsonArray(ExecutionEngine *engine, const QJso
int size = array.size();
Scoped<ArrayObject> a(scope, engine->newArrayObject());
a->arrayReserve(size);
ScopedValue v(scope);
for (int i = 0; i < size; i++) {
a->arrayData.data[i].value = fromJsonValue(engine, array.at(i));
a->arrayData.length = i + 1;
a->arrayData->put(i, (v = fromJsonValue(engine, array.at(i))));
a->arrayData->setLength(i + 1);
}
a->setArrayLengthUnchecked(size);
return a.asReturnedValue();
@ -1083,7 +1084,7 @@ QJsonArray JsonObject::toJsonArray(ArrayObjectRef a, V4ObjectSet &visitedObjects
visitedObjects.insert(a);
ScopedValue v(scope);
quint32 length = a->arrayLength();
quint32 length = a->getLength();
for (quint32 i = 0; i < length; ++i) {
v = a->getIndexed(i);
if (v->asFunctionObject())

View File

@ -316,6 +316,15 @@ ReturnedValue Lookup::stringLengthGetter(Lookup *l, const ValueRef object)
return getterGeneric(l, object);
}
ReturnedValue Lookup::arrayLengthGetter(Lookup *l, const ValueRef object)
{
if (ArrayObject *a = object->asArrayObject())
return a->memberData[ArrayObject::LengthPropertyIndex].value.asReturnedValue();
l->getter = getterGeneric;
return getterGeneric(l, object);
}
ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionContext *ctx)
{

View File

@ -85,6 +85,7 @@ struct Lookup {
static ReturnedValue primitiveGetterAccessor0(Lookup *l, const ValueRef object);
static ReturnedValue primitiveGetterAccessor1(Lookup *l, const ValueRef object);
static ReturnedValue stringLengthGetter(Lookup *l, const ValueRef object);
static ReturnedValue arrayLengthGetter(Lookup *l, const ValueRef object);
static ReturnedValue globalGetterGeneric(Lookup *l, ExecutionContext *ctx);
static ReturnedValue globalGetter0(Lookup *l, ExecutionContext *ctx);

View File

@ -71,6 +71,7 @@ const ManagedVTable Managed::static_vtbl =
0,
isEqualTo,
0,
0,
"Managed",
};

View File

@ -106,6 +106,7 @@ struct ManagedVTable
ReturnedValue (*getLookup)(Managed *m, Lookup *l);
void (*setLookup)(Managed *m, Lookup *l, const ValueRef v);
bool (*isEqualTo)(Managed *m, Managed *other);
uint (*getLength)(const Managed *m);
Property *(*advanceIterator)(Managed *m, ObjectIterator *it, StringRef name, uint *index, PropertyAttributes *attributes);
const char *className;
};
@ -136,6 +137,7 @@ const QV4::ManagedVTable classname::static_vtbl = \
getLookup, \
setLookup, \
isEqualTo, \
getLength, \
advanceIterator, \
#classname \
}
@ -166,6 +168,7 @@ const QV4::ManagedVTable classname::static_vtbl = \
getLookup, \
setLookup, \
isEqualTo, \
getLength, \
advanceIterator, \
#name \
}
@ -196,6 +199,7 @@ const QV4::ManagedVTable classname::static_vtbl = \
getLookup, \
setLookup, \
isEqualTo, \
getLength, \
advanceIterator, \
#classname \
}
@ -327,6 +331,7 @@ public:
bool isEqualTo(Managed *other)
{ return internalClass->vtable->isEqualTo(this, other); }
uint getLength() const { return internalClass->vtable->getLength(this); }
Property *advanceIterator(ObjectIterator *it, StringRef name, uint *index, PropertyAttributes *attributes);
static void destroy(Managed *that) { that->_data = 0; }
@ -335,16 +340,13 @@ public:
static ReturnedValue getLookup(Managed *m, Lookup *);
static void setLookup(Managed *m, Lookup *l, const ValueRef v);
static bool isEqualTo(Managed *m, Managed *other);
static uint getLength(const Managed *) { return 0; }
ReturnedValue asReturnedValue() { return Value::fromManaged(this).asReturnedValue(); }
InternalClass *internalClass;
enum {
SimpleArray = 1
};
union {
uint _data;
struct {
@ -358,7 +360,7 @@ public:
uchar hasAccessorProperty : 1;
uchar _type;
mutable uchar subtype;
uchar flags;
uchar _flags;
};
};

View File

@ -73,7 +73,6 @@ Object::Object(ExecutionEngine *engine)
: Managed(engine->objectClass)
, memberDataAlloc(InlinePropertySize), memberData(inlineProperties)
{
flags = SimpleArray;
}
Object::Object(InternalClass *ic)
@ -81,7 +80,6 @@ Object::Object(InternalClass *ic)
, memberDataAlloc(InlinePropertySize), memberData(inlineProperties)
{
Q_ASSERT(internalClass->vtable && internalClass->vtable != &Managed::static_vtbl);
flags = SimpleArray;
if (internalClass->size >= memberDataAlloc) {
memberDataAlloc = internalClass->size;
@ -93,10 +91,8 @@ Object::~Object()
{
if (memberData != inlineProperties)
delete [] memberData;
delete [] (arrayData.data - (arrayData.sparse ? 0 : arrayData.offset));
if (arrayData.attributes)
delete [] (arrayData.attributes - (arrayData.sparse ? 0 : arrayData.offset));
delete arrayData.sparse;
if (arrayData)
arrayData->free();
_data = 0;
}
@ -252,23 +248,8 @@ void Object::markObjects(Managed *that, ExecutionEngine *e)
}
}
}
if (o->flags & SimpleArray) {
for (uint i = 0; i < o->arrayData.length; ++i)
o->arrayData.data[i].value.mark(e);
return;
} else {
for (uint i = 0; i < o->arrayData.length; ++i) {
const Property &pd = o->arrayData.data[i];
if (o->arrayData.attributes && o->arrayData.attributes[i].isAccessor()) {
if (pd.getter())
pd.getter()->mark(e);
if (pd.setter())
pd.setter()->mark(e);
} else {
pd.value.mark(e);
}
}
}
if (o->arrayData)
o->arrayData->markObjects(e);
}
void Object::ensureMemberIndex(uint idx)
@ -318,12 +299,11 @@ Property *Object::__getOwnProperty__(const StringRef name, PropertyAttributes *a
Property *Object::__getOwnProperty__(uint index, PropertyAttributes *attrs)
{
uint pidx = propertyIndexFromArrayIndex(index);
if (pidx < UINT_MAX) {
Property *p = arrayData.data + pidx;
if (!p->value.isEmpty() && !(arrayData.attributes && arrayData.attributes[pidx].isGeneric())) {
Property *p = arrayData->getProperty(index);
if (p) {
if (!p->value.isEmpty()) {
if (attrs)
*attrs = arrayData.attributes ? arrayData.attributes[pidx] : PropertyAttributes(Attr_Data);
*attrs = arrayData->attributes(index);
return p;
}
}
@ -366,12 +346,11 @@ Property *Object::__getPropertyDescriptor__(uint index, PropertyAttributes *attr
{
const Object *o = this;
while (o) {
uint pidx = o->propertyIndexFromArrayIndex(index);
if (pidx < UINT_MAX) {
Property *p = o->arrayData.data + pidx;
Property *p = o->arrayData->getProperty(index);
if (p) {
if (!p->value.isEmpty()) {
if (attrs)
*attrs = o->arrayData.attributes ? o->arrayData.attributes[pidx] : PropertyAttributes(Attr_Data);
*attrs = o->arrayData->attributes(index);
return p;
}
}
@ -467,13 +446,9 @@ PropertyAttributes Object::query(const Managed *m, StringRef name)
PropertyAttributes Object::queryIndexed(const Managed *m, uint index)
{
const Object *o = static_cast<const Object *>(m);
uint pidx = o->propertyIndexFromArrayIndex(index);
if (pidx < UINT_MAX) {
if (o->arrayData.attributes)
return o->arrayData.attributes[pidx];
if (!o->arrayData.data[pidx].value.isEmpty())
return Attr_Data;
}
if (o->arrayData->get(index) != Primitive::emptyValue().asReturnedValue())
return o->arrayData->attributes(index);
if (o->isStringObject()) {
Property *p = static_cast<const StringObject *>(o)->getIndex(index);
if (p)
@ -574,41 +549,42 @@ Property *Object::advanceIterator(Managed *m, ObjectIterator *it, StringRef name
name = (String *)0;
*index = UINT_MAX;
if (!it->arrayIndex)
it->arrayNode = o->sparseArrayBegin();
if (o->arrayData) {
if (!it->arrayIndex)
it->arrayNode = o->sparseBegin();
// sparse arrays
if (it->arrayNode) {
while (it->arrayNode != o->sparseArrayEnd()) {
int k = it->arrayNode->key();
uint pidx = it->arrayNode->value;
Property *p = o->arrayData.data + pidx;
it->arrayNode = it->arrayNode->nextNode();
PropertyAttributes a = o->arrayData.attributes ? o->arrayData.attributes[pidx] : PropertyAttributes(Attr_Data);
if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) {
it->arrayIndex = k + 1;
*index = k;
// sparse arrays
if (it->arrayNode) {
while (it->arrayNode != o->sparseEnd()) {
int k = it->arrayNode->key();
uint pidx = it->arrayNode->value;
Property *p = o->arrayData->data + pidx;
it->arrayNode = it->arrayNode->nextNode();
PropertyAttributes a = o->arrayData->attributes(k);
if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) {
it->arrayIndex = k + 1;
*index = k;
if (attrs)
*attrs = a;
return p;
}
}
it->arrayNode = 0;
it->arrayIndex = UINT_MAX;
}
// dense arrays
while (it->arrayIndex < o->arrayData->length()) {
Property *p = o->arrayData->data + it->arrayIndex;
PropertyAttributes a = o->arrayData->attributes(it->arrayIndex);
++it->arrayIndex;
if (!p->value.isEmpty()
&& (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable())) {
*index = it->arrayIndex - 1;
if (attrs)
*attrs = a;
return p;
}
}
it->arrayNode = 0;
it->arrayIndex = UINT_MAX;
}
// dense arrays
while (it->arrayIndex < o->arrayData.length) {
uint pidx = o->propertyIndexFromArrayIndex(it->arrayIndex);
Property *p = o->arrayData.data + pidx;
PropertyAttributes a = o->arrayData.attributes ? o->arrayData.attributes[pidx] : PropertyAttributes(Attr_Data);
++it->arrayIndex;
if (!p->value.isEmpty()
&& (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable())) {
*index = it->arrayIndex - 1;
if (attrs)
*attrs = a;
return p;
}
}
while (it->memberIndex < o->internalClass->size) {
@ -658,15 +634,14 @@ ReturnedValue Object::internalGet(const StringRef name, bool *hasProperty)
ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty)
{
Property *pd = 0;
PropertyAttributes attrs = Attr_Data;
PropertyAttributes attrs;
Object *o = this;
while (o) {
uint pidx = o->propertyIndexFromArrayIndex(index);
if (pidx < UINT_MAX) {
if (!o->arrayData.data[pidx].value.isEmpty()) {
pd = o->arrayData.data + pidx;
if (o->arrayData.attributes)
attrs = o->arrayData.attributes[pidx];
Property *p = o->arrayData->getProperty(index);
if (p) {
if (!p->value.isEmpty()) {
pd = p;
attrs = o->arrayData->attributes(index);
break;
}
}
@ -788,10 +763,10 @@ void Object::internalPutIndexed(uint index, const ValueRef value)
Property *pd = 0;
PropertyAttributes attrs;
uint pidx = propertyIndexFromArrayIndex(index);
if (pidx < UINT_MAX && !arrayData.data[pidx].value.isEmpty()) {
pd = arrayData.data + pidx;
attrs = arrayData.attributes ? arrayData.attributes[pidx] : PropertyAttributes(Attr_Data);
Property *p = arrayData->getProperty(index);
if (p && !p->value.isEmpty()) {
pd = p;
attrs = arrayData->attributes(index);
}
if (!pd && isStringObject()) {
@ -883,22 +858,8 @@ bool Object::internalDeleteIndexedProperty(uint index)
if (internalClass->engine->hasException)
return false;
uint pidx = propertyIndexFromArrayIndex(index);
if (pidx == UINT_MAX)
if (!arrayData || arrayData->deleteIndex(index))
return true;
if (arrayData.data[pidx].value.isEmpty())
return true;
if (!arrayData.attributes || arrayData.attributes[pidx].isConfigurable()) {
arrayData.data[pidx].value = Primitive::emptyValue();
if (arrayData.attributes)
arrayData.attributes[pidx].clear();
if (arrayData.sparse) {
arrayData.data[pidx].value.int_32 = arrayData.freeList;
arrayData.freeList = pidx;
}
return true;
}
if (engine()->currentContext()->strictMode)
engine()->currentContext()->throwTypeError();
@ -974,7 +935,7 @@ reject:
bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs)
{
// 15.4.5.1, 4b
if (isArrayObject() && index >= arrayLength() && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable())
if (isArrayObject() && index >= getLength() && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable())
goto reject;
if (ArgumentsObject::isNonStrictArgumentsObject(this))
@ -993,9 +954,9 @@ bool Object::defineOwnProperty2(ExecutionContext *ctx, uint index, const Propert
// Clause 1
{
uint pidx = propertyIndexFromArrayIndex(index);
if (pidx < UINT_MAX && !arrayData.data[pidx].value.isEmpty())
current = arrayData.data + pidx;
Property *p = arrayData->getProperty(index);
if (p && !p->value.isEmpty())
current = p;
if (!current && isStringObject())
current = static_cast<StringObject *>(this)->getIndex(index);
}
@ -1005,9 +966,15 @@ bool Object::defineOwnProperty2(ExecutionContext *ctx, uint index, const Propert
if (!extensible)
goto reject;
// clause 4
Property *pd = arrayInsert(index, attrs);
*pd = p;
pd->fullyPopulated(&attrs);
Property pp(p);
pp.fullyPopulated(&attrs);
if (attrs == Attr_Data) {
Scope scope(ctx);
ScopedValue v(scope, pp.value);
arraySet(index, v);
} else {
arraySet(index, pp, attrs);
}
return true;
}
@ -1024,11 +991,11 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, con
if (attrs.isEmpty())
return true;
PropertyAttributes cattrs = Attr_Data;
PropertyAttributes cattrs;
if (!member.isNull())
cattrs = internalClass->propertyData[current - memberData];
else if (arrayData.attributes)
cattrs = arrayData.attributes[current - arrayData.data];
else
cattrs = arrayData->attrs ? arrayData->attrs[current - arrayData->data] : Attr_Data;
// clause 6
if (p.isSubset(attrs, *current, cattrs))
@ -1084,10 +1051,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, Property *current, con
if (!member.isNull()) {
internalClass = internalClass->changeMember(member.getPointer(), cattrs);
} else {
if (cattrs != Attr_Data)
ensureArrayAttributes();
if (arrayData.attributes)
arrayData.attributes[current - arrayData.data] = cattrs;
arrayData->setAttributes(current - arrayData->data, cattrs);
}
if (attrs.isAccessor())
hasAccessorProperty = 1;
@ -1113,7 +1077,7 @@ void Object::copyArrayData(Object *other)
Scope scope(engine());
if (other->protoHasArray() || other->hasAccessorProperty) {
uint len = other->arrayLength();
uint len = other->getLength();
Q_ASSERT(len);
ScopedValue v(scope);
@ -1121,314 +1085,98 @@ void Object::copyArrayData(Object *other)
arraySet(i, (v = other->getIndexed(i)));
}
} else {
arrayReserve(other->arrayData.length);
arrayData.length = other->arrayData.length;
memcpy(arrayData.data, other->arrayData.data, arrayData.length*sizeof(Property));
Q_ASSERT(!arrayData && other->arrayData);
if (other->arrayType() == ArrayData::Sparse) {
SparseArrayData *od = static_cast<SparseArrayData *>(other->arrayData);
SparseArrayData *dd = new SparseArrayData;
dd->type = ArrayData::Sparse;
dd->sparse = new SparseArray(*od->sparse);
dd->freeList = od->freeList;
arrayData = dd;
}
arrayReserve(other->arrayData->len);
arrayData->len = other->arrayData->len;
// ### correctly deal with accessor properties
memcpy(arrayData->data, other->arrayData->data, arrayData->len*sizeof(Property));
arrayData->offset = 0;
}
arrayData.offset = 0;
if (other->arrayData.sparse) {
flags &= ~SimpleArray;
arrayData.sparse = new SparseArray(*other->arrayData.sparse);
arrayData.freeList = other->arrayData.freeList;
}
setArrayLengthUnchecked(other->arrayLength());
setArrayLengthUnchecked(other->getLength());
}
ReturnedValue Object::arrayIndexOf(const ValueRef v, uint fromIndex, uint endIndex, ExecutionContext *ctx, Object *o)
uint Object::getLength(const Managed *m)
{
Q_UNUSED(ctx);
Scope scope(engine());
ScopedValue value(scope);
if (!(o->flags & SimpleArray) || o->protoHasArray()) {
// lets be safe and slow
for (uint i = fromIndex; i < endIndex; ++i) {
bool exists;
value = o->getIndexed(i, &exists);
if (scope.hasException())
return Encode::undefined();
if (exists && __qmljs_strict_equal(value, v))
return Encode(i);
}
} else if (arrayData.sparse) {
for (SparseArrayNode *n = arrayData.sparse->lowerBound(fromIndex); n != arrayData.sparse->end() && n->key() < endIndex; n = n->nextNode()) {
value = o->getValue(arrayData.data + n->value, arrayData.attributes ? arrayData.attributes[n->value] : Attr_Data);
if (scope.hasException())
return Encode::undefined();
if (__qmljs_strict_equal(value, v))
return Encode(n->key());
}
} else {
if (endIndex > arrayData.length)
endIndex = arrayData.length;
Property *pd = arrayData.data;
Property *end = pd + endIndex;
pd += fromIndex;
while (pd < end) {
if (!pd->value.isEmpty()) {
value = o->getValue(pd, arrayData.attributes ? arrayData.attributes[pd - arrayData.data] : Attr_Data);
if (scope.hasException())
return Encode::undefined();
if (__qmljs_strict_equal(value, v))
return Encode((uint)(pd - arrayData.data));
}
++pd;
}
}
return Encode(-1);
Scope scope(m->engine());
ScopedValue v(scope, const_cast<Managed *>(m)->get(scope.engine->id_length));
return v->toUInt32();
}
void Object::arrayConcat(const ArrayObject *other)
bool Object::setArrayLength(uint newLen)
{
int newLen = arrayData.length + other->arrayLength();
if (other->arrayData.sparse)
initSparse();
// ### copy attributes as well!
if (arrayData.sparse) {
if (other->arrayData.sparse) {
for (const SparseArrayNode *it = other->arrayData.sparse->begin(); it != other->arrayData.sparse->end(); it = it->nextNode())
arraySet(arrayData.length + it->key(), other->arrayData.data + it->value);
} else {
int oldSize = arrayData.length;
arrayReserve(oldSize + other->arrayLength());
memcpy(arrayData.data + oldSize, other->arrayData.data, other->arrayLength()*sizeof(Property));
if (arrayData.attributes)
std::fill(arrayData.attributes + oldSize, arrayData.attributes + oldSize + other->arrayLength(), PropertyAttributes(Attr_Data));
for (uint i = 0; i < other->arrayLength(); ++i) {
SparseArrayNode *n = arrayData.sparse->insert(arrayData.length + i);
n->value = oldSize + i;
}
}
} else {
uint oldSize = arrayLength();
arrayReserve(oldSize + other->arrayData.length);
if (oldSize > arrayData.length) {
for (uint i = arrayData.length; i < oldSize; ++i)
arrayData.data[i].value = Primitive::emptyValue();
}
if (other->arrayData.attributes) {
for (uint i = 0; i < other->arrayData.length; ++i) {
bool exists;
arrayData.data[oldSize + i].value = const_cast<ArrayObject *>(other)->getIndexed(i, &exists);
arrayData.length = oldSize + i + 1;
if (arrayData.attributes)
arrayData.attributes[oldSize + i] = Attr_Data;
if (!exists)
arrayData.data[oldSize + i].value = Primitive::emptyValue();
}
} else {
arrayData.length = oldSize + other->arrayData.length;
memcpy(arrayData.data + oldSize, other->arrayData.data, other->arrayData.length*sizeof(Property));
if (arrayData.attributes)
std::fill(arrayData.attributes + oldSize, arrayData.attributes + oldSize + other->arrayData.length, PropertyAttributes(Attr_Data));
}
}
setArrayLengthUnchecked(newLen);
}
void Object::arraySort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint len)
{
if (!arrayData.length)
return;
if (arrayData.sparse) {
context->throwUnimplemented(QStringLiteral("Object::sort unimplemented for sparse arrays"));
return;
}
if (len > arrayData.length)
len = arrayData.length;
// 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 (arrayData.attributes) {
for (uint i = 0; i < len; i++) {
if ((arrayData.attributes && arrayData.attributes[i].isGeneric()) || arrayData.data[i].value.isEmpty()) {
while (--len > i)
if (!((arrayData.attributes && arrayData.attributes[len].isGeneric())|| arrayData.data[len].value.isEmpty()))
break;
arrayData.data[i].value = getValue(arrayData.data + len, arrayData.attributes[len]);
arrayData.data[len].value = Primitive::emptyValue();
if (arrayData.attributes) {
arrayData.attributes[i] = Attr_Data;
arrayData.attributes[len].clear();
}
} else if (arrayData.attributes[i].isAccessor()) {
arrayData.data[i].value = getValue(arrayData.data + i, arrayData.attributes[i]);
arrayData.attributes[i] = Attr_Data;
}
}
}
if (!(comparefn->isUndefined() || comparefn->asObject())) {
context->throwTypeError();
return;
}
ArrayElementLessThan lessThan(context, thisObject, comparefn);
if (!len)
return;
Property *begin = arrayData.data;
std::sort(begin, begin + len, lessThan);
}
void Object::initSparse()
{
if (!arrayData.sparse) {
flags &= ~SimpleArray;
arrayData.sparse = new SparseArray;
for (uint i = 0; i < arrayData.length; ++i) {
if (!((arrayData.attributes && arrayData.attributes[i].isGeneric()) || arrayData.data[i].value.isEmpty())) {
SparseArrayNode *n = arrayData.sparse->insert(i);
n->value = i + arrayData.offset;
}
}
uint off = arrayData.offset;
if (!arrayData.offset) {
arrayData.freeList = arrayData.length;
} else {
arrayData.freeList = 0;
arrayData.data -= off;
arrayData.alloc += off;
int o = off;
for (int i = 0; i < o - 1; ++i) {
arrayData.data[i].value = Primitive::fromInt32(i + 1);
}
arrayData.data[o - 1].value = Primitive::fromInt32(arrayData.length + off);
}
for (uint i = arrayData.length + off; i < arrayData.alloc; ++i) {
arrayData.data[i].value = Primitive::fromInt32(i + 1);
}
}
}
void Object::arrayReserve(uint n)
{
if (n < 8)
n = 8;
if (n >= arrayData.alloc) {
uint off;
if (arrayData.sparse) {
assert(arrayData.freeList == arrayData.alloc);
// ### FIXME
arrayData.length = arrayData.alloc;
off = 0;
} else {
off = arrayData.offset;
}
arrayData.alloc = qMax(n, 2*arrayData.alloc);
Property *newArrayData = new Property[arrayData.alloc + off];
if (arrayData.data) {
memcpy(newArrayData + off, arrayData.data, sizeof(Property)*arrayData.length);
delete [] (arrayData.data - off);
}
arrayData.data = newArrayData + off;
if (arrayData.sparse) {
for (uint i = arrayData.freeList; i < arrayData.alloc; ++i) {
arrayData.data[i].value = Primitive::emptyValue();
arrayData.data[i].value = Primitive::fromInt32(i + 1);
}
}
if (arrayData.attributes) {
PropertyAttributes *newAttrs = new PropertyAttributes[arrayData.alloc];
memcpy(newAttrs, arrayData.attributes, sizeof(PropertyAttributes)*arrayData.length);
delete [] (arrayData.attributes - off);
arrayData.attributes = newAttrs;
if (arrayData.sparse) {
for (uint i = arrayData.freeList; i < arrayData.alloc; ++i)
arrayData.attributes[i] = Attr_Invalid;
}
}
}
}
void Object::ensureArrayAttributes()
{
if (arrayData.attributes)
return;
flags &= ~SimpleArray;
uint off = arrayData.sparse ? 0 : arrayData.offset;
arrayData.attributes = new PropertyAttributes[arrayData.alloc + off];
arrayData.attributes += off;
for (uint i = 0; i < arrayData.length; ++i)
arrayData.attributes[i] = Attr_Data;
for (uint i = arrayData.length; i < arrayData.alloc; ++i)
arrayData.attributes[i] = Attr_Invalid;
}
bool Object::setArrayLength(uint newLen) {
assert(isArrayObject());
Q_ASSERT(isArrayObject());
const Property *lengthProperty = memberData + ArrayObject::LengthPropertyIndex;
if (lengthProperty && !internalClass->propertyData[ArrayObject::LengthPropertyIndex].isWritable())
return false;
uint oldLen = arrayLength();
uint oldLen = getLength();
bool ok = true;
if (newLen < oldLen) {
if (arrayData.sparse) {
SparseArrayNode *begin = arrayData.sparse->lowerBound(newLen);
if (begin != arrayData.sparse->end()) {
SparseArrayNode *it = arrayData.sparse->end()->previousNode();
while (1) {
Property &pd = arrayData.data[it->value];
if (arrayData.attributes) {
if (!arrayData.attributes[it->value].isConfigurable()) {
ok = false;
newLen = it->key() + 1;
break;
} else {
arrayData.attributes[it->value].clear();
}
}
pd.value.tag = Value::Empty_Type;
pd.value.int_32 = arrayData.freeList;
arrayData.freeList = it->value;
bool brk = (it == begin);
SparseArrayNode *prev = it->previousNode();
arrayData.sparse->erase(it);
if (brk)
break;
it = prev;
}
}
} else {
Property *it = arrayData.data + arrayData.length;
const Property *begin = arrayData.data + newLen;
while (--it >= begin) {
if (arrayData.attributes) {
if (!arrayData.attributes[it - arrayData.data].isEmpty() && !arrayData.attributes[it - arrayData.data].isConfigurable()) {
ok = false;
newLen = it - arrayData.data + 1;
break;
} else {
arrayData.attributes[it - arrayData.data].clear();
}
it->value = Primitive::emptyValue();
}
}
arrayData.length = newLen;
}
uint l = arrayData->truncate(newLen);
if (l != newLen)
ok = false;
newLen = l;
} else {
if (newLen >= 0x100000)
initSparse();
initSparseArray();
}
setArrayLengthUnchecked(newLen);
return ok;
}
void Object::initSparseArray()
{
if (arrayType() == ArrayData::Sparse)
return;
SparseArrayData *data = new SparseArrayData;
data->type = ArrayData::Sparse;
data->sparse = new SparseArray;
if (!arrayData) {
arrayData = data;
return;
}
uint oldOffset = arrayData->offset;
data->data = arrayData->data - arrayData->offset;
data->attrs = arrayData->attrs;
data->len = arrayData->len;
data->alloc = arrayData->alloc;
data->offset = 0;
arrayData->data = 0;
arrayData->attrs = 0;
delete arrayData;
uint *lastFree = &data->freeList;
for (uint i = 0; i < oldOffset; ++i) {
*lastFree = i;
lastFree = &data->data[i].value.uint_32;
}
for (uint i = 0; i < data->len; ++i) {
if (!data->data[i + oldOffset].value.isEmpty()) {
SparseArrayNode *n = data->sparse->insert(i);
n->value = i + oldOffset;
} else {
*lastFree = i + oldOffset;
lastFree = &data->data[i + oldOffset].value.uint_32;
}
}
for (uint i = data->len + oldOffset; i < data->alloc; ++i) {
*lastFree = i;
lastFree = &data->data[i].value.uint_32;
}
arrayData = data;
}
DEFINE_MANAGED_VTABLE(ArrayObject);
ArrayObject::ArrayObject(ExecutionEngine *engine, const QStringList &list)
@ -1444,9 +1192,10 @@ ArrayObject::ArrayObject(ExecutionEngine *engine, const QStringList &list)
// elements converted to JS Strings.
int len = list.count();
arrayReserve(len);
ScopedValue v(scope);
for (int ii = 0; ii < len; ++ii) {
arrayData.data[ii].value = Encode(engine->newString(list.at(ii)));
arrayData.length = ii + 1;
arrayData->put(ii, (v = engine->newString(list.at(ii))));
arrayData->setLength(ii + 1);
}
setArrayLengthUnchecked(len);
}
@ -1458,6 +1207,25 @@ void ArrayObject::init(ExecutionEngine *engine)
memberData[LengthPropertyIndex].value = Primitive::fromInt32(0);
}
ReturnedValue ArrayObject::getLookup(Managed *m, Lookup *l)
{
if (l->name->equals(m->engine()->id_length)) {
// special case, as the property is on the object itself
l->getter = Lookup::arrayLengthGetter;
ArrayObject *a = static_cast<ArrayObject *>(m);
return a->memberData[ArrayObject::LengthPropertyIndex].value.asReturnedValue();
}
return Object::getLookup(m, l);
}
uint ArrayObject::getLength(const Managed *m)
{
const ArrayObject *a = static_cast<const ArrayObject *>(m);
if (a->memberData[ArrayObject::LengthPropertyIndex].value.isInteger())
return a->memberData[ArrayObject::LengthPropertyIndex].value.integerValue();
return Primitive::toUInt32(a->memberData[ArrayObject::LengthPropertyIndex].value.doubleValue());
}
QStringList ArrayObject::toQStringList() const
{
QStringList result;
@ -1466,7 +1234,7 @@ QStringList ArrayObject::toQStringList() const
Scope scope(engine);
ScopedValue v(scope);
uint32_t length = arrayLength();
uint32_t length = getLength();
for (uint32_t i = 0; i < length; ++i) {
v = const_cast<ArrayObject *>(this)->getIndexed(i);
result.append(v->toQStringNoThrow());

View File

@ -49,7 +49,7 @@
#include "qv4managed_p.h"
#include "qv4property_p.h"
#include "qv4internalclass_p.h"
#include "qv4sparsearray_p.h"
#include "qv4arraydata_p.h"
#include <QtCore/QString>
#include <QtCore/QHash>
@ -100,6 +100,7 @@ struct SyntaxErrorPrototype;
struct TypeErrorPrototype;
struct URIErrorPrototype;
struct Q_QML_EXPORT Object: Managed {
Q_MANAGED
Q_MANAGED_TYPE(Object)
@ -109,18 +110,7 @@ struct Q_QML_EXPORT Object: Managed {
uint memberDataAlloc;
Property *memberData;
struct ArrayData {
union {
uint freeList;
uint offset;
};
uint length;
uint alloc;
PropertyAttributes *attributes;
Property *data;
SparseArray *sparse;
};
ArrayData arrayData;
ArrayData *arrayData;
enum {
InlinePropertySize = 4
@ -183,107 +173,54 @@ struct Q_QML_EXPORT Object: Managed {
// Array handling
uint allocArrayValue() {
uint idx = arrayData.freeList;
if (arrayData.alloc <= arrayData.freeList)
arrayReserve(arrayData.alloc + 1);
arrayData.freeList = arrayData.data[arrayData.freeList].value.uint_32;
if (arrayData.attributes)
arrayData.attributes[idx].setType(PropertyAttributes::Data);
return idx;
}
uint allocArrayValue(const ValueRef v) {
uint idx = allocArrayValue();
Property *pd = &arrayData.data[idx];
pd->value = *v;
return idx;
}
void freeArrayValue(int idx) {
Property &pd = arrayData.data[idx];
pd.value.tag = Value::Empty_Type;
pd.value.int_32 = arrayData.freeList;
arrayData.freeList = idx;
if (arrayData.attributes)
arrayData.attributes[idx].clear();
}
void getArrayHeadRoom() {
assert(!arrayData.sparse && !arrayData.offset);
arrayData.offset = qMax(arrayData.length >> 2, (uint)16);
Property *newArray = new Property[arrayData.offset + arrayData.alloc];
memcpy(newArray + arrayData.offset, arrayData.data, arrayData.length*sizeof(Property));
delete [] arrayData.data;
arrayData.data = newArray + arrayData.offset;
if (arrayData.attributes) {
PropertyAttributes *newAttrs = new PropertyAttributes[arrayData.offset + arrayData.alloc];
memcpy(newAttrs + arrayData.offset, arrayData.attributes, arrayData.length*sizeof(PropertyAttributes));
delete [] arrayData.attributes;
arrayData.attributes = newAttrs + arrayData.offset;
}
}
public:
void copyArrayData(Object *other);
void initSparse();
uint arrayLength() const;
bool setArrayLength(uint newLen);
void setArrayLengthUnchecked(uint l);
Property *arrayInsert(uint index, PropertyAttributes attributes = Attr_Data);
void arraySet(uint index, const Property &p, PropertyAttributes attributes = Attr_Data);
void arraySet(uint index, const Property *pd);
void arraySet(uint index, ValueRef value);
uint propertyIndexFromArrayIndex(uint index) const
{
if (!arrayData.sparse) {
if (index >= arrayData.length)
return UINT_MAX;
return index;
} else {
SparseArrayNode *n = arrayData.sparse->findNode(index);
if (!n)
return UINT_MAX;
return n->value;
}
}
Property *arrayAt(uint index) const {
uint pidx = propertyIndexFromArrayIndex(index);
if (pidx == UINT_MAX)
return 0;
return arrayData.data + pidx;
}
Property *nonSparseArrayAt(uint index) const {
if (arrayData.sparse)
return 0;
if (index >= arrayData.length)
return 0;
return arrayData.data + index;
}
void push_back(const ValueRef v);
SparseArrayNode *sparseArrayBegin() { return arrayData.sparse ? arrayData.sparse->begin() : 0; }
SparseArrayNode *sparseArrayEnd() { return arrayData.sparse ? arrayData.sparse->end() : 0; }
ArrayData::Type arrayType() const {
return arrayData ? (ArrayData::Type)arrayData->type : ArrayData::Simple;
}
// ### remove me
void setArrayType(ArrayData::Type t) {
Q_ASSERT(t != ArrayData::Simple && t != ArrayData::Sparse);
arrayCreate();
arrayData->type = t;
}
void arrayConcat(const ArrayObject *other);
void arraySort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint dataLen);
ReturnedValue arrayIndexOf(const ValueRef v, uint fromIndex, uint dataLen, ExecutionContext *ctx, Object *o);
inline void arrayReserve(uint n) {
arrayCreate();
arrayData->vtable->reserve(arrayData, n);
}
void arrayReserve(uint n);
void ensureArrayAttributes();
void arrayCreate() {
if (!arrayData)
arrayData = new ArrayData;
}
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);
while ((p = p->prototype()))
if (p->arrayData.length)
if (p->arrayData)
return true;
return false;
@ -309,6 +246,7 @@ public:
using Managed::getLookup;
using Managed::setLookup;
using Managed::advanceIterator;
using Managed::getLength;
protected:
static void destroy(Managed *that);
static void markObjects(Managed *that, ExecutionEngine *e);
@ -323,7 +261,7 @@ protected:
static ReturnedValue getLookup(Managed *m, Lookup *l);
static void setLookup(Managed *m, Lookup *l, const ValueRef v);
static Property *advanceIterator(Managed *m, ObjectIterator *it, StringRef name, uint *index, PropertyAttributes *attributes);
static uint getLength(const Managed *m);
private:
ReturnedValue internalGet(const StringRef name, bool *hasProperty);
@ -382,19 +320,13 @@ struct ArrayObject: Object {
void init(ExecutionEngine *engine);
static ReturnedValue getLookup(Managed *m, Lookup *l);
using Managed::getLength;
static uint getLength(const Managed *m);
QStringList toQStringList() const;
};
inline uint Object::arrayLength() const
{
if (isArrayObject()) {
if (memberData[ArrayObject::LengthPropertyIndex].value.isInteger())
return memberData[ArrayObject::LengthPropertyIndex].value.integerValue();
return Primitive::toUInt32(memberData[ArrayObject::LengthPropertyIndex].value.doubleValue());
}
return 0;
}
inline void Object::setArrayLengthUnchecked(uint l)
{
if (isArrayObject()) {
@ -406,64 +338,35 @@ inline void Object::setArrayLengthUnchecked(uint l)
inline void Object::push_back(const ValueRef v)
{
uint idx = arrayLength();
if (!arrayData.sparse) {
if (idx >= arrayData.alloc)
arrayReserve(idx + 1);
arrayData.data[idx].value = *v;
arrayData.length = idx + 1;
} else {
uint idx = allocArrayValue(v);
arrayData.sparse->push_back(idx, arrayLength());
}
arrayCreate();
Q_ASSERT(!arrayData->isSparse());
uint idx = getLength();
arrayReserve(idx + 1);
arrayData->put(idx, v);
arrayData->setLength(idx + 1);
setArrayLengthUnchecked(idx + 1);
}
inline Property *Object::arrayInsert(uint index, PropertyAttributes attributes) {
inline void Object::arraySet(uint index, const Property &p, PropertyAttributes attributes)
{
if (attributes.isAccessor())
hasAccessorProperty = 1;
Property *pd;
if (!arrayData.sparse && (index < 0x1000 || index < arrayData.length + (arrayData.length >> 2))) {
if (index >= arrayData.alloc)
arrayReserve(index + 1);
if (index >= arrayData.length) {
// mark possible hole in the array
for (uint i = arrayData.length; i < index; ++i) {
arrayData.data[i].value = Primitive::emptyValue();
if (arrayData.attributes)
arrayData.attributes[i].clear();
}
arrayData.length = index + 1;
}
pd = arrayData.data + index;
} else {
initSparse();
SparseArrayNode *n = arrayData.sparse->insert(index);
if (n->value == UINT_MAX)
n->value = allocArrayValue();
pd = arrayData.data + n->value;
}
if (index >= arrayLength())
Property *pd = arrayInsert(index);
*pd = p;
arrayData->setAttributes(index, attributes);
if (isArrayObject() && index >= getLength())
setArrayLengthUnchecked(index + 1);
if (arrayData.attributes || attributes != Attr_Data) {
if (!arrayData.attributes)
ensureArrayAttributes();
attributes.resolve();
arrayData.attributes[pd - arrayData.data] = attributes;
}
return pd;
}
inline void Object::arraySet(uint index, ValueRef value)
{
Property *pd = arrayInsert(index);
pd->value = *value;
}
inline void Object::arraySet(uint index, const Property *pd)
{
*arrayInsert(index) = *pd;
pd->value = value ? *value : Primitive::undefinedValue();
if (isArrayObject() && index >= getLength())
setArrayLengthUnchecked(index + 1);
}
template<>

View File

@ -271,10 +271,12 @@ ReturnedValue ObjectPrototype::method_seal(CallContext *ctx)
o->internalClass = o->internalClass->sealed();
o->ensureArrayAttributes();
for (uint i = 0; i < o->arrayData.length; ++i) {
if (!(o->arrayData.attributes[i].isGeneric() || o->arrayData.data[i].value.isEmpty()))
o->arrayData.attributes[i].setConfigurable(false);
if (o->arrayData) {
o->arrayData->ensureAttributes();
for (uint i = 0; i < o->arrayData->length(); ++i) {
if (!o->arrayData->isEmpty(i))
o->arrayData->attrs[i].setConfigurable(false);
}
}
return o.asReturnedValue();
@ -294,12 +296,14 @@ ReturnedValue ObjectPrototype::method_freeze(CallContext *ctx)
o->internalClass = o->internalClass->frozen();
o->ensureArrayAttributes();
for (uint i = 0; i < o->arrayData.length; ++i) {
if (!(o->arrayData.attributes[i].isGeneric() || o->arrayData.data[i].value.isEmpty()))
o->arrayData.attributes[i].setConfigurable(false);
if (o->arrayData.attributes[i].isData())
o->arrayData.attributes[i].setWritable(false);
if (o->arrayData) {
o->arrayData->ensureAttributes();
for (uint i = 0; i < o->arrayData->length(); ++i) {
if (!o->arrayData->isEmpty(i))
o->arrayData->attrs[i].setConfigurable(false);
if (o->arrayData->attrs[i].isData())
o->arrayData->attrs[i].setWritable(false);
}
}
return o.asReturnedValue();
}
@ -328,15 +332,16 @@ ReturnedValue ObjectPrototype::method_isSealed(CallContext *ctx)
if (o->internalClass != o->internalClass->sealed())
return Encode(false);
if (!o->arrayData.length)
if (!o->arrayData || !o->arrayData->length())
return Encode(true);
if (!o->arrayData.attributes)
if (o->arrayData->length() && !o->arrayData->attrs)
return Encode(false);
for (uint i = 0; i < o->arrayData.length; ++i) {
if (!(o->arrayData.attributes[i].isGeneric() || o->arrayData.data[i].value.isEmpty()))
if (o->arrayData.attributes[i].isConfigurable())
for (uint i = 0; i < o->arrayData->length(); ++i) {
// ### Fix for sparse arrays
if (!o->arrayData->isEmpty(i))
if (o->arrayData->attributes(i).isConfigurable())
return Encode(false);
}
@ -356,15 +361,16 @@ ReturnedValue ObjectPrototype::method_isFrozen(CallContext *ctx)
if (o->internalClass != o->internalClass->frozen())
return Encode(false);
if (!o->arrayData.length)
if (!o->arrayData->length())
return Encode(true);
if (!o->arrayData.attributes)
if (o->arrayData->length() && !o->arrayData->attrs)
return Encode(false);
for (uint i = 0; i < o->arrayData.length; ++i) {
if (!(o->arrayData.attributes[i].isGeneric() || o->arrayData.data[i].value.isEmpty()))
if (o->arrayData.attributes[i].isConfigurable() || o->arrayData.attributes[i].isWritable())
for (uint i = 0; i < o->arrayData->length(); ++i) {
// ### Fix for sparse arrays
if (!o->arrayData->isEmpty(i))
if (o->arrayData->attributes(i).isConfigurable() || o->arrayData->attributes(i).isWritable())
return Encode(false);
}

View File

@ -53,7 +53,7 @@ struct FunctionObject;
struct Property {
union {
Value value;
SafeValue value;
struct {
FunctionObject *get;
FunctionObject *set;

View File

@ -1592,7 +1592,7 @@ void CallArgument::fromValue(int callType, QV8Engine *engine, const QV4::ValueRe
if (array) {
Scoped<QV4::QObjectWrapper> qobjectWrapper(scope);
uint32_t length = array->arrayLength();
uint32_t length = array->getLength();
for (uint32_t ii = 0; ii < length; ++ii) {
QObject *o = 0;
qobjectWrapper = array->getIndexed(ii);
@ -1689,9 +1689,10 @@ QV4::ReturnedValue CallArgument::toValue(QV8Engine *engine)
QList<QObject *> &list = *qlistPtr;
QV4::Scoped<ArrayObject> array(scope, v4->newArrayObject());
array->arrayReserve(list.count());
QV4::ScopedValue v(scope);
for (int ii = 0; ii < list.count(); ++ii) {
array->arrayData.data[ii].value = QV4::QObjectWrapper::wrap(v4, list.at(ii));
array->arrayData.length = ii + 1;
array->arrayData->put(ii, (v = QV4::QObjectWrapper::wrap(v4, list.at(ii))));
array->arrayData->setLength(ii + 1);
}
array->setArrayLengthUnchecked(list.count());
return array.asReturnedValue();

View File

@ -344,11 +344,13 @@ ReturnedValue RegExpPrototype::method_exec(CallContext *ctx)
Scoped<ArrayObject> array(scope, ctx->engine->newArrayObject(ctx->engine->regExpExecArrayClass));
int len = r->value->captureCount();
array->arrayReserve(len);
ScopedValue v(scope);
for (int i = 0; i < len; ++i) {
int start = matchOffsets[i * 2];
int end = matchOffsets[i * 2 + 1];
array->arrayData.data[i].value = (start != -1 && end != -1) ? ctx->engine->newString(s.mid(start, end - start))->asReturnedValue() : Encode::undefined();
array->arrayData.length = i + 1;
v = (start != -1 && end != -1) ? ctx->engine->newString(s.mid(start, end - start))->asReturnedValue() : Encode::undefined();
array->arrayData->put(i, v);
array->arrayData->setLength(i + 1);
}
array->setArrayLengthUnchecked(len);

View File

@ -589,12 +589,10 @@ ReturnedValue __qmljs_get_element(ExecutionContext *ctx, const ValueRef object,
}
if (idx < UINT_MAX) {
uint pidx = o->propertyIndexFromArrayIndex(idx);
if (pidx < UINT_MAX) {
if (!o->arrayData.attributes || o->arrayData.attributes[pidx].isData()) {
if (!o->arrayData.data[pidx].value.isEmpty())
return o->arrayData.data[pidx].value.asReturnedValue();
}
if (!o->arrayData->hasAttributes()) {
ScopedValue v(scope, o->arrayData->get(idx));
if (!v->isEmpty())
return v->asReturnedValue();
}
return o->getIndexed(idx);
@ -615,36 +613,10 @@ void __qmljs_set_element(ExecutionContext *ctx, const ValueRef object, const Val
uint idx = index->asArrayIndex();
if (idx < UINT_MAX) {
uint pidx = o->propertyIndexFromArrayIndex(idx);
if (pidx < UINT_MAX && !o->asArgumentsObject()) {
if (o->arrayData.attributes && !o->arrayData.attributes[pidx].isEmpty() && !o->arrayData.attributes[pidx].isWritable()) {
if (ctx->strictMode)
ctx->throwTypeError();
return;
}
Property *p = o->arrayData.data + pidx;
if (!o->arrayData.attributes || o->arrayData.attributes[pidx].isData()) {
p->value = *value;
return;
}
if (o->arrayData.attributes[pidx].isAccessor()) {
FunctionObject *setter = p->setter();
if (!setter) {
if (ctx->strictMode)
ctx->throwTypeError();
return;
}
ScopedCallData callData(scope, 1);
callData->thisObject = o;
callData->args[0] = *value;
setter->call(callData);
return;
}
}
o->putIndexed(idx, value);
if (idx < o->arrayData->length() && o->arrayType() == ArrayData::Simple)
o->arrayData->put(idx, value);
else
o->putIndexed(idx, value);
return;
}
@ -1118,25 +1090,23 @@ void __qmljs_builtin_define_property(ExecutionContext *ctx, const ValueRef objec
assert(o);
uint idx = name->asArrayIndex();
Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx) : o->insertMember(name, Attr_Data);
pd->value = val ? *val : Primitive::undefinedValue();
if (idx != UINT_MAX) {
o->arraySet(idx, val);
} else {
Property *pd = o->insertMember(name, Attr_Data);
pd->value = val ? *val : Primitive::undefinedValue();
}
}
ReturnedValue __qmljs_builtin_define_array(ExecutionContext *ctx, Value *values, uint length)
ReturnedValue __qmljs_builtin_define_array(ExecutionContext *ctx, SafeValue *values, uint length)
{
Scope scope(ctx);
Scoped<ArrayObject> a(scope, ctx->engine->newArrayObject());
// ### FIXME: We need to allocate the array data to avoid crashes other places
// This should rather be done when required
a->arrayReserve(length);
if (length) {
a->arrayData.length = length;
Property *pd = a->arrayData.data;
for (uint i = 0; i < length; ++i) {
pd->value = values[i];
++pd;
}
a->arrayReserve(length);
a->arrayData->setLength(length);
a->arrayData->put(0, values, length);
a->setArrayLengthUnchecked(length);
}
return a.asReturnedValue();
@ -1149,9 +1119,16 @@ void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const ValueRef
Q_ASSERT(!!o);
uint idx = name->asArrayIndex();
Property *pd = (idx != UINT_MAX) ? o->arrayInsert(idx, Attr_Accessor) : o->insertMember(name, Attr_Accessor);
pd->setGetter(getter ? getter->asFunctionObject() : 0);
pd->setSetter(setter ? setter->asFunctionObject() : 0);
if (idx != UINT_MAX) {
Property pd;
pd.setGetter(getter ? getter->asFunctionObject() : 0);
pd.setSetter(setter ? setter->asFunctionObject() : 0);
o->arraySet(idx, pd, Attr_Accessor);
} else {
Property *pd = o->insertMember(name, Attr_Accessor);
pd->setGetter(getter ? getter->asFunctionObject() : 0);
pd->setSetter(setter ? setter->asFunctionObject() : 0);
}
}
ReturnedValue __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, const QV4::Value *args, int classId)

View File

@ -141,7 +141,7 @@ QV4::ExecutionContext *__qmljs_builtin_pop_scope(QV4::ExecutionContext *ctx);
ReturnedValue __qmljs_builtin_unwind_exception(ExecutionContext *ctx);
void __qmljs_builtin_declare_var(QV4::ExecutionContext *ctx, bool deletable, const QV4::StringRef name);
void __qmljs_builtin_define_property(QV4::ExecutionContext *ctx, const QV4::ValueRef object, const QV4::StringRef name, QV4::ValueRef val);
QV4::ReturnedValue __qmljs_builtin_define_array(QV4::ExecutionContext *ctx, QV4::Value *values, uint length);
QV4::ReturnedValue __qmljs_builtin_define_array(QV4::ExecutionContext *ctx, SafeValue *values, uint length);
void __qmljs_builtin_define_getter_setter(QV4::ExecutionContext *ctx, const QV4::ValueRef object, const QV4::StringRef name, const QV4::ValueRef getter, const QV4::ValueRef setter);
QV4::ReturnedValue __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, const QV4::Value *args, int classId);
QV4::ReturnedValue __qmljs_builtin_setup_arguments_object(ExecutionContext *ctx);

View File

@ -175,7 +175,7 @@ public:
, m_propertyIndex(-1)
, m_isReference(false)
{
flags &= ~SimpleArray;
setArrayType(ArrayData::Custom);
QV4::Scope scope(engine);
QV4::ScopedObject protectThis(scope, this);
Q_UNUSED(protectThis);
@ -188,7 +188,7 @@ public:
, m_propertyIndex(propertyIndex)
, m_isReference(true)
{
flags &= ~SimpleArray;
setArrayType(ArrayData::Custom);
QV4::Scope scope(engine);
QV4::ScopedObject protectThis(scope, this);
Q_UNUSED(protectThis);
@ -468,7 +468,7 @@ public:
{
QV4::Scope scope(array->engine());
Container result;
quint32 length = array->arrayLength();
quint32 length = array->getLength();
QV4::ScopedValue v(scope);
for (quint32 i = 0; i < length; ++i)
result << convertValueToElement<typename Container::value_type>((v = array->getIndexed(i)));

View File

@ -183,7 +183,7 @@ void Serialize::serialize(QByteArray &data, const QV4::ValueRef v, QV8Engine *en
push(data, valueheader(WorkerUndefined));
} else if (v->asArrayObject()) {
QV4::ScopedArrayObject array(scope, v);
uint32_t length = array->arrayLength();
uint32_t length = array->getLength();
if (length > 0xFFFFFF) {
push(data, valueheader(WorkerUndefined));
return;
@ -266,7 +266,7 @@ void Serialize::serialize(QByteArray &data, const QV4::ValueRef v, QV8Engine *en
// regular object
QV4::ScopedValue val(scope, *v);
QV4::ScopedArrayObject properties(scope, QV4::ObjectPrototype::getOwnPropertyNames(v4, val));
quint32 length = properties->arrayLength();
quint32 length = properties->getLength();
if (length > 0xFFFFFF) {
push(data, valueheader(WorkerUndefined));
return;
@ -390,8 +390,8 @@ ReturnedValue Serialize::deserialize(const char *&data, QV8Engine *engine)
array->arrayReserve(seqLength);
for (quint32 ii = 0; ii < seqLength; ++ii) {
value = deserialize(data, engine);
array->arrayData.data[ii].value = value.asReturnedValue();
array->arrayData.length = ii + 1;
array->arrayData->put(ii, value);
array->arrayData->setLength(ii + 1);
}
array->setArrayLengthUnchecked(seqLength);
QVariant seqVariant = QV4::SequencePrototype::toVariant(array, sequenceType, &succeeded);

View File

@ -126,6 +126,7 @@ const ManagedVTable String::static_vtbl =
0 /*getLookup*/,
0 /*setLookup*/,
isEqualTo,
0,
0 /*advanceIterator*/,
"String",
};
@ -422,3 +423,8 @@ uint String::toArrayIndex(const QString &str)
bool ok;
return ::toArrayIndex(str.constData(), str.constData() + str.length(), &ok);
}
uint String::getLength(const Managed *m)
{
return static_cast<const String *>(m)->length();
}

View File

@ -91,6 +91,7 @@ struct Q_QML_EXPORT String : public Managed {
return toQString() == other->toQString();
}
inline bool compare(const String *other) {
return toQString() < other->toQString();
}
@ -172,6 +173,7 @@ protected:
static bool deleteProperty(Managed *, const StringRef);
static bool deleteIndexedProperty(Managed *m, uint index);
static bool isEqualTo(Managed *that, Managed *o);
static uint getLength(const Managed *m);
private:
QChar *recursiveAppend(QChar *ch) const;

View File

@ -144,14 +144,20 @@ Property *StringObject::advanceIterator(Managed *m, ObjectIterator *it, StringRe
while (it->arrayIndex < slen) {
*index = it->arrayIndex;
++it->arrayIndex;
if (attrs)
*attrs = s->arrayData.attributes ? s->arrayData.attributes[it->arrayIndex] : PropertyAttributes(Attr_NotWritable|Attr_NotConfigurable);
return s->__getOwnProperty__(*index);
PropertyAttributes a;
Property *p = s->__getOwnProperty__(*index, &a);
if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) {
if (attrs)
*attrs = a;
return p;
}
}
if (s->arrayData) {
it->arrayNode = s->sparseBegin();
// iterate until we're past the end of the string
while (it->arrayNode && it->arrayNode->key() < slen)
it->arrayNode = it->arrayNode->nextNode();
}
it->arrayNode = s->sparseArrayBegin();
// iterate until we're past the end of the string
while (it->arrayNode && it->arrayNode->key() < slen)
it->arrayNode = it->arrayNode->nextNode();
}
return Object::advanceIterator(m, it, name, index, attrs);
@ -692,18 +698,18 @@ ReturnedValue StringPrototype::method_split(CallContext *ctx)
array->push_back((s = ctx->engine->newString(text.mid(offset, matchOffsets[0] - offset))));
offset = qMax(offset + 1, matchOffsets[1]);
if (array->arrayLength() >= limit)
if (array->getLength() >= limit)
break;
for (int i = 1; i < re->value->captureCount(); ++i) {
uint start = matchOffsets[i * 2];
uint end = matchOffsets[i * 2 + 1];
array->push_back((s = ctx->engine->newString(text.mid(start, end - start))));
if (array->arrayLength() >= limit)
if (array->getLength() >= limit)
break;
}
}
if (array->arrayLength() < limit)
if (array->getLength() < limit)
array->push_back((s = ctx->engine->newString(text.mid(offset))));
} else {
QString separator = separatorValue->toString(ctx)->toQString();
@ -718,10 +724,10 @@ ReturnedValue StringPrototype::method_split(CallContext *ctx)
while ((end = text.indexOf(separator, start)) != -1) {
array->push_back((s = ctx->engine->newString(text.mid(start, end - start))));
start = end + separator.size();
if (array->arrayLength() >= limit)
if (array->getLength() >= limit)
break;
}
if (array->arrayLength() < limit && start != -1)
if (array->getLength() < limit && start != -1)
array->push_back((s = ctx->engine->newString(text.mid(start))));
}
return array.asReturnedValue();

View File

@ -57,7 +57,7 @@ QmlListWrapper::QmlListWrapper(QV8Engine *engine)
v8(engine)
{
setVTable(&static_vtbl);
flags &= ~SimpleArray;
setArrayType(ArrayData::Custom);
}
QmlListWrapper::~QmlListWrapper()

View File

@ -554,12 +554,12 @@ QV4::ReturnedValue QQmlLocaleData::method_get_weekDays(QV4::CallContext *ctx)
QV4::Scoped<QV4::ArrayObject> result(scope, ctx->engine->newArrayObject());
result->arrayReserve(days.size());
result->arrayData.length = days.size();
result->arrayData->setLength(days.size());
for (int i = 0; i < days.size(); ++i) {
int day = days.at(i);
if (day == 7) // JS Date days in range 0(Sunday) to 6(Saturday)
day = 0;
result->arrayData.data[i].value = QV4::Primitive::fromInt32(day);
result->arrayData->put(i, QV4::Primitive::fromInt32(day));
}
result->setArrayLengthUnchecked(days.size());
@ -576,9 +576,10 @@ QV4::ReturnedValue QQmlLocaleData::method_get_uiLanguages(QV4::CallContext *ctx)
QStringList langs = locale->uiLanguages();
QV4::Scoped<QV4::ArrayObject> result(scope, ctx->engine->newArrayObject());
result->arrayReserve(langs.size());
QV4::ScopedValue v(scope);
for (int i = 0; i < langs.size(); ++i) {
result->arrayData.data[i].value = ctx->engine->newString(langs.at(i));
result->arrayData.length = i + 1;
result->arrayData->put(i, (v = ctx->engine->newString(langs.at(i))));
result->arrayData->setLength(i + 1);
}
result->setArrayLengthUnchecked(langs.size());

View File

@ -164,7 +164,7 @@ QVariant QV8Engine::toVariant(const QV4::ValueRef value, int typeHint)
QV4::ScopedArrayObject a(scope, value);
if (typeHint == qMetaTypeId<QList<QObject *> >()) {
QList<QObject *> list;
uint32_t length = a->arrayLength();
uint32_t length = a->getLength();
QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope);
for (uint32_t ii = 0; ii < length; ++ii) {
qobjectWrapper = a->getIndexed(ii);
@ -196,9 +196,10 @@ static QV4::ReturnedValue arrayFromStringList(QV8Engine *engine, const QStringLi
QV4::Scoped<QV4::ArrayObject> a(scope, e->newArrayObject());
int len = list.count();
a->arrayReserve(len);
QV4::ScopedValue v(scope);
for (int ii = 0; ii < len; ++ii) {
a->arrayData.data[ii].value = QV4::Encode(e->newString(list.at(ii)));
a->arrayData.length = ii + 1;
a->arrayData->put(ii, (v = QV4::Encode(e->newString(list.at(ii)))));
a->arrayData->setLength(ii + 1);
}
a->setArrayLengthUnchecked(len);
return a.asReturnedValue();
@ -211,9 +212,10 @@ static QV4::ReturnedValue arrayFromVariantList(QV8Engine *engine, const QVariant
QV4::Scoped<QV4::ArrayObject> a(scope, e->newArrayObject());
int len = list.count();
a->arrayReserve(len);
QV4::ScopedValue v(scope);
for (int ii = 0; ii < len; ++ii) {
a->arrayData.data[ii].value = engine->fromVariant(list.at(ii));
a->arrayData.length = ii + 1;
a->arrayData->put(ii, (v = engine->fromVariant(list.at(ii))));
a->arrayData->setLength(ii + 1);
}
a->setArrayLengthUnchecked(len);
return a.asReturnedValue();
@ -325,9 +327,10 @@ QV4::ReturnedValue QV8Engine::fromVariant(const QVariant &variant)
const QList<QObject *> &list = *(QList<QObject *>*)ptr;
QV4::Scoped<QV4::ArrayObject> a(scope, m_v4Engine->newArrayObject());
a->arrayReserve(list.count());
QV4::ScopedValue v(scope);
for (int ii = 0; ii < list.count(); ++ii) {
a->arrayData.data[ii].value = QV4::QObjectWrapper::wrap(m_v4Engine, list.at(ii));
a->arrayData.length = ii + 1;
a->arrayData->put(ii, (v = QV4::QObjectWrapper::wrap(m_v4Engine, list.at(ii))));
a->arrayData->setLength(ii + 1);
}
a->setArrayLengthUnchecked(list.count());
return a.asReturnedValue();
@ -407,7 +410,7 @@ QVariant QV8Engine::toBasicVariant(const QV4::ValueRef value)
QV4::ScopedValue v(scope);
QVariantList rv;
int length = a->arrayLength();
int length = a->getLength();
for (int ii = 0; ii < length; ++ii) {
v = a->getIndexed(ii);
rv << toVariant(v, -1);
@ -540,9 +543,10 @@ QV4::ReturnedValue QV8Engine::variantListToJS(const QVariantList &lst)
QV4::Scope scope(m_v4Engine);
QV4::Scoped<QV4::ArrayObject> a(scope, m_v4Engine->newArrayObject());
a->arrayReserve(lst.size());
QV4::ScopedValue v(scope);
for (int i = 0; i < lst.size(); i++) {
a->arrayData.data[i].value = variantToJS(lst.at(i));
a->arrayData.length = i + 1;
a->arrayData->put(i, (v = variantToJS(lst.at(i))));
a->arrayData->setLength(i + 1);
}
a->setArrayLengthUnchecked(lst.size());
return a.asReturnedValue();
@ -568,7 +572,7 @@ QVariantList QV8Engine::variantListFromJS(QV4::ArrayObjectRef a,
QV4::Scope scope(a->engine());
QV4::ScopedValue v(scope);
quint32 length = a->arrayLength();
quint32 length = a->getLength();
for (quint32 i = 0; i < length; ++i) {
v = a->getIndexed(i);
result.append(variantFromJS(v, visitedObjects));

View File

@ -1692,7 +1692,7 @@ int QQmlDelegateModelItemMetaType::parseGroups(const QV4::ValueRef groups) const
QV4::ScopedArrayObject array(scope, groups);
if (array) {
QV4::ScopedValue v(scope);
uint arrayLength = array->arrayLength();
uint arrayLength = array->getLength();
for (uint i = 0; i < arrayLength; ++i) {
v = array->getIndexed(i);
const QString groupName = v->toQString();
@ -3189,7 +3189,7 @@ public:
: Object(engine)
{
setVTable(&static_vtbl);
flags &= ~SimpleArray;
setArrayType(QV4::ArrayData::Custom);
}
virtual ~QQmlDelegateModelGroupChangeArray() {}

View File

@ -441,7 +441,7 @@ void ListModel::set(int elementIndex, QV4::ObjectRef object, QVector<int> *roles
const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List);
ListModel *subModel = new ListModel(r.subLayout, 0, -1);
int arrayLength = a->arrayLength();
int arrayLength = a->getLength();
for (int j=0 ; j < arrayLength ; ++j) {
o = a->getIndexed(j);
subModel->append(o, eng);
@ -517,7 +517,7 @@ void ListModel::set(int elementIndex, QV4::ObjectRef object, QV8Engine *eng)
if (r.type == ListLayout::Role::List) {
ListModel *subModel = new ListModel(r.subLayout, 0, -1);
int arrayLength = a->arrayLength();
int arrayLength = a->getLength();
for (int j=0 ; j < arrayLength ; ++j) {
o = a->getIndexed(j);
subModel->append(o, eng);
@ -1188,7 +1188,7 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::ValueRef
QV4::Scoped<QV4::Object> o(scope);
ListModel *subModel = new ListModel(role.subLayout, 0, -1);
int arrayLength = a->arrayLength();
int arrayLength = a->getLength();
for (int j=0 ; j < arrayLength ; ++j) {
o = a->getIndexed(j);
subModel->append(o, eng);
@ -1953,7 +1953,7 @@ void QQmlListModel::insert(QQmlV4Function *args)
if (objectArray) {
QV4::ScopedObject argObject(scope);
int objectArrayLength = objectArray->arrayLength();
int objectArrayLength = objectArray->getLength();
for (int i=0 ; i < objectArrayLength ; ++i) {
argObject = objectArray->getIndexed(i);
@ -2055,7 +2055,7 @@ void QQmlListModel::append(QQmlV4Function *args)
if (objectArray) {
QV4::Scoped<QV4::Object> argObject(scope);
int objectArrayLength = objectArray->arrayLength();
int objectArrayLength = objectArray->getLength();
int index = count();
for (int i=0 ; i < objectArrayLength ; ++i) {

View File

@ -871,7 +871,7 @@ struct QQuickJSContext2DPixelData : public QV4::Object
: QV4::Object(engine)
{
setVTable(&static_vtbl);
flags &= ~SimpleArray;
setArrayType(QV4::ArrayData::Custom);
}
static void destroy(QV4::Managed *that) {

View File

@ -398,7 +398,7 @@ public:
if (!array)
return QMatrix4x4();
if (array->arrayLength() != 16)
if (array->getLength() != 16)
return QMatrix4x4();
float matVals[16];

View File

@ -3910,7 +3910,7 @@ void tst_qqmlecmascript::verifyContextLifetime(QQmlContextData *ctxt) {
QV4::Scope scope(v4);
QV4::ScopedArrayObject scripts(scope, ctxt->importedScripts);
QV4::ScopedValue qml(scope);
for (quint32 i = 0; i < scripts->arrayLength(); ++i) {
for (quint32 i = 0; i < scripts->getLength(); ++i) {
QQmlContextData *scriptContext, *newContext;
qml = scripts->getIndexed(i);