1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #ifndef QV8SEQUENCEWRAPPER_P_P_H
43 #define QV8SEQUENCEWRAPPER_P_P_H
49 // This file is not part of the Qt API. It exists purely as an
50 // implementation detail. This header file may change from version to
51 // version without notice, or even be removed.
56 #include <private/qqmlengine_p.h>
57 #include <private/qqmlmetatype_p.h>
63 \class QV8SequenceResource
64 \brief The abstract base class of the external resource used in sequence type objects
66 Every sequence type object returned by QV8SequenceWrapper::fromVariant() or
67 QV8SequenceWrapper::newSequence() has a type-specific QV8SequenceResource which
68 contains the type name, the meta type ids of the sequence and sequence element
69 types, as well as either the sequence data (copy) or object pointer and property
70 index (reference) data associated with the sequence.
72 class QV8SequenceResource : public QV8ObjectResource
74 V8_RESOURCE_TYPE(SequenceType);
77 virtual ~QV8SequenceResource() {}
79 enum ObjectType { Reference, Copy };
81 virtual QVariant toVariant() = 0;
82 virtual bool isEqual(const QV8SequenceResource *v) = 0;
84 virtual quint32 lengthGetter() = 0;
85 virtual void lengthSetter(v8::Handle<v8::Value> value) = 0;
86 virtual v8::Handle<v8::Value> indexedSetter(quint32 index, v8::Handle<v8::Value> value) = 0;
87 virtual v8::Handle<v8::Value> indexedGetter(quint32 index) = 0;
88 virtual v8::Handle<v8::Boolean> indexedDeleter(quint32 index) = 0;
89 virtual v8::Handle<v8::Array> indexedEnumerator() = 0;
90 virtual v8::Handle<v8::Value> toString() = 0;
91 virtual void sort(v8::Handle<v8::Function> comparer) = 0;
93 ObjectType objectType;
95 int sequenceMetaTypeId;
96 int elementMetaTypeId;
99 QV8SequenceResource(QV8Engine *engine, ObjectType type, const QByteArray &name, int sequenceId, int elementId)
100 : QV8ObjectResource(engine), objectType(type), typeName(name), sequenceMetaTypeId(sequenceId), elementMetaTypeId(elementId)
105 // helper function to generate valid warnings if errors occur during sequence operations.
106 static void generateWarning(QV8Engine *engine, const QString& description)
110 v8::Local<v8::StackTrace> currStack = v8::StackTrace::CurrentStackTrace(1);
111 if (currStack.IsEmpty())
113 v8::Local<v8::StackFrame> currFrame = currStack->GetFrame(0);
114 if (currFrame.IsEmpty())
118 retn.setDescription(description);
119 retn.setLine(currFrame->GetLineNumber());
120 retn.setUrl(QUrl(engine->toString(currFrame->GetScriptName())));
121 QQmlEnginePrivate::warning(engine->engine(), retn);
125 static int convertV8ValueToInt(QV8Engine *, v8::Handle<v8::Value> v)
127 return v->Int32Value();
130 static v8::Handle<v8::Value> convertIntToV8Value(QV8Engine *, int v)
132 return v8::Integer::New(v);
135 static QString convertIntToString(QV8Engine *, int v)
137 return QString::number(v);
140 static qreal convertV8ValueToReal(QV8Engine *, v8::Handle<v8::Value> v)
142 return v->NumberValue();
145 static v8::Handle<v8::Value> convertRealToV8Value(QV8Engine *, qreal v)
147 return v8::Number::New(v);
150 static QString convertRealToString(QV8Engine *, qreal v)
152 return QString::number(v);
155 static bool convertV8ValueToBool(QV8Engine *, v8::Handle<v8::Value> v)
157 return v->BooleanValue();
160 static v8::Handle<v8::Value> convertBoolToV8Value(QV8Engine *, bool v)
162 return v8::Boolean::New(v);
165 static QString convertBoolToString(QV8Engine *, bool v)
168 return QLatin1String("true");
169 return QLatin1String("false");
172 static QString convertV8ValueToString(QV8Engine *e, v8::Handle<v8::Value> v)
174 return e->toString(v->ToString());
177 static v8::Handle<v8::Value> convertStringToV8Value(QV8Engine *e, const QString &v)
179 return e->toString(v);
182 static QString convertStringToString(QV8Engine *, const QString &v)
187 static QString convertV8ValueToQString(QV8Engine *e, v8::Handle<v8::Value> v)
189 return e->toString(v->ToString());
192 static v8::Handle<v8::Value> convertQStringToV8Value(QV8Engine *e, const QString &v)
194 return e->toString(v);
197 static QString convertQStringToString(QV8Engine *, const QString &v)
202 static QUrl convertV8ValueToUrl(QV8Engine *e, v8::Handle<v8::Value> v)
204 return QUrl(e->toString(v->ToString()));
207 static v8::Handle<v8::Value> convertUrlToV8Value(QV8Engine *e, const QUrl &v)
209 return e->toString(QLatin1String(v.toEncoded().data()));
212 static QString convertUrlToString(QV8Engine *, const QUrl &v)
220 \class QV8<Type>SequenceResource
221 \brief The external resource used in sequence type objects
223 Every sequence type object returned by QV8SequenceWrapper::newSequence() has
224 a QV8<Type>SequenceResource which contains a property index and a pointer
225 to the object which contains the property.
227 Every sequence type object returned by QV8SequenceWrapper::fromVariant() has
228 a QV8<Type>SequenceResource which contains a copy of the sequence value.
229 Operations on the sequence are implemented directly in terms of that sequence data.
231 There exists one QV8<Type>SequenceResource instance for every JavaScript Object
232 (sequence) instance returned from QV8SequenceWrapper::newSequence() or
233 QV8SequenceWrapper::fromVariant().
236 // F(elementType, elementTypeName, sequenceType, defaultValue)
237 #define FOREACH_QML_SEQUENCE_TYPE(F) \
238 F(int, Int, QList<int>, 0) \
239 F(qreal, Real, QList<qreal>, 0.0) \
240 F(bool, Bool, QList<bool>, false) \
241 F(QString, String, QList<QString>, QString()) \
242 F(QString, QString, QStringList, QString()) \
243 F(QUrl, Url, QList<QUrl>, QUrl())
245 #define QML_SEQUENCE_TYPE_RESOURCE(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue, ConversionToV8fn, ConversionFromV8fn, ToStringfn) \
247 Q_DECLARE_METATYPE(SequenceType) \
249 class QV8##SequenceElementTypeName##SequenceResource : public QV8SequenceResource { \
251 QV8##SequenceElementTypeName##SequenceResource(QV8Engine *engine, QObject *obj, int propIdx) \
252 : QV8SequenceResource(engine, QV8SequenceResource::Reference, #SequenceType, qMetaTypeId<SequenceType>(), qMetaTypeId<SequenceElementType>()) \
253 , object(obj), propertyIndex(propIdx) \
256 QV8##SequenceElementTypeName##SequenceResource(QV8Engine *engine, const SequenceType &value) \
257 : QV8SequenceResource(engine, QV8SequenceResource::Copy, #SequenceType, qMetaTypeId<SequenceType>(), qMetaTypeId<SequenceElementType>()) \
258 , object(0), propertyIndex(-1), c(value) \
261 ~QV8##SequenceElementTypeName##SequenceResource() \
264 static QVariant toVariant(QV8Engine *e, v8::Handle<v8::Array> array, uint32_t length, bool *succeeded) \
267 list.reserve(length); \
268 for (uint32_t ii = 0; ii < length; ++ii) { \
269 list.append(ConversionFromV8fn(e, array->Get(ii))); \
272 return QVariant::fromValue<SequenceType>(list); \
274 QVariant toVariant() \
276 if (objectType == QV8SequenceResource::Reference) { \
281 return QVariant::fromValue<SequenceType>(c); \
283 bool isEqual(const QV8SequenceResource *v) \
285 /* Note: two different sequences can never be equal (even if they */ \
286 /* contain the same elements in the same order) in order to */ \
287 /* maintain JavaScript semantics. However, if they both reference */ \
288 /* the same QObject+propertyIndex, they are equal. */ \
289 if (objectType == QV8SequenceResource::Reference && v->objectType == QV8SequenceResource::Reference) { \
290 if (sequenceMetaTypeId == v->sequenceMetaTypeId) { \
291 const QV8##SequenceElementTypeName##SequenceResource *rhs = static_cast<const QV8##SequenceElementTypeName##SequenceResource *>(v); \
292 return (object != 0 && object == rhs->object && propertyIndex == rhs->propertyIndex); \
294 } else if (objectType == QV8SequenceResource::Copy && v->objectType == QV8SequenceResource::Copy) { \
295 if (sequenceMetaTypeId == v->sequenceMetaTypeId) { \
296 const QV8##SequenceElementTypeName##SequenceResource *rhs = static_cast<const QV8##SequenceElementTypeName##SequenceResource *>(v); \
297 return (this == rhs); \
302 quint32 lengthGetter() \
304 if (objectType == QV8SequenceResource::Reference) { \
309 return static_cast<quint32>(c.count()); \
311 void lengthSetter(v8::Handle<v8::Value> value) \
313 /* Get the new required length */ \
314 if (value.IsEmpty() || !value->IsUint32()) \
316 quint32 newLength = value->Uint32Value(); \
317 /* Qt containers have int (rather than uint) allowable indexes. */ \
318 if (newLength > INT_MAX) { \
319 generateWarning(engine, QLatin1String("Index out of range during length set")); \
322 /* Read the sequence from the QObject property if we're a reference */ \
323 if (objectType == QV8SequenceResource::Reference) { \
328 /* Determine whether we need to modify the sequence */ \
329 qint32 newCount = static_cast<qint32>(newLength); \
330 qint32 count = c.count(); \
331 if (newCount == count) { \
333 } else if (newCount > count) { \
334 /* according to ECMA262r3 we need to insert */ \
335 /* undefined values increasing length to newLength. */ \
336 /* We cannot, so we insert default-values instead. */ \
337 c.reserve(newCount); \
338 while (newCount > count++) { \
339 c.append(DefaultValue); \
342 /* according to ECMA262r3 we need to remove */ \
343 /* elements until the sequence is the required length. */ \
344 while (newCount < count) { \
349 /* write back if required. */ \
350 if (objectType == QV8SequenceResource::Reference) { \
351 /* write back. already checked that object is non-null, so skip that check here. */ \
356 v8::Handle<v8::Value> indexedSetter(quint32 index, v8::Handle<v8::Value> value) \
358 /* Qt containers have int (rather than uint) allowable indexes. */ \
359 if (index > INT_MAX) { \
360 generateWarning(engine, QLatin1String("Index out of range during indexed set")); \
361 return v8::Undefined(); \
363 if (objectType == QV8SequenceResource::Reference) { \
365 return v8::Undefined(); \
368 /* modify the sequence */ \
369 SequenceElementType elementValue = ConversionFromV8fn(engine, value); \
370 qint32 count = c.count(); \
371 qint32 signedIdx = static_cast<qint32>(index); \
372 if (signedIdx == count) { \
373 c.append(elementValue); \
374 } else if (signedIdx < count) { \
375 c[index] = elementValue; \
377 /* according to ECMA262r3 we need to insert */ \
378 /* the value at the given index, increasing length to index+1. */ \
379 c.reserve(signedIdx + 1); \
380 while (signedIdx > count++) { \
381 c.append(DefaultValue); \
383 c.append(elementValue); \
385 /* write back. already checked that object is non-null, so skip that check here. */ \
386 if (objectType == QV8SequenceResource::Reference) \
390 v8::Handle<v8::Value> indexedGetter(quint32 index) \
392 /* Qt containers have int (rather than uint) allowable indexes. */ \
393 if (index > INT_MAX) { \
394 generateWarning(engine, QLatin1String("Index out of range during indexed get")); \
395 return v8::Undefined(); \
397 if (objectType == QV8SequenceResource::Reference) { \
399 return v8::Undefined(); \
402 qint32 count = c.count(); \
403 qint32 signedIdx = static_cast<qint32>(index); \
404 if (signedIdx < count) \
405 return ConversionToV8fn(engine, c.at(signedIdx)); \
406 return v8::Undefined(); \
408 v8::Handle<v8::Boolean> indexedDeleter(quint32 index) \
410 /* Qt containers have int (rather than uint) allowable indexes. */ \
411 if (index > INT_MAX) \
412 return v8::Boolean::New(false); \
413 /* Read in the sequence from the QObject */ \
414 if (objectType == QV8SequenceResource::Reference) { \
416 return v8::Boolean::New(false); \
419 qint32 signedIdx = static_cast<qint32>(index); \
420 if (signedIdx < c.count()) { \
421 /* according to ECMA262r3 it should be Undefined, */ \
422 /* but we cannot, so we insert a default-value instead. */ \
423 c.replace(signedIdx, DefaultValue); \
424 if (objectType == QV8SequenceResource::Reference) { \
425 /* write back. already checked that object is non-null, so skip that check here. */ \
428 return v8::Boolean::New(true); \
430 return v8::Boolean::New(false); \
432 v8::Handle<v8::Array> indexedEnumerator() \
434 if (objectType == QV8SequenceResource::Reference) { \
436 return v8::Handle<v8::Array>(); \
439 qint32 count = c.count(); \
440 v8::Local<v8::Array> retn = v8::Array::New(count); \
441 for (qint32 i = 0; i < count; ++i) { \
442 retn->Set(static_cast<quint32>(i), v8::Integer::NewFromUnsigned(static_cast<quint32>(i))); \
446 v8::Handle<v8::Value> toString() \
448 if (objectType == QV8SequenceResource::Reference) { \
450 return v8::Undefined(); \
454 qint32 count = c.count(); \
455 for (qint32 i = 0; i < count; ++i) { \
456 str += QString(QLatin1String("%1,")).arg(ToStringfn(engine, c[i])); \
459 return engine->toString(str); \
461 void loadReference() \
464 Q_ASSERT(objectType == QV8SequenceResource::Reference); \
465 void *a[] = { &c, 0 }; \
466 QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyIndex, a); \
468 void storeReference() \
471 Q_ASSERT(objectType == QV8SequenceResource::Reference); \
473 QQmlPropertyPrivate::WriteFlags flags = \
474 QQmlPropertyPrivate::DontRemoveBinding; \
475 void *a[] = { &c, 0, &status, &flags }; \
476 QMetaObject::metacall(object, QMetaObject::WriteProperty, propertyIndex, a); \
478 class CompareFunctor \
481 CompareFunctor(QV8Engine *engine, v8::Handle<v8::Function> f) : jsFn(f), eng(engine) {} \
482 bool operator()(SequenceElementType e0, SequenceElementType e1) \
484 v8::Handle<v8::Value> argv[2] = { eng->fromVariant(e0), eng->fromVariant(e1) }; \
485 v8::Handle<v8::Value> compareValue = jsFn->Call(eng->global(), 2, argv); \
486 return compareValue->NumberValue() < 0; \
489 v8::Handle<v8::Function> jsFn; \
492 void sort(v8::Handle<v8::Function> jsCompareFunction) \
494 CompareFunctor cf(engine, jsCompareFunction); \
495 qSort(c.begin(), c.end(), cf); \
498 QQmlGuard<QObject> object; \
503 #define GENERATE_QML_SEQUENCE_TYPE_RESOURCE(ElementType, ElementTypeName, SequenceType, DefaultValue) \
504 QML_SEQUENCE_TYPE_RESOURCE(ElementType, ElementTypeName, SequenceType, DefaultValue, convert##ElementTypeName##ToV8Value, convertV8ValueTo##ElementTypeName, convert##ElementTypeName##ToString)
506 FOREACH_QML_SEQUENCE_TYPE(GENERATE_QML_SEQUENCE_TYPE_RESOURCE)
507 #undef GENERATE_QML_SEQUENCE_TYPE_RESOURCE
508 #undef QML_SEQUENCE_TYPE_RESOURCE
512 #endif // QV8SEQUENCEWRAPPER_P_P_H