/**************************************************************************** ** ** 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(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(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(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(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(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(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(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(d)->freeList; static_cast(d)->freeList = pidx; static_cast(d)->sparse->erase(n); return true; } return false; } void SparseArrayData::setAttribute(ArrayData *d, uint index, PropertyAttributes attrs) { SparseArrayNode *n = static_cast(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(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(d)->sparse->push_front(idx); } } ReturnedValue SparseArrayData::pop_front(ArrayData *d) { Q_ASSERT(!d->attrs); uint idx = static_cast(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(d)->sparse->lowerBound(newLen); if (begin != static_cast(d)->sparse->end()) { SparseArrayNode *it = static_cast(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(d)->freeList; static_cast(d)->freeList = it->value; bool brk = (it == begin); SparseArrayNode *prev = it->previousNode(); static_cast(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(other)->sparse->begin(); it != static_cast(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(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(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(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); }