diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2013-05-14 18:58:34 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@digia.com> | 2013-05-21 20:49:24 +0200 |
commit | 32f7d078b64a864dd2d9e66c64a44ebf940ac2e1 (patch) | |
tree | 8cf1c4100b9bbaab145036f57785ed860e291fe3 /src/qml/qml/v8/qv8sequencewrapper_p_p.h | |
parent | cdc3433ebff280574e6103e4a6fdb13d29c5a7ce (diff) |
Port the rest of the sequence types to V4
Change-Id: Ia248f54b65b74d16e91e6ec72503eb967894586d
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src/qml/qml/v8/qv8sequencewrapper_p_p.h')
-rw-r--r-- | src/qml/qml/v8/qv8sequencewrapper_p_p.h | 729 |
1 files changed, 322 insertions, 407 deletions
diff --git a/src/qml/qml/v8/qv8sequencewrapper_p_p.h b/src/qml/qml/v8/qv8sequencewrapper_p_p.h index 0a8af7acd8..ced24ce5a0 100644 --- a/src/qml/qml/v8/qv8sequencewrapper_p_p.h +++ b/src/qml/qml/v8/qv8sequencewrapper_p_p.h @@ -60,50 +60,6 @@ QT_BEGIN_NAMESPACE -/*! - \internal - \class QV8SequenceResource - \brief The abstract base class of the external resource used in sequence type objects - - Every sequence type object returned by QV8SequenceWrapper::fromVariant() or - QV8SequenceWrapper::newSequence() has a type-specific QV8SequenceResource which - contains the type name, the meta type ids of the sequence and sequence element - types, as well as either the sequence data (copy) or object pointer and property - index (reference) data associated with the sequence. - */ -class QV8SequenceResource : public QV8ObjectResource -{ - V8_RESOURCE_TYPE(SequenceType); - -public: - virtual ~QV8SequenceResource() {} - - enum ObjectType { Reference, Copy }; - - virtual QVariant toVariant() = 0; - virtual bool isEqual(const QV8SequenceResource *v) = 0; - - virtual quint32 lengthGetter() = 0; - virtual void lengthSetter(v8::Handle<v8::Value> value) = 0; - virtual v8::Handle<v8::Value> indexedSetter(quint32 index, v8::Handle<v8::Value> value) = 0; - virtual v8::Handle<v8::Value> indexedGetter(quint32 index) = 0; - virtual v8::Handle<v8::Value> indexedDeleter(quint32 index) = 0; - virtual v8::Handle<v8::Array> indexedEnumerator() = 0; - virtual QV4::Value toString() = 0; - virtual void sort(v8::Handle<v8::Function> comparer) = 0; - - ObjectType objectType; - QByteArray typeName; - int sequenceMetaTypeId; - int elementMetaTypeId; - -protected: - QV8SequenceResource(QV8Engine *engine, ObjectType type, const QByteArray &name, int sequenceId, int elementId) - : QV8ObjectResource(engine), objectType(type), typeName(name), sequenceMetaTypeId(sequenceId), elementMetaTypeId(elementId) - { - } -}; - // helper function to generate valid warnings if errors occur during sequence operations. static void generateWarning(QV8Engine *engine, const QString& description) { @@ -123,415 +79,153 @@ static void generateWarning(QV8Engine *engine, const QString& description) QQmlEnginePrivate::warning(engine->engine(), retn); } +// F(elementType, elementTypeName, sequenceType, defaultValue) +#define FOREACH_QML_SEQUENCE_TYPE(F) \ + F(int, Int, QList<int>, 0) \ + F(qreal, Real, QList<qreal>, 0.0) \ + F(bool, Bool, QList<bool>, false) \ + F(QString, String, QList<QString>, QString()) \ + F(QString, QString, QStringList, QString()) \ + F(QUrl, Url, QList<QUrl>, QUrl()) -static int convertV8ValueToInt(QV8Engine *, v8::Handle<v8::Value> v) +class QV4_JS_CLASS(QQmlSequenceBase) : public QV4::Object { - return v->Int32Value(); -} +public: + QQmlSequenceBase(QV4::ExecutionEngine *engine) + : QV4::Object(engine) + {} -static v8::Handle<v8::Value> convertIntToV8Value(QV8Engine *, int v) -{ - return QV4::Value::fromInt32(v); -} + void initClass(QV4::ExecutionEngine *engine); + + QV4::Value method_get_length(QV4::SimpleCallContext* ctx) QV4_ANNOTATE(attributes QV4::Attr_ReadOnly); + + QV4::Value method_set_length(QV4::SimpleCallContext* ctx); +}; -static QString convertIntToString(QV8Engine *, int v) +class QV4_JS_CLASS(QQmlSequencePrototype) : public QV4::Object { - return QString::number(v); -} + QV4_ANNOTATE(staticInitClass true) +public: + static void initClass(QV4::ExecutionEngine *engine, const QV4::Value &value); + + static QV4::Value method_valueOf(QV4::SimpleCallContext *ctx) + { + return QV4::Value::fromString(ctx->thisObject.toString(ctx)); + } + + static QV4::Value method_sort(QV4::SimpleCallContext *ctx) QV4_ARGC(1); +}; -static qreal convertV8ValueToReal(QV8Engine *, v8::Handle<v8::Value> v) +static QV4::Value convertElementToValue(QV4::ExecutionContext *ctx, const QString &element) { - return v->NumberValue(); + return QV4::Value::fromString(ctx, element); } -static v8::Handle<v8::Value> convertRealToV8Value(QV8Engine *, qreal v) +static QV4::Value convertElementToValue(QV4::ExecutionContext *ctx, int element) { - return QV4::Value::fromDouble(v); + return QV4::Value::fromInt32(element); } -static QString convertRealToString(QV8Engine *, qreal v) +static QV4::Value convertElementToValue(QV4::ExecutionContext *ctx, const QUrl &element) { - return QString::number(v); + return QV4::Value::fromString(ctx, element.toString()); } -static bool convertV8ValueToBool(QV8Engine *, v8::Handle<v8::Value> v) +static QV4::Value convertElementToValue(QV4::ExecutionContext *ctx, qreal element) { - return v->BooleanValue(); + return QV4::Value::fromDouble(element); } -static v8::Handle<v8::Value> convertBoolToV8Value(QV8Engine *, bool v) +static QV4::Value convertElementToValue(QV4::ExecutionContext *ctx, bool element) { - return QV4::Value::fromBoolean(v); + return QV4::Value::fromBoolean(element); } -static QString convertBoolToString(QV8Engine *, bool v) +static QString convertElementToString(const QString &element) { - if (v) - return QLatin1String("true"); - return QLatin1String("false"); + return element; } -static QString convertV8ValueToString(QV8Engine *e, v8::Handle<v8::Value> v) +static QString convertElementToString(int element) { - return v->v4Value().toQString(); + return QString::number(element); } -static v8::Handle<v8::Value> convertStringToV8Value(QV8Engine *e, const QString &v) +static QString convertElementToString(const QUrl &element) { - return e->toString(v); + return element.toString(); } -static QString convertStringToString(QV8Engine *, const QString &v) +static QString convertElementToString(qreal element) { - return v; + QString qstr; + __qmljs_numberToString(&qstr, element, 10); + return qstr; } -static QString convertV8ValueToQString(QV8Engine *e, v8::Handle<v8::Value> v) +static QString convertElementToString(bool element) { - return v->v4Value().toQString(); + if (element) + return QStringLiteral("true"); + else + return QStringLiteral("false"); } -static v8::Handle<v8::Value> convertQStringToV8Value(QV8Engine *e, const QString &v) +template <typename ElementType> ElementType convertValueToElement(const QV4::Value &value); + +template <> QString convertValueToElement(const QV4::Value &value) { - return e->toString(v); + return value.toQString(); } -static QString convertQStringToString(QV8Engine *, const QString &v) +template <> int convertValueToElement(const QV4::Value &value) { - return v; + return value.toInt32(); } -static QUrl convertV8ValueToUrl(QV8Engine *e, v8::Handle<v8::Value> v) +template <> QUrl convertValueToElement(const QV4::Value &value) { - return QUrl(v->v4Value().toQString()); + return QUrl(value.toQString()); } -static v8::Handle<v8::Value> convertUrlToV8Value(QV8Engine *e, const QUrl &v) +template <> qreal convertValueToElement(const QV4::Value &value) { - return e->toString(QLatin1String(v.toEncoded().data())); + return value.toNumber(); } -static QString convertUrlToString(QV8Engine *, const QUrl &v) +template <> bool convertValueToElement(const QV4::Value &value) { - return v.toString(); + return value.toBoolean(); } - -/* - \internal - \class QV8<Type>SequenceResource - \brief The external resource used in sequence type objects - - Every sequence type object returned by QV8SequenceWrapper::newSequence() has - a QV8<Type>SequenceResource which contains a property index and a pointer - to the object which contains the property. - - Every sequence type object returned by QV8SequenceWrapper::fromVariant() has - a QV8<Type>SequenceResource which contains a copy of the sequence value. - Operations on the sequence are implemented directly in terms of that sequence data. - - There exists one QV8<Type>SequenceResource instance for every JavaScript Object - (sequence) instance returned from QV8SequenceWrapper::newSequence() or - QV8SequenceWrapper::fromVariant(). - */ - -// F(elementType, elementTypeName, sequenceType, defaultValue) -#define FOREACH_QML_SEQUENCE_TYPE(F) \ - F(int, Int, QList<int>, 0) \ - F(qreal, Real, QList<qreal>, 0.0) \ - F(bool, Bool, QList<bool>, false) \ - F(QString, String, QList<QString>, QString()) \ - F(QUrl, Url, QList<QUrl>, QUrl()) - -#define QML_SEQUENCE_TYPE_RESOURCE(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue, ConversionToV8fn, ConversionFromV8fn, ToStringfn) \ - QT_END_NAMESPACE \ - Q_DECLARE_METATYPE(SequenceType) \ - QT_BEGIN_NAMESPACE \ - class QV8##SequenceElementTypeName##SequenceResource : public QV8SequenceResource { \ - public:\ - QV8##SequenceElementTypeName##SequenceResource(QV8Engine *engine, QObject *obj, int propIdx) \ - : QV8SequenceResource(engine, QV8SequenceResource::Reference, #SequenceType, qMetaTypeId<SequenceType>(), qMetaTypeId<SequenceElementType>()) \ - , object(obj), propertyIndex(propIdx) \ - { \ - } \ - QV8##SequenceElementTypeName##SequenceResource(QV8Engine *engine, const SequenceType &value) \ - : QV8SequenceResource(engine, QV8SequenceResource::Copy, #SequenceType, qMetaTypeId<SequenceType>(), qMetaTypeId<SequenceElementType>()) \ - , object(0), propertyIndex(-1), c(value) \ - { \ - } \ - ~QV8##SequenceElementTypeName##SequenceResource() \ - { \ - } \ - static QVariant toVariant(QV8Engine *e, v8::Handle<v8::Array> array, uint32_t length, bool *succeeded) \ - { \ - SequenceType list; \ - list.reserve(length); \ - for (uint32_t ii = 0; ii < length; ++ii) { \ - list.append(ConversionFromV8fn(e, array->Get(ii))); \ - } \ - *succeeded = true; \ - return QVariant::fromValue<SequenceType>(list); \ - } \ - QVariant toVariant() \ - { \ - if (objectType == QV8SequenceResource::Reference) { \ - if (!object) \ - return QVariant(); \ - loadReference(); \ - } \ - return QVariant::fromValue<SequenceType>(c); \ - } \ - bool isEqual(const QV8SequenceResource *v) \ - { \ - /* Note: two different sequences can never be equal (even if they */ \ - /* contain the same elements in the same order) in order to */ \ - /* maintain JavaScript semantics. However, if they both reference */ \ - /* the same QObject+propertyIndex, they are equal. */ \ - if (objectType == QV8SequenceResource::Reference && v->objectType == QV8SequenceResource::Reference) { \ - if (sequenceMetaTypeId == v->sequenceMetaTypeId) { \ - const QV8##SequenceElementTypeName##SequenceResource *rhs = static_cast<const QV8##SequenceElementTypeName##SequenceResource *>(v); \ - return (object != 0 && object == rhs->object && propertyIndex == rhs->propertyIndex); \ - } \ - } else if (objectType == QV8SequenceResource::Copy && v->objectType == QV8SequenceResource::Copy) { \ - if (sequenceMetaTypeId == v->sequenceMetaTypeId) { \ - const QV8##SequenceElementTypeName##SequenceResource *rhs = static_cast<const QV8##SequenceElementTypeName##SequenceResource *>(v); \ - return (this == rhs); \ - } \ - } \ - return false; \ - } \ - quint32 lengthGetter() \ - { \ - if (objectType == QV8SequenceResource::Reference) { \ - if (!object) \ - return 0; \ - loadReference(); \ - } \ - return static_cast<quint32>(c.count()); \ - } \ - void lengthSetter(v8::Handle<v8::Value> value) \ - { \ - /* Get the new required length */ \ - if (value.IsEmpty() || !value->IsUint32()) \ - return; \ - quint32 newLength = value->Uint32Value(); \ - /* Qt containers have int (rather than uint) allowable indexes. */ \ - if (newLength > INT_MAX) { \ - generateWarning(engine, QLatin1String("Index out of range during length set")); \ - return; \ - } \ - /* Read the sequence from the QObject property if we're a reference */ \ - if (objectType == QV8SequenceResource::Reference) { \ - if (!object) \ - return; \ - loadReference(); \ - } \ - /* Determine whether we need to modify the sequence */ \ - qint32 newCount = static_cast<qint32>(newLength); \ - qint32 count = c.count(); \ - if (newCount == count) { \ - return; \ - } else if (newCount > count) { \ - /* according to ECMA262r3 we need to insert */ \ - /* undefined values increasing length to newLength. */ \ - /* We cannot, so we insert default-values instead. */ \ - c.reserve(newCount); \ - while (newCount > count++) { \ - c.append(DefaultValue); \ - } \ - } else { \ - /* according to ECMA262r3 we need to remove */ \ - /* elements until the sequence is the required length. */ \ - while (newCount < count) { \ - count--; \ - c.removeAt(count); \ - } \ - } \ - /* write back if required. */ \ - if (objectType == QV8SequenceResource::Reference) { \ - /* write back. already checked that object is non-null, so skip that check here. */ \ - storeReference(); \ - } \ - return; \ - } \ - v8::Handle<v8::Value> indexedSetter(quint32 index, v8::Handle<v8::Value> value) \ - { \ - /* Qt containers have int (rather than uint) allowable indexes. */ \ - if (index > INT_MAX) { \ - generateWarning(engine, QLatin1String("Index out of range during indexed set")); \ - return QV4::Value::undefinedValue(); \ - } \ - if (objectType == QV8SequenceResource::Reference) { \ - if (!object) \ - return QV4::Value::undefinedValue(); \ - loadReference(); \ - } \ - /* modify the sequence */ \ - SequenceElementType elementValue = ConversionFromV8fn(engine, value); \ - qint32 count = c.count(); \ - qint32 signedIdx = static_cast<qint32>(index); \ - if (signedIdx == count) { \ - c.append(elementValue); \ - } else if (signedIdx < count) { \ - c[index] = elementValue; \ - } else { \ - /* according to ECMA262r3 we need to insert */ \ - /* the value at the given index, increasing length to index+1. */ \ - c.reserve(signedIdx + 1); \ - while (signedIdx > count++) { \ - c.append(DefaultValue); \ - } \ - c.append(elementValue); \ - } \ - /* write back. already checked that object is non-null, so skip that check here. */ \ - if (objectType == QV8SequenceResource::Reference) \ - storeReference(); \ - return value; \ - } \ - v8::Handle<v8::Value> indexedGetter(quint32 index) \ - { \ - /* Qt containers have int (rather than uint) allowable indexes. */ \ - if (index > INT_MAX) { \ - generateWarning(engine, QLatin1String("Index out of range during indexed get")); \ - return QV4::Value::undefinedValue(); \ - } \ - if (objectType == QV8SequenceResource::Reference) { \ - if (!object) \ - return QV4::Value::undefinedValue(); \ - loadReference(); \ - } \ - qint32 count = c.count(); \ - qint32 signedIdx = static_cast<qint32>(index); \ - if (signedIdx < count) \ - return ConversionToV8fn(engine, c.at(signedIdx)); \ - return QV4::Value::undefinedValue(); \ - } \ - v8::Handle<v8::Value> indexedDeleter(quint32 index) \ - { \ - /* Qt containers have int (rather than uint) allowable indexes. */ \ - if (index > INT_MAX) \ - return QV4::Value::fromBoolean(false); \ - /* Read in the sequence from the QObject */ \ - if (objectType == QV8SequenceResource::Reference) { \ - if (!object) \ - return QV4::Value::fromBoolean(false); \ - loadReference(); \ - } \ - qint32 signedIdx = static_cast<qint32>(index); \ - if (signedIdx < c.count()) { \ - /* according to ECMA262r3 it should be Undefined, */ \ - /* but we cannot, so we insert a default-value instead. */ \ - c.replace(signedIdx, DefaultValue); \ - if (objectType == QV8SequenceResource::Reference) { \ - /* write back. already checked that object is non-null, so skip that check here. */ \ - storeReference(); \ - } \ - return QV4::Value::fromBoolean(true); \ - } \ - return QV4::Value::fromBoolean(false); \ - } \ - v8::Handle<v8::Array> indexedEnumerator() \ - { \ - if (objectType == QV8SequenceResource::Reference) { \ - if (!object) \ - return v8::Handle<v8::Array>(); \ - loadReference(); \ - } \ - qint32 count = c.count(); \ - v8::Handle<v8::Array> retn = v8::Array::New(count); \ - for (qint32 i = 0; i < count; ++i) { \ - retn->Set(static_cast<quint32>(i), QV4::Value::fromUInt32(static_cast<quint32>(i))); \ - } \ - return retn; \ - } \ - QV4::Value toString() \ - { \ - if (objectType == QV8SequenceResource::Reference) { \ - if (!object) \ - return QV4::Value::undefinedValue(); \ - loadReference(); \ - } \ - QString str; \ - qint32 count = c.count(); \ - for (qint32 i = 0; i < count; ++i) { \ - str += QString(QLatin1String("%1,")).arg(ToStringfn(engine, c[i])); \ - } \ - str.chop(1); \ - return engine->toString(str); \ - } \ - void loadReference() \ - { \ - Q_ASSERT(object); \ - Q_ASSERT(objectType == QV8SequenceResource::Reference); \ - void *a[] = { &c, 0 }; \ - QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyIndex, a); \ - } \ - void storeReference() \ - { \ - Q_ASSERT(object); \ - Q_ASSERT(objectType == QV8SequenceResource::Reference); \ - int status = -1; \ - QQmlPropertyPrivate::WriteFlags flags = \ - QQmlPropertyPrivate::DontRemoveBinding; \ - void *a[] = { &c, 0, &status, &flags }; \ - QMetaObject::metacall(object, QMetaObject::WriteProperty, propertyIndex, a); \ - } \ - class CompareFunctor \ - { \ - public: \ - CompareFunctor(QV8Engine *engine, v8::Handle<v8::Function> f) : jsFn(f), eng(engine) {} \ - bool operator()(SequenceElementType e0, SequenceElementType e1) \ - { \ - v8::Handle<v8::Value> argv[2] = { eng->fromVariant(e0), eng->fromVariant(e1) }; \ - v8::Handle<v8::Value> compareValue = jsFn->Call(v8::Value::fromV4Value(eng->global()), 2, argv); \ - return compareValue->NumberValue() < 0; \ - } \ - private: \ - v8::Handle<v8::Function> jsFn; \ - QV8Engine *eng; \ - }; \ - void sort(v8::Handle<v8::Function> jsCompareFunction) \ - { \ - CompareFunctor cf(engine, jsCompareFunction); \ - qSort(c.begin(), c.end(), cf); \ - } \ - private: \ - QQmlGuard<QObject> object; \ - int propertyIndex; \ - SequenceType c; \ - }; - -#define GENERATE_QML_SEQUENCE_TYPE_RESOURCE(ElementType, ElementTypeName, SequenceType, DefaultValue) \ - QML_SEQUENCE_TYPE_RESOURCE(ElementType, ElementTypeName, SequenceType, DefaultValue, convert##ElementTypeName##ToV8Value, convertV8ValueTo##ElementTypeName, convert##ElementTypeName##ToString) - -FOREACH_QML_SEQUENCE_TYPE(GENERATE_QML_SEQUENCE_TYPE_RESOURCE) -#undef GENERATE_QML_SEQUENCE_TYPE_RESOURCE -#undef QML_SEQUENCE_TYPE_RESOURCE - -class QQmlStringList : public QV4::Object +template <typename Container, int ManagedType> +class QQmlSequence : public QQmlSequenceBase { public: - QQmlStringList(QV4::ExecutionEngine *engine, const QStringList &container) - : QV4::Object(engine) + QQmlSequence(QV4::ExecutionEngine *engine, const Container &container) + : QQmlSequenceBase(engine) , m_container(container) + , m_object(0) + , m_propertyIndex(-1) + , m_isReference(false) { - type = Type_QmlStringList; + type = ManagedType; vtbl = &static_vtbl; - m_lengthProperty = insertMember(engine->id_length, QV4::Attr_ReadOnly); prototype = engine->arrayPrototype; - updateLength(); + initClass(engine); } - QQmlStringList(QV4::ExecutionEngine *engine, QObject *object, int propertyIndex) - : QV4::Object(engine) + QQmlSequence(QV4::ExecutionEngine *engine, QObject *object, int propertyIndex) + : QQmlSequenceBase(engine) + , m_object(object) + , m_propertyIndex(propertyIndex) + , m_isReference(true) { - type = Type_QmlStringList; + type = ManagedType; vtbl = &static_vtbl; - m_lengthProperty = insertMember(engine->id_length, QV4::Attr_ReadOnly); prototype = engine->arrayPrototype; - void *a[] = { &m_container, 0 }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyIndex, a); - updateLength(); + loadReference(); + initClass(engine); } QV4::Value containerGetIndexed(QV4::ExecutionContext *ctx, uint index, bool *hasProperty) @@ -543,11 +237,19 @@ public: *hasProperty = false; return QV4::Value::undefinedValue(); } + if (m_isReference) { + if (!m_object) { + if (hasProperty) + *hasProperty = false; + return QV4::Value::undefinedValue(); + } + loadReference(); + } qint32 signedIdx = static_cast<qint32>(index); if (signedIdx < m_container.count()) { if (hasProperty) *hasProperty = true; - return QV4::Value::fromString(ctx, m_container.at(signedIdx)); + return convertElementToValue(ctx, m_container.at(signedIdx)); } if (hasProperty) *hasProperty = false; @@ -561,15 +263,21 @@ public: generateWarning(QV8Engine::get(ctx->engine->publicEngine), QLatin1String("Index out of range during indexed put")); return; } + + if (m_isReference) { + if (!m_object) + return; + loadReference(); + } + qint32 signedIdx = static_cast<qint32>(index); int count = m_container.count(); - QString element = value.toQString(); + typename Container::value_type element = convertValueToElement<typename Container::value_type>(value); if (signedIdx == count) { m_container.append(element); - updateLength(); } else if (signedIdx < count) { m_container[signedIdx] = element; } else { @@ -577,11 +285,13 @@ public: /* the value at the given index, increasing length to index+1. */ m_container.reserve(signedIdx + 1); while (signedIdx > count++) { - m_container.append(QString()); + m_container.append(typename Container::value_type()); } m_container.append(element); - updateLength(); } + + if (m_isReference) + storeReference(); } QV4::PropertyAttributes containerQueryIndexed(QV4::ExecutionContext *ctx, uint index) @@ -591,6 +301,11 @@ public: generateWarning(QV8Engine::get(ctx->engine->publicEngine), QLatin1String("Index out of range during indexed query")); return QV4::Attr_Invalid; } + if (m_isReference) { + if (!m_object) + return QV4::Attr_Invalid; + loadReference(); + } qint32 signedIdx = static_cast<qint32>(index); return (index < m_container.count()) ? QV4::Attr_Data : QV4::Attr_Invalid; } @@ -600,6 +315,11 @@ public: /* Qt containers have int (rather than uint) allowable indexes. */ if (index > INT_MAX) return false; + if (m_isReference) { + if (!m_object) + return false; + loadReference(); + } qint32 signedIdx = static_cast<qint32>(index); if (signedIdx >= m_container.count()) @@ -607,40 +327,235 @@ public: /* according to ECMA262r3 it should be Undefined, */ /* but we cannot, so we insert a default-value instead. */ - m_container.replace(signedIdx, QString()); + m_container.replace(signedIdx, typename Container::value_type()); + + if (m_isReference) + storeReference(); + return true; } + void sort(QV4::SimpleCallContext *ctx) + { + if (m_isReference) { + if (!m_object) + return; + loadReference(); + } + + struct DefaultCompareFunctor + { + bool operator()(typename Container::value_type lhs, typename Container::value_type rhs) + { + return convertElementToString(lhs) < convertElementToString(rhs); + } + }; + + struct CompareFunctor + { + CompareFunctor(QV4::ExecutionContext *ctx, const QV4::Value &compareFn) + : m_ctx(ctx), m_compareFn(compareFn) + {} + + bool operator()(typename Container::value_type lhs, typename Container::value_type rhs) + { + QV4::Managed *fun = m_compareFn.asManaged(); + QV4::Value argv[2] = { + convertElementToValue(m_ctx, lhs), + convertElementToValue(m_ctx, rhs) + }; + QV4::Value result = fun->call(m_ctx, QV4::Value::fromObject(m_ctx->engine->globalObject), argv, 2); + return result.toNumber() < 0; + } + + private: + QV4::ExecutionContext *m_ctx; + QV4::Value m_compareFn; + }; + + if (ctx->argumentCount == 1 && ctx->arguments[0].asFunctionObject()) { + QV4::Value compareFn = ctx->arguments[0]; + CompareFunctor cf(ctx, compareFn); + qSort(m_container.begin(), m_container.end(), cf); + } else { + DefaultCompareFunctor cf; + qSort(m_container.begin(), m_container.end(), cf); + } + + if (m_isReference) + storeReference(); + } + + QV4::Value lengthGetter(QV4::SimpleCallContext*) + { + if (m_isReference) { + if (!m_object) + return QV4::Value::fromInt32(0); + loadReference(); + } + return QV4::Value::fromInt32(m_container.count()); + } + + void lengthSetter(QV4::SimpleCallContext* ctx) + { + quint32 newLength = ctx->arguments[0].toUInt32(); + /* Qt containers have int (rather than uint) allowable indexes. */ + if (newLength > INT_MAX) { + generateWarning(QV8Engine::get(ctx->engine->publicEngine), QLatin1String("Index out of range during length set")); + return; + } + /* Read the sequence from the QObject property if we're a reference */ + if (m_isReference) { + if (!m_object) + return; + loadReference(); + } + /* Determine whether we need to modify the sequence */ + qint32 newCount = static_cast<qint32>(newLength); + qint32 count = m_container.count(); + if (newCount == count) { + return; + } else if (newCount > count) { + /* according to ECMA262r3 we need to insert */ + /* undefined values increasing length to newLength. */ + /* We cannot, so we insert default-values instead. */ + m_container.reserve(newCount); + while (newCount > count++) { + m_container.append(typename Container::value_type()); + } + } else { + /* according to ECMA262r3 we need to remove */ + /* elements until the sequence is the required length. */ + while (newCount < count) { + count--; + m_container.removeAt(count); + } + } + /* write back if required. */ + if (m_isReference) { + /* write back. already checked that object is non-null, so skip that check here. */ + storeReference(); + } + } + QVariant toVariant() const - { return QVariant::fromValue<QStringList>(m_container); } + { return QVariant::fromValue<Container>(m_container); } + + static QVariant toVariant(QV4::ArrayObject *array) + { + Container result; + uint32_t length = array->arrayLength(); + for (uint32_t i = 0; i < length; ++i) + result << convertValueToElement<typename Container::value_type>(array->getIndexed(i)); + return QVariant::fromValue(result); + } private: - void updateLength() + void loadReference() + { + Q_ASSERT(m_object); + Q_ASSERT(m_isReference); + void *a[] = { &m_container, 0 }; + QMetaObject::metacall(m_object, QMetaObject::ReadProperty, m_propertyIndex, a); + } + + void storeReference() { - m_lengthProperty->value = QV4::Value::fromInt32(m_container.length()); + Q_ASSERT(m_object); + Q_ASSERT(m_isReference); + int status = -1; + QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding; + void *a[] = { &m_container, 0, &status, &flags }; + QMetaObject::metacall(m_object, QMetaObject::WriteProperty, m_propertyIndex, a); } - QStringList m_container; - QV4::Property *m_lengthProperty; + Container m_container; + QQmlGuard<QObject> m_object; + int m_propertyIndex; + bool m_isReference; static QV4::Value getIndexed(QV4::Managed *that, QV4::ExecutionContext *ctx, uint index, bool *hasProperty) - { return static_cast<QQmlStringList *>(that)->containerGetIndexed(ctx, index, hasProperty); } + { return static_cast<QQmlSequence<Container, ManagedType> *>(that)->containerGetIndexed(ctx, index, hasProperty); } static void putIndexed(Managed *that, QV4::ExecutionContext *ctx, uint index, const QV4::Value &value) - { static_cast<QQmlStringList *>(that)->containerPutIndexed(ctx, index, value); } + { static_cast<QQmlSequence<Container, ManagedType> *>(that)->containerPutIndexed(ctx, index, value); } static QV4::PropertyAttributes queryIndexed(QV4::Managed *that, QV4::ExecutionContext *ctx, uint index) - { return static_cast<QQmlStringList *>(that)->containerQueryIndexed(ctx, index); } + { return static_cast<QQmlSequence<Container, ManagedType> *>(that)->containerQueryIndexed(ctx, index); } static bool deleteIndexedProperty(QV4::Managed *that, QV4::ExecutionContext *ctx, uint index) - { return static_cast<QQmlStringList *>(that)->containerDeleteIndexedProperty(ctx, index); } + { return static_cast<QQmlSequence<Container, ManagedType> *>(that)->containerDeleteIndexedProperty(ctx, index); } static void destroy(Managed *that) { - static_cast<QQmlStringList *>(that)->~QQmlStringList(); + static_cast<QQmlSequence<Container, ManagedType> *>(that)->~QQmlSequence<Container, ManagedType>(); } static const QV4::ManagedVTable static_vtbl; }; +typedef QQmlSequence<QStringList, QV4::Managed::Type_QmlQStringList> QQmlQStringList; +template<> +DEFINE_MANAGED_VTABLE(QQmlQStringList); +typedef QQmlSequence<QList<QString>, QV4::Managed::Type_QmlStringList> QQmlStringList; +template<> DEFINE_MANAGED_VTABLE(QQmlStringList); +typedef QQmlSequence<QList<int>, QV4::Managed::Type_QmlIntList> QQmlIntList; +template<> +DEFINE_MANAGED_VTABLE(QQmlIntList); +typedef QQmlSequence<QList<QUrl>, QV4::Managed::Type_QmlUrlList> QQmlUrlList; +template<> +DEFINE_MANAGED_VTABLE(QQmlUrlList); +typedef QQmlSequence<QList<bool>, QV4::Managed::Type_QmlBoolList> QQmlBoolList; +template<> +DEFINE_MANAGED_VTABLE(QQmlBoolList); +typedef QQmlSequence<QList<qreal>, QV4::Managed::Type_QmlRealList> QQmlRealList; +template<> +DEFINE_MANAGED_VTABLE(QQmlRealList); + +QV4::Value QQmlSequencePrototype::method_sort(QV4::SimpleCallContext *ctx) +{ + QV4::Object *o = ctx->thisObject.asObject(); + if (!o || !o->isListType()) + ctx->throwTypeError(); + + if (ctx->argumentCount < 2) { +#define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \ + case QV4::Managed::Type_Qml##SequenceElementTypeName##List: o->asQml##SequenceElementTypeName##List()->sort(ctx); break; + + switch (o->internalType()) { + FOREACH_QML_SEQUENCE_TYPE(CALL_SORT) + default: break; + } + +#undef CALL_SORT + } + return ctx->thisObject; +} + +QV4::Value QQmlSequenceBase::method_get_length(QV4::SimpleCallContext* ctx) QV4_ANNOTATE(attributes QV4::Attr_ReadOnly) +{ +#define CALL_LENGTH_GETTER(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \ + case QV4::Managed::Type_Qml##SequenceElementTypeName##List: return asQml##SequenceElementTypeName##List()->lengthGetter(ctx); + + switch (internalType()) { + FOREACH_QML_SEQUENCE_TYPE(CALL_LENGTH_GETTER) + default: QV4::Value::undefinedValue(); + } + +#undef CALL_LENGTH_GETTER +} + +QV4::Value QQmlSequenceBase::method_set_length(QV4::SimpleCallContext* ctx) +{ +#define CALL_LENGTH_SETTER(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \ + case QV4::Managed::Type_Qml##SequenceElementTypeName##List: asQml##SequenceElementTypeName##List()->lengthSetter(ctx); break; + + switch (internalType()) { + FOREACH_QML_SEQUENCE_TYPE(CALL_LENGTH_SETTER) + default: break; + } +#undef CALL_LENGTH_SETTER + + return QV4::Value::undefinedValue(); +} QT_END_NAMESPACE |