2013-12-16 08:16:57 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** 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"
|
2014-01-24 13:29:40 +00:00
|
|
|
#include "qv4mm_p.h"
|
2013-12-16 08:16:57 +00:00
|
|
|
|
|
|
|
using namespace QV4;
|
|
|
|
|
2014-07-17 11:43:07 +00:00
|
|
|
const QV4::ManagedVTable QV4::ArrayData::static_vtbl = {
|
|
|
|
0,
|
|
|
|
QV4::ArrayData::IsExecutionContext,
|
|
|
|
QV4::ArrayData::IsString,
|
|
|
|
QV4::ArrayData::IsObject,
|
|
|
|
QV4::ArrayData::IsFunctionObject,
|
|
|
|
QV4::ArrayData::IsErrorObject,
|
|
|
|
QV4::ArrayData::IsArrayData,
|
|
|
|
0,
|
|
|
|
QV4::ArrayData::MyType,
|
|
|
|
"ArrayData",
|
|
|
|
Q_VTABLE_FUNCTION(QV4::ArrayData, destroy),
|
|
|
|
0,
|
|
|
|
isEqualTo
|
|
|
|
};
|
|
|
|
|
2014-01-13 08:09:14 +00:00
|
|
|
const ArrayVTable SimpleArrayData::static_vtbl =
|
|
|
|
{
|
2014-06-13 14:04:39 +00:00
|
|
|
DEFINE_MANAGED_VTABLE_INT(SimpleArrayData, 0),
|
2014-01-13 08:09:14 +00:00
|
|
|
SimpleArrayData::Simple,
|
2014-01-24 13:29:40 +00:00
|
|
|
SimpleArrayData::reallocate,
|
2014-01-13 08:09:14 +00:00
|
|
|
SimpleArrayData::get,
|
|
|
|
SimpleArrayData::put,
|
|
|
|
SimpleArrayData::putArray,
|
|
|
|
SimpleArrayData::del,
|
|
|
|
SimpleArrayData::setAttribute,
|
|
|
|
SimpleArrayData::attribute,
|
|
|
|
SimpleArrayData::push_front,
|
|
|
|
SimpleArrayData::pop_front,
|
|
|
|
SimpleArrayData::truncate,
|
|
|
|
SimpleArrayData::length
|
2013-12-16 08:16:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const ArrayVTable SparseArrayData::static_vtbl =
|
|
|
|
{
|
2014-06-13 14:04:39 +00:00
|
|
|
DEFINE_MANAGED_VTABLE_INT(SparseArrayData, 0),
|
2013-12-16 08:16:57 +00:00
|
|
|
ArrayData::Sparse,
|
2014-01-24 13:29:40 +00:00
|
|
|
SparseArrayData::reallocate,
|
2013-12-16 08:16:57 +00:00
|
|
|
SparseArrayData::get,
|
|
|
|
SparseArrayData::put,
|
|
|
|
SparseArrayData::putArray,
|
|
|
|
SparseArrayData::del,
|
|
|
|
SparseArrayData::setAttribute,
|
|
|
|
SparseArrayData::attribute,
|
|
|
|
SparseArrayData::push_front,
|
|
|
|
SparseArrayData::pop_front,
|
2014-01-13 08:09:14 +00:00
|
|
|
SparseArrayData::truncate,
|
|
|
|
SparseArrayData::length
|
2013-12-16 08:16:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2014-01-24 13:29:40 +00:00
|
|
|
void ArrayData::realloc(Object *o, Type newType, uint offset, uint alloc, bool enforceAttributes)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-05 18:47:36 +00:00
|
|
|
ArrayData *d = o->arrayData();
|
2014-01-24 13:29:40 +00:00
|
|
|
|
|
|
|
uint oldAlloc = 0;
|
|
|
|
uint toCopy = 0;
|
|
|
|
if (alloc < 8)
|
|
|
|
alloc = 8;
|
|
|
|
|
|
|
|
if (d) {
|
2014-04-10 15:53:00 +00:00
|
|
|
bool hasAttrs = d->attrs();
|
2014-01-24 13:29:40 +00:00
|
|
|
enforceAttributes |= hasAttrs;
|
|
|
|
|
2014-04-10 15:53:00 +00:00
|
|
|
if (!offset && alloc <= d->alloc() && newType == d->type() && hasAttrs == enforceAttributes)
|
2014-01-24 13:29:40 +00:00
|
|
|
return;
|
|
|
|
|
2014-04-10 15:53:00 +00:00
|
|
|
oldAlloc = d->alloc();
|
|
|
|
if (d->type() < Sparse) {
|
|
|
|
offset = qMax(offset, static_cast<SimpleArrayData *>(d)->offset());
|
|
|
|
toCopy = static_cast<SimpleArrayData *>(d)->len();
|
2014-01-24 13:29:40 +00:00
|
|
|
} else {
|
|
|
|
Q_ASSERT(!offset);
|
2014-04-10 15:53:00 +00:00
|
|
|
toCopy = d->alloc();
|
2014-01-24 13:29:40 +00:00
|
|
|
newType = Sparse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (enforceAttributes && newType == Simple)
|
|
|
|
newType = Complex;
|
|
|
|
|
|
|
|
alloc = qMax(alloc, 2*oldAlloc) + offset;
|
2014-01-24 21:55:39 +00:00
|
|
|
size_t size = alloc*sizeof(Value);
|
2014-01-24 13:29:40 +00:00
|
|
|
if (enforceAttributes)
|
|
|
|
size += alloc*sizeof(PropertyAttributes);
|
|
|
|
|
|
|
|
if (newType < Sparse) {
|
2014-07-17 13:56:30 +00:00
|
|
|
size += sizeof(SimpleArrayData::Data);
|
2014-01-24 13:29:40 +00:00
|
|
|
SimpleArrayData *newData = static_cast<SimpleArrayData *>(o->engine()->memoryManager->allocManaged(size));
|
2014-06-13 14:19:25 +00:00
|
|
|
new (newData->d()) SimpleArrayData::Data(o->engine());
|
2014-04-10 15:53:00 +00:00
|
|
|
newData->setAlloc(alloc - offset);
|
|
|
|
newData->setType(newType);
|
2014-07-17 13:56:30 +00:00
|
|
|
newData->setArrayData(reinterpret_cast<Value *>(newData->d() + 1) + offset);
|
2014-04-10 15:53:00 +00:00
|
|
|
newData->setAttrs(enforceAttributes ? reinterpret_cast<PropertyAttributes *>(newData->arrayData() + alloc) + offset : 0);
|
|
|
|
newData->offset() = offset;
|
|
|
|
newData->len() = d ? static_cast<SimpleArrayData *>(d)->len() : 0;
|
2014-04-05 18:47:36 +00:00
|
|
|
o->setArrayData(newData);
|
2014-01-24 13:29:40 +00:00
|
|
|
} else {
|
2014-07-17 13:56:30 +00:00
|
|
|
size += sizeof(SparseArrayData::Data);
|
2014-01-24 13:29:40 +00:00
|
|
|
SparseArrayData *newData = static_cast<SparseArrayData *>(o->engine()->memoryManager->allocManaged(size));
|
2014-06-13 14:19:25 +00:00
|
|
|
new (newData->d()) SparseArrayData::Data(o->engine());
|
2014-04-10 15:53:00 +00:00
|
|
|
newData->setAlloc(alloc);
|
|
|
|
newData->setType(newType);
|
2014-07-17 13:56:30 +00:00
|
|
|
newData->setArrayData(reinterpret_cast<Value *>(newData->d() + 1));
|
2014-04-10 15:53:00 +00:00
|
|
|
newData->setAttrs(enforceAttributes ? reinterpret_cast<PropertyAttributes *>(newData->arrayData() + alloc) : 0);
|
2014-04-05 18:47:36 +00:00
|
|
|
o->setArrayData(newData);
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2014-01-24 13:29:40 +00:00
|
|
|
if (d) {
|
2014-04-10 15:53:00 +00:00
|
|
|
memcpy(o->arrayData()->arrayData(), d->arrayData(), sizeof(Value)*toCopy);
|
2014-01-24 13:29:40 +00:00
|
|
|
if (enforceAttributes) {
|
2014-04-10 15:53:00 +00:00
|
|
|
if (d->attrs())
|
|
|
|
memcpy(o->arrayData()->attrs(), d->attrs(), sizeof(PropertyAttributes)*toCopy);
|
2014-01-24 13:29:40 +00:00
|
|
|
else
|
|
|
|
for (uint i = 0; i < toCopy; ++i)
|
2014-04-10 15:53:00 +00:00
|
|
|
o->arrayData()->attrs()[i] = Attr_Data;
|
2014-01-24 13:29:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newType != Sparse)
|
2013-12-16 08:16:57 +00:00
|
|
|
return;
|
|
|
|
|
2014-04-05 18:47:36 +00:00
|
|
|
SparseArrayData *newData = static_cast<SparseArrayData *>(o->arrayData());
|
2014-04-10 15:53:00 +00:00
|
|
|
if (d && d->type() == Sparse) {
|
2014-01-24 13:29:40 +00:00
|
|
|
SparseArrayData *old = static_cast<SparseArrayData *>(d);
|
2014-04-10 15:53:00 +00:00
|
|
|
newData->setSparse(old->sparse());
|
|
|
|
old->setSparse(0);
|
|
|
|
newData->freeList() = old->freeList();
|
2014-03-28 13:12:41 +00:00
|
|
|
} else {
|
2014-04-10 15:53:00 +00:00
|
|
|
newData->setSparse(new SparseArray);
|
|
|
|
uint *lastFree = &newData->freeList();
|
2014-03-28 13:12:41 +00:00
|
|
|
for (uint i = 0; i < toCopy; ++i) {
|
2014-04-10 15:53:00 +00:00
|
|
|
if (!newData->arrayData()[i].isEmpty()) {
|
|
|
|
SparseArrayNode *n = newData->sparse()->insert(i);
|
2014-03-28 13:12:41 +00:00
|
|
|
n->value = i;
|
|
|
|
} else {
|
|
|
|
*lastFree = i;
|
2014-04-10 15:53:00 +00:00
|
|
|
newData->arrayData()[i].tag = Value::Empty_Type;
|
|
|
|
lastFree = &newData->arrayData()[i].uint_32;
|
2014-03-28 13:12:41 +00:00
|
|
|
}
|
|
|
|
}
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2014-04-10 15:53:00 +00:00
|
|
|
uint *lastFree = &newData->freeList();
|
|
|
|
for (uint i = toCopy; i < newData->alloc(); ++i) {
|
2014-01-24 13:29:40 +00:00
|
|
|
*lastFree = i;
|
2014-04-10 15:53:00 +00:00
|
|
|
newData->arrayData()[i].tag = Value::Empty_Type;
|
|
|
|
lastFree = &newData->arrayData()[i].uint_32;
|
2014-01-24 13:29:40 +00:00
|
|
|
}
|
2014-04-10 15:53:00 +00:00
|
|
|
*lastFree = newData->alloc();
|
2014-01-24 13:29:40 +00:00
|
|
|
|
|
|
|
// ### Could explicitly free the old data
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SimpleArrayData::getHeadRoom(Object *o)
|
|
|
|
{
|
2014-04-05 18:47:36 +00:00
|
|
|
SimpleArrayData *dd = static_cast<SimpleArrayData *>(o->arrayData());
|
2014-01-24 13:29:40 +00:00
|
|
|
Q_ASSERT(dd);
|
2014-04-10 15:53:00 +00:00
|
|
|
Q_ASSERT(!dd->offset());
|
|
|
|
uint offset = qMax(dd->len() >> 2, (uint)16);
|
2014-01-24 13:29:40 +00:00
|
|
|
realloc(o, Simple, offset, 0, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
ArrayData *SimpleArrayData::reallocate(Object *o, uint n, bool enforceAttributes)
|
|
|
|
{
|
|
|
|
realloc(o, Simple, 0, n, enforceAttributes);
|
2014-04-05 18:47:36 +00:00
|
|
|
return o->arrayData();
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2014-01-24 13:29:40 +00:00
|
|
|
void ArrayData::ensureAttributes(Object *o)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-10 15:53:00 +00:00
|
|
|
if (o->arrayData() && o->arrayData()->attrs())
|
2013-12-16 08:16:57 +00:00
|
|
|
return;
|
|
|
|
|
2014-01-24 13:29:40 +00:00
|
|
|
ArrayData::realloc(o, Simple, 0, 0, true);
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-01-20 15:01:26 +00:00
|
|
|
void SimpleArrayData::markObjects(Managed *d, ExecutionEngine *e)
|
2014-01-20 11:15:26 +00:00
|
|
|
{
|
2014-01-20 15:01:26 +00:00
|
|
|
SimpleArrayData *dd = static_cast<SimpleArrayData *>(d);
|
2014-04-10 15:53:00 +00:00
|
|
|
uint l = dd->len();
|
2014-01-20 11:15:26 +00:00
|
|
|
for (uint i = 0; i < l; ++i)
|
2014-04-10 15:53:00 +00:00
|
|
|
dd->arrayData()[i].mark(e);
|
2014-01-20 11:15:26 +00:00
|
|
|
}
|
|
|
|
|
2014-01-13 08:09:14 +00:00
|
|
|
ReturnedValue SimpleArrayData::get(const ArrayData *d, uint index)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-01-13 08:09:14 +00:00
|
|
|
const SimpleArrayData *dd = static_cast<const SimpleArrayData *>(d);
|
2014-04-10 15:53:00 +00:00
|
|
|
if (index >= dd->len())
|
2013-12-16 08:16:57 +00:00
|
|
|
return Primitive::emptyValue().asReturnedValue();
|
2014-04-10 15:53:00 +00:00
|
|
|
return dd->arrayData()[index].asReturnedValue();
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2014-01-22 14:25:50 +00:00
|
|
|
bool SimpleArrayData::put(Object *o, uint index, ValueRef value)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-05 18:47:36 +00:00
|
|
|
SimpleArrayData *dd = static_cast<SimpleArrayData *>(o->arrayData());
|
2014-04-10 15:53:00 +00:00
|
|
|
Q_ASSERT(index >= dd->len() || !dd->attrs() || !dd->attrs()[index].isAccessor());
|
2013-12-16 08:16:57 +00:00
|
|
|
// ### honour attributes
|
2014-04-10 15:53:00 +00:00
|
|
|
dd->arrayData()[index] = value;
|
|
|
|
if (index >= dd->len()) {
|
|
|
|
if (dd->attrs())
|
|
|
|
dd->attrs()[index] = Attr_Data;
|
|
|
|
dd->len() = index + 1;
|
2014-01-09 10:05:08 +00:00
|
|
|
}
|
2013-12-16 08:16:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-01-22 14:25:50 +00:00
|
|
|
bool SimpleArrayData::del(Object *o, uint index)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-05 18:47:36 +00:00
|
|
|
SimpleArrayData *dd = static_cast<SimpleArrayData *>(o->arrayData());
|
2014-04-10 15:53:00 +00:00
|
|
|
if (index >= dd->len())
|
2013-12-16 08:16:57 +00:00
|
|
|
return true;
|
|
|
|
|
2014-04-10 15:53:00 +00:00
|
|
|
if (!dd->attrs() || dd->attrs()[index].isConfigurable()) {
|
|
|
|
dd->arrayData()[index] = Primitive::emptyValue();
|
|
|
|
if (dd->attrs())
|
|
|
|
dd->attrs()[index] = Attr_Data;
|
2013-12-16 08:16:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
2014-04-10 15:53:00 +00:00
|
|
|
if (dd->arrayData()[index].isEmpty())
|
2013-12-16 08:16:57 +00:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-01-22 14:25:50 +00:00
|
|
|
void SimpleArrayData::setAttribute(Object *o, uint index, PropertyAttributes attrs)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-10 15:53:00 +00:00
|
|
|
o->arrayData()->attrs()[index] = attrs;
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2014-01-13 08:09:14 +00:00
|
|
|
PropertyAttributes SimpleArrayData::attribute(const ArrayData *d, uint index)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-10 15:53:00 +00:00
|
|
|
return d->attrs()[index];
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2014-01-24 21:55:39 +00:00
|
|
|
void SimpleArrayData::push_front(Object *o, Value *values, uint n)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-05 18:47:36 +00:00
|
|
|
SimpleArrayData *dd = static_cast<SimpleArrayData *>(o->arrayData());
|
2014-04-10 15:53:00 +00:00
|
|
|
Q_ASSERT(!dd->attrs());
|
2013-12-16 08:16:57 +00:00
|
|
|
for (int i = n - 1; i >= 0; --i) {
|
2014-04-10 15:53:00 +00:00
|
|
|
if (!dd->offset()) {
|
2014-01-24 13:29:40 +00:00
|
|
|
getHeadRoom(o);
|
2014-04-05 18:47:36 +00:00
|
|
|
dd = static_cast<SimpleArrayData *>(o->arrayData());
|
2014-01-24 13:29:40 +00:00
|
|
|
}
|
|
|
|
|
2014-01-13 08:09:14 +00:00
|
|
|
|
2014-04-10 15:53:00 +00:00
|
|
|
--dd->offset();
|
|
|
|
--dd->arrayData();
|
|
|
|
++dd->len();
|
|
|
|
++dd->alloc();
|
|
|
|
*dd->arrayData() = values[i].asReturnedValue();
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-01-22 14:25:50 +00:00
|
|
|
ReturnedValue SimpleArrayData::pop_front(Object *o)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-05 18:47:36 +00:00
|
|
|
SimpleArrayData *dd = static_cast<SimpleArrayData *>(o->arrayData());
|
2014-04-10 15:53:00 +00:00
|
|
|
Q_ASSERT(!dd->attrs());
|
|
|
|
if (!dd->len())
|
2013-12-16 08:16:57 +00:00
|
|
|
return Encode::undefined();
|
|
|
|
|
2014-04-10 15:53:00 +00:00
|
|
|
ReturnedValue v = dd->arrayData()[0].isEmpty() ? Encode::undefined() : dd->arrayData()[0].asReturnedValue();
|
|
|
|
++dd->offset();
|
|
|
|
++dd->arrayData();
|
|
|
|
--dd->len();
|
|
|
|
--dd->alloc();
|
2013-12-16 08:16:57 +00:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2014-01-22 14:25:50 +00:00
|
|
|
uint SimpleArrayData::truncate(Object *o, uint newLen)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-05 18:47:36 +00:00
|
|
|
SimpleArrayData *dd = static_cast<SimpleArrayData *>(o->arrayData());
|
2014-04-10 15:53:00 +00:00
|
|
|
if (dd->len() < newLen)
|
2014-01-13 08:09:14 +00:00
|
|
|
return newLen;
|
|
|
|
|
2014-04-10 15:53:00 +00:00
|
|
|
if (dd->attrs()) {
|
|
|
|
Value *it = dd->arrayData() + dd->len();
|
|
|
|
const Value *begin = dd->arrayData() + newLen;
|
2013-12-16 08:16:57 +00:00
|
|
|
while (--it >= begin) {
|
2014-04-10 15:53:00 +00:00
|
|
|
if (!it->isEmpty() && !dd->attrs()[it - dd->arrayData()].isConfigurable()) {
|
|
|
|
newLen = it - dd->arrayData() + 1;
|
2013-12-16 08:16:57 +00:00
|
|
|
break;
|
|
|
|
}
|
2014-01-09 10:05:08 +00:00
|
|
|
*it = Primitive::emptyValue();
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
}
|
2014-04-10 15:53:00 +00:00
|
|
|
dd->len() = newLen;
|
2013-12-16 08:16:57 +00:00
|
|
|
return newLen;
|
|
|
|
}
|
|
|
|
|
2014-01-13 08:09:14 +00:00
|
|
|
uint SimpleArrayData::length(const ArrayData *d)
|
|
|
|
{
|
2014-04-10 15:53:00 +00:00
|
|
|
return static_cast<const SimpleArrayData *>(d)->len();
|
2014-01-13 08:09:14 +00:00
|
|
|
}
|
|
|
|
|
2014-01-24 21:55:39 +00:00
|
|
|
bool SimpleArrayData::putArray(Object *o, uint index, Value *values, uint n)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-05 18:47:36 +00:00
|
|
|
SimpleArrayData *dd = static_cast<SimpleArrayData *>(o->arrayData());
|
2014-04-10 15:53:00 +00:00
|
|
|
if (index + n > dd->alloc()) {
|
2014-01-24 13:29:40 +00:00
|
|
|
reallocate(o, index + n + 1, false);
|
2014-04-05 18:47:36 +00:00
|
|
|
dd = static_cast<SimpleArrayData *>(o->arrayData());
|
2014-01-24 13:29:40 +00:00
|
|
|
}
|
2014-04-10 15:53:00 +00:00
|
|
|
for (uint i = dd->len(); i < index; ++i)
|
|
|
|
dd->arrayData()[i] = Primitive::emptyValue();
|
2013-12-16 08:16:57 +00:00
|
|
|
for (uint i = 0; i < n; ++i)
|
2014-04-10 15:53:00 +00:00
|
|
|
dd->arrayData()[index + i] = values[i];
|
|
|
|
dd->len() = qMax(dd->len(), index + n);
|
2013-12-16 08:16:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SparseArrayData::free(ArrayData *d, uint idx)
|
|
|
|
{
|
2014-04-10 15:53:00 +00:00
|
|
|
Q_ASSERT(d && d->type() == ArrayData::Sparse);
|
2013-12-16 08:16:57 +00:00
|
|
|
SparseArrayData *dd = static_cast<SparseArrayData *>(d);
|
2014-04-10 15:53:00 +00:00
|
|
|
Value *v = dd->arrayData() + idx;
|
|
|
|
if (dd->attrs() && dd->attrs()[idx].isAccessor()) {
|
2014-01-09 10:05:08 +00:00
|
|
|
// double slot, free both. Order is important, so we have a double slot for allocation again afterwards.
|
|
|
|
v[1].tag = Value::Empty_Type;
|
2014-04-10 15:53:00 +00:00
|
|
|
v[1].uint_32 = dd->freeList();
|
2014-01-09 10:05:08 +00:00
|
|
|
v[0].tag = Value::Empty_Type;
|
|
|
|
v[0].uint_32 = idx + 1;
|
|
|
|
} else {
|
|
|
|
v->tag = Value::Empty_Type;
|
2014-04-10 15:53:00 +00:00
|
|
|
v->uint_32 = dd->freeList();
|
2014-01-09 10:05:08 +00:00
|
|
|
}
|
2014-04-10 15:53:00 +00:00
|
|
|
dd->freeList() = idx;
|
|
|
|
if (dd->attrs())
|
|
|
|
dd->attrs()[idx].clear();
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-01-20 15:01:26 +00:00
|
|
|
void SparseArrayData::destroy(Managed *d)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-01-20 15:01:26 +00:00
|
|
|
SparseArrayData *dd = static_cast<SparseArrayData *>(d);
|
2014-04-10 15:53:00 +00:00
|
|
|
delete dd->sparse();
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2014-01-20 15:01:26 +00:00
|
|
|
void SparseArrayData::markObjects(Managed *d, ExecutionEngine *e)
|
2014-01-20 11:15:26 +00:00
|
|
|
{
|
2014-01-20 15:01:26 +00:00
|
|
|
SparseArrayData *dd = static_cast<SparseArrayData *>(d);
|
2014-04-10 15:53:00 +00:00
|
|
|
uint l = dd->alloc();
|
2014-01-20 11:15:26 +00:00
|
|
|
for (uint i = 0; i < l; ++i)
|
2014-04-10 15:53:00 +00:00
|
|
|
dd->arrayData()[i].mark(e);
|
2014-01-20 11:15:26 +00:00
|
|
|
}
|
|
|
|
|
2014-01-24 13:29:40 +00:00
|
|
|
ArrayData *SparseArrayData::reallocate(Object *o, uint n, bool enforceAttributes)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-01-24 13:29:40 +00:00
|
|
|
realloc(o, Sparse, 0, n, enforceAttributes);
|
2014-04-05 18:47:36 +00:00
|
|
|
return o->arrayData();
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2014-01-09 10:05:08 +00:00
|
|
|
// double slots are required for accessor properties
|
2014-01-24 13:29:40 +00:00
|
|
|
uint SparseArrayData::allocate(Object *o, bool doubleSlot)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-10 15:53:00 +00:00
|
|
|
Q_ASSERT(o->arrayData()->type() == ArrayData::Sparse);
|
2014-04-05 18:47:36 +00:00
|
|
|
SparseArrayData *dd = static_cast<SparseArrayData *>(o->arrayData());
|
2014-01-09 10:05:08 +00:00
|
|
|
if (doubleSlot) {
|
2014-04-10 15:53:00 +00:00
|
|
|
uint *last = &dd->freeList();
|
2014-01-09 10:05:08 +00:00
|
|
|
while (1) {
|
2014-04-10 15:53:00 +00:00
|
|
|
if (*last + 1 >= dd->alloc()) {
|
|
|
|
reallocate(o, o->arrayData()->alloc() + 2, true);
|
2014-04-05 18:47:36 +00:00
|
|
|
dd = static_cast<SparseArrayData *>(o->arrayData());
|
2014-04-10 15:53:00 +00:00
|
|
|
last = &dd->freeList();
|
2014-01-09 10:05:08 +00:00
|
|
|
}
|
|
|
|
|
2014-04-10 15:53:00 +00:00
|
|
|
if (dd->arrayData()[*last].uint_32 == (*last + 1)) {
|
2014-01-09 10:05:08 +00:00
|
|
|
// found two slots in a row
|
|
|
|
uint idx = *last;
|
2014-04-10 15:53:00 +00:00
|
|
|
*last = dd->arrayData()[*last + 1].uint_32;
|
|
|
|
o->arrayData()->attrs()[idx] = Attr_Accessor;
|
2014-01-09 10:05:08 +00:00
|
|
|
return idx;
|
|
|
|
}
|
2014-04-10 15:53:00 +00:00
|
|
|
last = &dd->arrayData()[*last].uint_32;
|
2014-01-09 10:05:08 +00:00
|
|
|
}
|
|
|
|
} else {
|
2014-04-10 15:53:00 +00:00
|
|
|
if (dd->alloc() == dd->freeList()) {
|
|
|
|
reallocate(o, o->arrayData()->alloc() + 2, false);
|
2014-04-05 18:47:36 +00:00
|
|
|
dd = static_cast<SparseArrayData *>(o->arrayData());
|
2014-01-24 13:29:40 +00:00
|
|
|
}
|
2014-04-10 15:53:00 +00:00
|
|
|
uint idx = dd->freeList();
|
|
|
|
dd->freeList() = dd->arrayData()[idx].uint_32;
|
|
|
|
if (dd->attrs())
|
|
|
|
dd->attrs()[idx] = Attr_Data;
|
2014-01-09 10:05:08 +00:00
|
|
|
return idx;
|
|
|
|
}
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ReturnedValue SparseArrayData::get(const ArrayData *d, uint index)
|
|
|
|
{
|
2014-04-10 15:53:00 +00:00
|
|
|
SparseArrayNode *n = static_cast<const SparseArrayData *>(d)->sparse()->findNode(index);
|
2013-12-16 08:16:57 +00:00
|
|
|
if (!n)
|
|
|
|
return Primitive::emptyValue().asReturnedValue();
|
2014-04-10 15:53:00 +00:00
|
|
|
return d->arrayData()[n->value].asReturnedValue();
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2014-01-22 14:25:50 +00:00
|
|
|
bool SparseArrayData::put(Object *o, uint index, ValueRef value)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-01-09 10:05:08 +00:00
|
|
|
if (value->isEmpty())
|
|
|
|
return true;
|
|
|
|
|
2014-04-10 15:53:00 +00:00
|
|
|
SparseArrayNode *n = static_cast<SparseArrayData *>(o->arrayData())->sparse()->insert(index);
|
|
|
|
Q_ASSERT(n->value == UINT_MAX || !o->arrayData()->attrs() || !o->arrayData()->attrs()[n->value].isAccessor());
|
2013-12-16 08:16:57 +00:00
|
|
|
if (n->value == UINT_MAX)
|
2014-01-24 13:29:40 +00:00
|
|
|
n->value = allocate(o);
|
2014-04-10 15:53:00 +00:00
|
|
|
o->arrayData()->arrayData()[n->value] = value;
|
|
|
|
if (o->arrayData()->attrs())
|
|
|
|
o->arrayData()->attrs()[n->value] = Attr_Data;
|
2013-12-16 08:16:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-01-22 14:25:50 +00:00
|
|
|
bool SparseArrayData::del(Object *o, uint index)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-05 18:47:36 +00:00
|
|
|
SparseArrayData *dd = static_cast<SparseArrayData *>(o->arrayData());
|
2014-04-10 15:53:00 +00:00
|
|
|
SparseArrayNode *n = dd->sparse()->findNode(index);
|
2013-12-16 08:16:57 +00:00
|
|
|
if (!n)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
uint pidx = n->value;
|
2014-04-10 15:53:00 +00:00
|
|
|
Q_ASSERT(!dd->arrayData()[pidx].isEmpty());
|
2013-12-16 08:16:57 +00:00
|
|
|
|
2014-01-09 10:05:08 +00:00
|
|
|
bool isAccessor = false;
|
2014-04-10 15:53:00 +00:00
|
|
|
if (dd->attrs()) {
|
|
|
|
if (!dd->attrs()[pidx].isConfigurable())
|
2014-01-09 10:05:08 +00:00
|
|
|
return false;
|
|
|
|
|
2014-04-10 15:53:00 +00:00
|
|
|
isAccessor = dd->attrs()[pidx].isAccessor();
|
|
|
|
dd->attrs()[pidx] = Attr_Data;
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
2014-01-09 10:05:08 +00:00
|
|
|
|
|
|
|
if (isAccessor) {
|
|
|
|
// free up both indices
|
2014-04-10 15:53:00 +00:00
|
|
|
dd->arrayData()[pidx + 1].tag = Value::Undefined_Type;
|
|
|
|
dd->arrayData()[pidx + 1].uint_32 = static_cast<SparseArrayData *>(dd)->freeList();
|
|
|
|
dd->arrayData()[pidx].tag = Value::Undefined_Type;
|
|
|
|
dd->arrayData()[pidx].uint_32 = pidx + 1;
|
2014-01-09 10:05:08 +00:00
|
|
|
} else {
|
2014-04-10 15:53:00 +00:00
|
|
|
dd->arrayData()[pidx].tag = Value::Undefined_Type;
|
|
|
|
dd->arrayData()[pidx].uint_32 = static_cast<SparseArrayData *>(dd)->freeList();
|
2014-01-09 10:05:08 +00:00
|
|
|
}
|
|
|
|
|
2014-04-10 15:53:00 +00:00
|
|
|
dd->freeList() = pidx;
|
|
|
|
dd->sparse()->erase(n);
|
2014-01-09 10:05:08 +00:00
|
|
|
return true;
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2014-01-22 14:25:50 +00:00
|
|
|
void SparseArrayData::setAttribute(Object *o, uint index, PropertyAttributes attrs)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-05 18:47:36 +00:00
|
|
|
SparseArrayData *d = static_cast<SparseArrayData *>(o->arrayData());
|
2014-04-10 15:53:00 +00:00
|
|
|
SparseArrayNode *n = d->sparse()->insert(index);
|
2014-01-24 13:29:40 +00:00
|
|
|
if (n->value == UINT_MAX) {
|
|
|
|
n->value = allocate(o, attrs.isAccessor());
|
2014-04-05 18:47:36 +00:00
|
|
|
d = static_cast<SparseArrayData *>(o->arrayData());
|
2014-01-24 13:29:40 +00:00
|
|
|
}
|
2014-04-10 15:53:00 +00:00
|
|
|
else if (attrs.isAccessor() != d->attrs()[n->value].isAccessor()) {
|
2014-01-09 10:05:08 +00:00
|
|
|
// need to convert the slot
|
|
|
|
free(d, n->value);
|
2014-01-24 13:29:40 +00:00
|
|
|
n->value = allocate(o, attrs.isAccessor());
|
2014-01-09 10:05:08 +00:00
|
|
|
}
|
2014-04-10 15:53:00 +00:00
|
|
|
o->arrayData()->attrs()[n->value] = attrs;
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PropertyAttributes SparseArrayData::attribute(const ArrayData *d, uint index)
|
|
|
|
{
|
2014-04-10 15:53:00 +00:00
|
|
|
SparseArrayNode *n = static_cast<const SparseArrayData *>(d)->sparse()->insert(index);
|
2013-12-16 08:16:57 +00:00
|
|
|
if (!n)
|
|
|
|
return PropertyAttributes();
|
2014-04-10 15:53:00 +00:00
|
|
|
return d->attrs()[n->value];
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2014-01-24 21:55:39 +00:00
|
|
|
void SparseArrayData::push_front(Object *o, Value *values, uint n)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-10 15:53:00 +00:00
|
|
|
Q_ASSERT(!o->arrayData()->attrs());
|
2013-12-16 08:16:57 +00:00
|
|
|
for (int i = n - 1; i >= 0; --i) {
|
2014-01-24 13:29:40 +00:00
|
|
|
uint idx = allocate(o);
|
2014-04-10 15:53:00 +00:00
|
|
|
o->arrayData()->arrayData()[idx] = values[i];
|
|
|
|
static_cast<SparseArrayData *>(o->arrayData())->sparse()->push_front(idx);
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-22 14:25:50 +00:00
|
|
|
ReturnedValue SparseArrayData::pop_front(Object *o)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-10 15:53:00 +00:00
|
|
|
Q_ASSERT(!o->arrayData()->attrs());
|
|
|
|
uint idx = static_cast<SparseArrayData *>(o->arrayData())->sparse()->pop_front();
|
2013-12-16 08:16:57 +00:00
|
|
|
ReturnedValue v;
|
|
|
|
if (idx != UINT_MAX) {
|
2014-04-10 15:53:00 +00:00
|
|
|
v = o->arrayData()->arrayData()[idx].asReturnedValue();
|
2014-04-05 18:47:36 +00:00
|
|
|
free(o->arrayData(), idx);
|
2013-12-16 08:16:57 +00:00
|
|
|
} else {
|
|
|
|
v = Encode::undefined();
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2014-01-22 14:25:50 +00:00
|
|
|
uint SparseArrayData::truncate(Object *o, uint newLen)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-05 18:47:36 +00:00
|
|
|
SparseArrayData *d = static_cast<SparseArrayData *>(o->arrayData());
|
2014-04-10 15:53:00 +00:00
|
|
|
SparseArrayNode *begin = d->sparse()->lowerBound(newLen);
|
|
|
|
if (begin != d->sparse()->end()) {
|
|
|
|
SparseArrayNode *it = d->sparse()->end()->previousNode();
|
2013-12-16 08:16:57 +00:00
|
|
|
while (1) {
|
2014-04-10 15:53:00 +00:00
|
|
|
if (d->attrs()) {
|
|
|
|
if (!d->attrs()[it->value].isConfigurable()) {
|
2013-12-16 08:16:57 +00:00
|
|
|
newLen = it->key() + 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-01-09 10:05:08 +00:00
|
|
|
free(d, it->value);
|
2013-12-16 08:16:57 +00:00
|
|
|
bool brk = (it == begin);
|
|
|
|
SparseArrayNode *prev = it->previousNode();
|
2014-04-10 15:53:00 +00:00
|
|
|
static_cast<SparseArrayData *>(d)->sparse()->erase(it);
|
2013-12-16 08:16:57 +00:00
|
|
|
if (brk)
|
|
|
|
break;
|
|
|
|
it = prev;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newLen;
|
|
|
|
}
|
|
|
|
|
2014-01-13 08:09:14 +00:00
|
|
|
uint SparseArrayData::length(const ArrayData *d)
|
|
|
|
{
|
|
|
|
const SparseArrayData *dd = static_cast<const SparseArrayData *>(d);
|
2014-04-10 15:53:00 +00:00
|
|
|
if (!dd->sparse())
|
2014-01-13 08:09:14 +00:00
|
|
|
return 0;
|
2014-04-10 15:53:00 +00:00
|
|
|
SparseArrayNode *n = dd->sparse()->end();
|
2014-01-13 08:09:14 +00:00
|
|
|
n = n->previousNode();
|
|
|
|
return n ? n->key() + 1 : 0;
|
|
|
|
}
|
|
|
|
|
2014-01-24 21:55:39 +00:00
|
|
|
bool SparseArrayData::putArray(Object *o, uint index, Value *values, uint n)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
|
|
|
for (uint i = 0; i < n; ++i)
|
2014-01-22 14:25:50 +00:00
|
|
|
put(o, index + i, values[i]);
|
2013-12-16 08:16:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-01-13 08:09:14 +00:00
|
|
|
uint ArrayData::append(Object *obj, const ArrayObject *otherObj, uint n)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-05 18:47:36 +00:00
|
|
|
Q_ASSERT(!obj->arrayData()->hasAttributes());
|
2014-01-13 08:09:14 +00:00
|
|
|
|
2013-12-16 08:16:57 +00:00
|
|
|
if (!n)
|
2014-01-13 08:09:14 +00:00
|
|
|
return obj->getLength();
|
2013-12-16 08:16:57 +00:00
|
|
|
|
2014-04-05 18:47:36 +00:00
|
|
|
const ArrayData *other = otherObj->arrayData();
|
2013-12-16 08:16:57 +00:00
|
|
|
|
2014-01-13 08:09:14 +00:00
|
|
|
if (other->isSparse())
|
|
|
|
obj->initSparseArray();
|
2014-03-14 15:00:36 +00:00
|
|
|
else
|
|
|
|
obj->arrayCreate();
|
2013-12-16 08:16:57 +00:00
|
|
|
|
2014-01-13 08:09:14 +00:00
|
|
|
uint oldSize = obj->getLength();
|
|
|
|
|
|
|
|
if (other->isSparse()) {
|
2014-04-05 18:23:20 +00:00
|
|
|
if (otherObj->hasAccessorProperty() && other->hasAttributes()) {
|
2014-01-13 08:09:14 +00:00
|
|
|
Scope scope(obj->engine());
|
|
|
|
ScopedValue v(scope);
|
2014-04-10 15:53:00 +00:00
|
|
|
for (const SparseArrayNode *it = static_cast<const SparseArrayData *>(other)->sparse()->begin();
|
|
|
|
it != static_cast<const SparseArrayData *>(other)->sparse()->end(); it = it->nextNode()) {
|
|
|
|
v = otherObj->getValue(reinterpret_cast<Property *>(other->arrayData() + it->value), other->attrs()[it->value]);
|
2014-01-13 08:09:14 +00:00
|
|
|
obj->arraySet(oldSize + it->key(), v);
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
} else {
|
2014-04-10 15:53:00 +00:00
|
|
|
for (const SparseArrayNode *it = static_cast<const SparseArrayData *>(other)->sparse()->begin();
|
|
|
|
it != static_cast<const SparseArrayData *>(other)->sparse()->end(); it = it->nextNode())
|
|
|
|
obj->arraySet(oldSize + it->key(), ValueRef(other->arrayData()[it->value]));
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
2014-01-13 08:09:14 +00:00
|
|
|
} else {
|
2014-04-10 15:53:00 +00:00
|
|
|
obj->arrayPut(oldSize, other->arrayData(), n);
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return oldSize + n;
|
|
|
|
}
|
|
|
|
|
2014-01-09 10:05:08 +00:00
|
|
|
Property *ArrayData::insert(Object *o, uint index, bool isAccessor)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-04-10 15:53:00 +00:00
|
|
|
if (!isAccessor && o->arrayData()->type() != ArrayData::Sparse) {
|
2014-04-05 18:47:36 +00:00
|
|
|
SimpleArrayData *d = static_cast<SimpleArrayData *>(o->arrayData());
|
2014-04-10 15:53:00 +00:00
|
|
|
if (index < 0x1000 || index < d->len() + (d->len() >> 2)) {
|
|
|
|
if (index >= o->arrayData()->alloc()) {
|
2014-01-13 08:09:14 +00:00
|
|
|
o->arrayReserve(index + 1);
|
2014-04-05 18:47:36 +00:00
|
|
|
d = static_cast<SimpleArrayData *>(o->arrayData());
|
2014-01-13 08:09:14 +00:00
|
|
|
}
|
2014-04-10 15:53:00 +00:00
|
|
|
if (index >= d->len()) {
|
2014-01-13 08:09:14 +00:00
|
|
|
// mark possible hole in the array
|
2014-04-10 15:53:00 +00:00
|
|
|
for (uint i = d->len(); i < index; ++i)
|
|
|
|
d->arrayData()[i] = Primitive::emptyValue();
|
|
|
|
d->len() = index + 1;
|
2014-01-13 08:09:14 +00:00
|
|
|
}
|
2014-04-10 15:53:00 +00:00
|
|
|
return reinterpret_cast<Property *>(o->arrayData()->arrayData() + index);
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
}
|
2014-01-13 08:09:14 +00:00
|
|
|
|
|
|
|
o->initSparseArray();
|
2014-04-10 15:53:00 +00:00
|
|
|
SparseArrayNode *n = static_cast<SparseArrayData *>(o->arrayData())->sparse()->insert(index);
|
2014-01-13 08:09:14 +00:00
|
|
|
if (n->value == UINT_MAX)
|
2014-01-24 13:29:40 +00:00
|
|
|
n->value = SparseArrayData::allocate(o, isAccessor);
|
2014-04-10 15:53:00 +00:00
|
|
|
return reinterpret_cast<Property *>(o->arrayData()->arrayData() + n->value);
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2014-01-24 21:11:16 +00:00
|
|
|
|
|
|
|
class ArrayElementLessThan
|
|
|
|
{
|
|
|
|
public:
|
2014-05-07 14:14:08 +00:00
|
|
|
inline ArrayElementLessThan(ExecutionContext *context, Object *thisObject, const ValueRef comparefn)
|
2014-01-24 21:11:16 +00:00
|
|
|
: m_context(context), thisObject(thisObject), m_comparefn(comparefn) {}
|
|
|
|
|
2014-01-24 21:55:39 +00:00
|
|
|
bool operator()(const Value &v1, const Value &v2) const;
|
2014-01-24 21:11:16 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
ExecutionContext *m_context;
|
2014-05-07 14:14:08 +00:00
|
|
|
Object *thisObject;
|
2014-01-24 21:11:16 +00:00
|
|
|
const ValueRef m_comparefn;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2014-01-24 21:55:39 +00:00
|
|
|
bool ArrayElementLessThan::operator()(const Value &v1, const Value &v2) const
|
2014-01-24 21:11:16 +00:00
|
|
|
{
|
|
|
|
Scope scope(m_context);
|
|
|
|
|
|
|
|
if (v1.isUndefined() || v1.isEmpty())
|
|
|
|
return false;
|
|
|
|
if (v2.isUndefined() || v2.isEmpty())
|
|
|
|
return true;
|
|
|
|
ScopedObject o(scope, m_comparefn);
|
|
|
|
if (o) {
|
|
|
|
Scope scope(o->engine());
|
|
|
|
ScopedValue result(scope);
|
|
|
|
ScopedCallData callData(scope, 2);
|
|
|
|
callData->thisObject = Primitive::undefinedValue();
|
|
|
|
callData->args[0] = v1;
|
|
|
|
callData->args[1] = v2;
|
2014-03-10 18:58:05 +00:00
|
|
|
result = Runtime::callValue(m_context, m_comparefn, callData);
|
2014-01-24 21:11:16 +00:00
|
|
|
|
|
|
|
return result->toNumber() < 0;
|
|
|
|
}
|
|
|
|
ScopedString p1s(scope, v1.toString(m_context));
|
|
|
|
ScopedString p2s(scope, v2.toString(m_context));
|
|
|
|
return p1s->toQString() < p2s->toQString();
|
|
|
|
}
|
|
|
|
|
2014-05-07 14:14:08 +00:00
|
|
|
void ArrayData::sort(ExecutionContext *context, Object *thisObject, const ValueRef comparefn, uint len)
|
2013-12-16 08:16:57 +00:00
|
|
|
{
|
2014-01-09 10:05:08 +00:00
|
|
|
if (!len)
|
|
|
|
return;
|
|
|
|
|
2014-04-05 18:47:36 +00:00
|
|
|
if (!thisObject->arrayData()->length())
|
2013-12-16 08:16:57 +00:00
|
|
|
return;
|
|
|
|
|
2014-01-09 10:05:08 +00:00
|
|
|
if (!(comparefn->isUndefined() || comparefn->asObject())) {
|
|
|
|
context->throwTypeError();
|
2013-12-16 08:16:57 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2014-01-09 10:05:08 +00:00
|
|
|
|
2014-04-10 15:53:00 +00:00
|
|
|
if (thisObject->arrayData()->type() == ArrayData::Sparse) {
|
2014-01-09 10:05:08 +00:00
|
|
|
// since we sort anyway, we can simply iterate over the entries in the sparse
|
|
|
|
// array and append them one by one to a regular one.
|
2014-04-05 18:47:36 +00:00
|
|
|
SparseArrayData *sparse = static_cast<SparseArrayData *>(thisObject->arrayData());
|
2014-01-09 10:05:08 +00:00
|
|
|
|
2014-04-10 15:53:00 +00:00
|
|
|
if (!sparse->sparse()->nEntries())
|
2014-01-09 10:05:08 +00:00
|
|
|
return;
|
|
|
|
|
2014-04-05 18:47:36 +00:00
|
|
|
thisObject->setArrayData(0);
|
2014-04-10 15:53:00 +00:00
|
|
|
ArrayData::realloc(thisObject, ArrayData::Simple, 0, sparse->sparse()->nEntries(), sparse->attrs() ? true : false);
|
2014-04-05 18:47:36 +00:00
|
|
|
SimpleArrayData *d = static_cast<SimpleArrayData *>(thisObject->arrayData());
|
2014-01-09 10:05:08 +00:00
|
|
|
|
2014-04-10 15:53:00 +00:00
|
|
|
SparseArrayNode *n = sparse->sparse()->begin();
|
2014-01-09 10:05:08 +00:00
|
|
|
uint i = 0;
|
2014-04-10 15:53:00 +00:00
|
|
|
if (sparse->attrs()) {
|
|
|
|
while (n != sparse->sparse()->end()) {
|
2014-01-09 10:05:08 +00:00
|
|
|
if (n->value >= len)
|
|
|
|
break;
|
|
|
|
|
2014-04-10 15:53:00 +00:00
|
|
|
PropertyAttributes a = sparse->attrs() ? sparse->attrs()[n->value] : Attr_Data;
|
|
|
|
d->arrayData()[i] = thisObject->getValue(reinterpret_cast<Property *>(sparse->arrayData() + n->value), a);
|
|
|
|
d->attrs()[i] = a.isAccessor() ? Attr_Data : a;
|
2014-01-09 10:05:08 +00:00
|
|
|
|
|
|
|
n = n->nextNode();
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
} else {
|
2014-04-10 15:53:00 +00:00
|
|
|
while (n != sparse->sparse()->end()) {
|
2014-01-09 10:05:08 +00:00
|
|
|
if (n->value >= len)
|
|
|
|
break;
|
2014-04-10 15:53:00 +00:00
|
|
|
d->arrayData()[i] = sparse->arrayData()[n->value];
|
2014-01-09 10:05:08 +00:00
|
|
|
n = n->nextNode();
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
2014-04-10 15:53:00 +00:00
|
|
|
d->len() = i;
|
2014-01-09 10:05:08 +00:00
|
|
|
if (len > i)
|
|
|
|
len = i;
|
2014-04-10 15:53:00 +00:00
|
|
|
if (n != sparse->sparse()->end()) {
|
2014-01-09 10:05:08 +00:00
|
|
|
// have some entries outside the sort range that we need to ignore when sorting
|
|
|
|
thisObject->initSparseArray();
|
2014-04-10 15:53:00 +00:00
|
|
|
while (n != sparse->sparse()->end()) {
|
|
|
|
PropertyAttributes a = sparse->attrs() ? sparse->attrs()[n->value] : Attr_Data;
|
|
|
|
thisObject->arraySet(n->value, *reinterpret_cast<Property *>(sparse->arrayData() + n->value), a);
|
2014-01-09 10:05:08 +00:00
|
|
|
|
|
|
|
n = n->nextNode();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2014-01-20 15:01:26 +00:00
|
|
|
// ### explicitly delete sparse
|
2014-01-09 10:05:08 +00:00
|
|
|
} else {
|
2014-04-05 18:47:36 +00:00
|
|
|
SimpleArrayData *d = static_cast<SimpleArrayData *>(thisObject->arrayData());
|
2014-04-10 15:53:00 +00:00
|
|
|
if (len > d->len())
|
|
|
|
len = d->len();
|
2014-01-09 10:05:08 +00:00
|
|
|
|
|
|
|
// sort empty values to the end
|
2013-12-16 08:16:57 +00:00
|
|
|
for (uint i = 0; i < len; i++) {
|
2014-04-10 15:53:00 +00:00
|
|
|
if (thisObject->arrayData()->arrayData()[i].isEmpty()) {
|
2013-12-16 08:16:57 +00:00
|
|
|
while (--len > i)
|
2014-04-10 15:53:00 +00:00
|
|
|
if (!thisObject->arrayData()->arrayData()[len].isEmpty())
|
2013-12-16 08:16:57 +00:00
|
|
|
break;
|
2014-04-10 15:53:00 +00:00
|
|
|
Q_ASSERT(!thisObject->arrayData()->attrs() || !thisObject->arrayData()->attrs()[len].isAccessor());
|
|
|
|
thisObject->arrayData()->arrayData()[i] = thisObject->arrayData()->arrayData()[len];
|
|
|
|
thisObject->arrayData()->arrayData()[len] = Primitive::emptyValue();
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-09 10:05:08 +00:00
|
|
|
if (!len)
|
|
|
|
return;
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|
|
|
|
|
2014-01-09 10:05:08 +00:00
|
|
|
|
2013-12-16 08:16:57 +00:00
|
|
|
ArrayElementLessThan lessThan(context, thisObject, comparefn);
|
|
|
|
|
2014-04-10 15:53:00 +00:00
|
|
|
Value *begin = thisObject->arrayData()->arrayData();
|
2013-12-16 08:16:57 +00:00
|
|
|
std::sort(begin, begin + len, lessThan);
|
2014-01-09 10:05:08 +00:00
|
|
|
|
|
|
|
#ifdef CHECK_SPARSE_ARRAYS
|
|
|
|
thisObject->initSparseArray();
|
|
|
|
#endif
|
|
|
|
|
2013-12-16 08:16:57 +00:00
|
|
|
}
|