Introduce more generic fast property handling
[profile/ivi/qtdeclarative.git] / src / declarative / qml / v8 / qv8sequencewrapper_p_p.h
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #ifndef QV8SEQUENCEWRAPPER_P_P_H
43 #define QV8SEQUENCEWRAPPER_P_P_H
44
45 //
46 //  W A R N I N G
47 //  -------------
48 //
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.
52 //
53 // We mean it.
54 //
55
56 #include <private/qdeclarativeengine_p.h>
57 #include <private/qdeclarativemetatype_p.h>
58
59 QT_BEGIN_NAMESPACE
60
61 /*!
62   \internal
63   \class QV8SequenceResource
64   \brief The abstract base class of the external resource used in sequence type objects
65
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.
71  */
72 class QV8SequenceResource : public QV8ObjectResource
73 {
74     V8_RESOURCE_TYPE(SequenceType);
75
76 public:
77     virtual ~QV8SequenceResource() {}
78
79     enum ObjectType { Reference, Copy };
80
81     virtual QVariant toVariant() = 0;
82     virtual bool isEqual(const QV8SequenceResource *v) = 0;
83
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
92     ObjectType objectType;
93     QByteArray typeName;
94     int sequenceMetaTypeId;
95     int elementMetaTypeId;
96
97 protected:
98     QV8SequenceResource(QV8Engine *engine, ObjectType type, const QByteArray &name, int sequenceId, int elementId)
99         : QV8ObjectResource(engine), objectType(type), typeName(name), sequenceMetaTypeId(sequenceId), elementMetaTypeId(elementId)
100     {
101     }
102 };
103
104 static int convertV8ValueToInt(QV8Engine *, v8::Handle<v8::Value> v)
105 {
106     return v->Int32Value();
107 }
108
109 static v8::Handle<v8::Value> convertIntToV8Value(QV8Engine *, int v)
110 {
111     return v8::Integer::New(v);
112 }
113
114 static QString convertIntToString(QV8Engine *, int v)
115 {
116     return QString::number(v);
117 }
118
119 static qreal convertV8ValueToReal(QV8Engine *, v8::Handle<v8::Value> v)
120 {
121     return v->NumberValue();
122 }
123
124 static v8::Handle<v8::Value> convertRealToV8Value(QV8Engine *, qreal v)
125 {
126     return v8::Number::New(v);
127 }
128
129 static QString convertRealToString(QV8Engine *, qreal v)
130 {
131     return QString::number(v);
132 }
133
134 static bool convertV8ValueToBool(QV8Engine *, v8::Handle<v8::Value> v)
135 {
136     return v->BooleanValue();
137 }
138
139 static v8::Handle<v8::Value> convertBoolToV8Value(QV8Engine *, bool v)
140 {
141     return v8::Boolean::New(v);
142 }
143
144 static QString convertBoolToString(QV8Engine *, bool v)
145 {
146     if (v)
147         return QLatin1String("true");
148     return QLatin1String("false");
149 }
150
151 static QString convertV8ValueToString(QV8Engine *e, v8::Handle<v8::Value> v)
152 {
153     return e->toString(v->ToString());
154 }
155
156 static v8::Handle<v8::Value> convertStringToV8Value(QV8Engine *e, const QString &v)
157 {
158     return e->toString(v);
159 }
160
161 static QString convertStringToString(QV8Engine *, const QString &v)
162 {
163     return v;
164 }
165
166 static QString convertV8ValueToQString(QV8Engine *e, v8::Handle<v8::Value> v)
167 {
168     return e->toString(v->ToString());
169 }
170
171 static v8::Handle<v8::Value> convertQStringToV8Value(QV8Engine *e, const QString &v)
172 {
173     return e->toString(v);
174 }
175
176 static QString convertQStringToString(QV8Engine *, const QString &v)
177 {
178     return v;
179 }
180
181 static QUrl convertV8ValueToUrl(QV8Engine *e, v8::Handle<v8::Value> v)
182 {
183     return QUrl(e->toString(v->ToString()));
184 }
185
186 static v8::Handle<v8::Value> convertUrlToV8Value(QV8Engine *e, const QUrl &v)
187 {
188     return e->toString(v.toString());
189 }
190
191 static QString convertUrlToString(QV8Engine *, const QUrl &v)
192 {
193     return v.toString();
194 }
195
196
197 /*
198   \internal
199   \class QV8<Type>SequenceResource
200   \brief The external resource used in sequence type objects
201
202   Every sequence type object returned by QV8SequenceWrapper::newSequence() has
203   a QV8<Type>SequenceResource which contains a property index and a pointer
204   to the object which contains the property.
205
206   Every sequence type object returned by QV8SequenceWrapper::fromVariant() has
207   a QV8<Type>SequenceResource which contains a copy of the sequence value.
208   Operations on the sequence are implemented directly in terms of that sequence data.
209
210   There exists one QV8<Type>SequenceResource instance for every JavaScript Object
211   (sequence) instance returned from QV8SequenceWrapper::newSequence() or
212   QV8SequenceWrapper::fromVariant().
213  */
214
215 //  F(elementType, elementTypeName, sequenceType, defaultValue)
216 #define FOREACH_QML_SEQUENCE_TYPE(F) \
217     F(int, Int, QList<int>, 0) \
218     F(qreal, Real, QList<qreal>, 0.0) \
219     F(bool, Bool, QList<bool>, false) \
220     F(QString, String, QList<QString>, QString()) \
221     F(QString, QString, QStringList, QString()) \
222     F(QUrl, Url, QList<QUrl>, QUrl())
223
224 #define QML_SEQUENCE_TYPE_RESOURCE(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue, ConversionToV8fn, ConversionFromV8fn, ToStringfn) \
225     QT_END_NAMESPACE \
226     Q_DECLARE_METATYPE(SequenceType) \
227     QT_BEGIN_NAMESPACE \
228     class QV8##SequenceElementTypeName##SequenceResource : public QV8SequenceResource { \
229         public:\
230             QV8##SequenceElementTypeName##SequenceResource(QV8Engine *engine, QObject *obj, int propIdx) \
231                 : QV8SequenceResource(engine, QV8SequenceResource::Reference, #SequenceType, qMetaTypeId<SequenceType>(), qMetaTypeId<SequenceElementType>()) \
232                 , object(obj), propertyIndex(propIdx) \
233             { \
234             } \
235             QV8##SequenceElementTypeName##SequenceResource(QV8Engine *engine, const SequenceType &value) \
236                 : QV8SequenceResource(engine, QV8SequenceResource::Copy, #SequenceType, qMetaTypeId<SequenceType>(), qMetaTypeId<SequenceElementType>()) \
237                 , object(0), propertyIndex(-1), c(value) \
238             { \
239             } \
240             ~QV8##SequenceElementTypeName##SequenceResource() \
241             { \
242             } \
243             static QVariant toVariant(QV8Engine *e, v8::Handle<v8::Array> array, uint32_t length, bool *succeeded) \
244             { \
245                 SequenceType list; \
246                 for (uint32_t ii = 0; ii < length; ++ii) { \
247                     list.append(ConversionFromV8fn(e, array->Get(ii))); \
248                 } \
249                 *succeeded = true; \
250                 return QVariant::fromValue<SequenceType>(list); \
251             } \
252             QVariant toVariant() \
253             { \
254                 if (objectType == QV8SequenceResource::Reference) { \
255                     if (!object) \
256                         return QVariant(); \
257                     loadReference(); \
258                 } \
259                 return QVariant::fromValue<SequenceType>(c); \
260             } \
261             bool isEqual(const QV8SequenceResource *v) \
262             { \
263                 /* Note: two different sequences can never be equal (even if they  */ \
264                 /* contain the same elements in the same order) in order to        */ \
265                 /* maintain JavaScript semantics.  However, if they both reference */ \
266                 /* the same QObject+propertyIndex, they are equal.                 */ \
267                 if (objectType == QV8SequenceResource::Reference && v->objectType == QV8SequenceResource::Reference) { \
268                     if (sequenceMetaTypeId == v->sequenceMetaTypeId) { \
269                         const QV8##SequenceElementTypeName##SequenceResource *rhs = static_cast<const QV8##SequenceElementTypeName##SequenceResource *>(v); \
270                         return (object != 0 && object == rhs->object && propertyIndex == rhs->propertyIndex); \
271                     } \
272                 } else if (objectType == QV8SequenceResource::Copy && v->objectType == QV8SequenceResource::Copy) { \
273                     if (sequenceMetaTypeId == v->sequenceMetaTypeId) { \
274                         const QV8##SequenceElementTypeName##SequenceResource *rhs = static_cast<const QV8##SequenceElementTypeName##SequenceResource *>(v); \
275                         return (this == rhs); \
276                     } \
277                 } \
278                 return false; \
279             } \
280             quint32 lengthGetter() \
281             { \
282                 if (objectType == QV8SequenceResource::Reference) { \
283                     if (!object) \
284                         return 0; \
285                     loadReference(); \
286                 } \
287                 return c.count(); \
288             } \
289             void lengthSetter(v8::Handle<v8::Value> value) \
290             { \
291                 /* Get the new required length */ \
292                 if (value.IsEmpty() || !value->IsUint32()) \
293                     return; \
294                 quint32 newLength = value->Uint32Value(); \
295                 /* Read the sequence from the QObject property if we're a reference */ \
296                 if (objectType == QV8SequenceResource::Reference) { \
297                     if (!object) \
298                         return; \
299                     void *a[] = { &c, 0 }; \
300                     QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyIndex, a); \
301                 } \
302                 /* Determine whether we need to modify the sequence */ \
303                 quint32 count = c.count(); \
304                 if (newLength == count) { \
305                     return; \
306                 } else if (newLength > count) { \
307                     /* according to ECMA262r3 we need to insert */ \
308                     /* undefined values increasing length to newLength. */ \
309                     /* We cannot, so we insert default-values instead. */ \
310                     while (newLength > count++) { \
311                         c.append(DefaultValue); \
312                     } \
313                 } else { \
314                     /* according to ECMA262r3 we need to remove */ \
315                     /* elements until the sequence is the required length. */ \
316                     while (newLength < count) { \
317                         count--; \
318                         c.removeAt(count); \
319                     } \
320                 } \
321                 /* write back if required. */ \
322                 if (objectType == QV8SequenceResource::Reference) { \
323                     /* write back.  already checked that object is non-null, so skip that check here. */ \
324                     int status = -1; \
325                     QDeclarativePropertyPrivate::WriteFlags flags = QDeclarativePropertyPrivate::DontRemoveBinding; \
326                     void *a[] = { &c, 0, &status, &flags }; \
327                     QMetaObject::metacall(object, QMetaObject::WriteProperty, propertyIndex, a); \
328                 } \
329                 return; \
330             } \
331             v8::Handle<v8::Value> indexedSetter(quint32 index, v8::Handle<v8::Value> value) \
332             { \
333                 if (objectType == QV8SequenceResource::Reference) { \
334                     if (!object) \
335                         return v8::Undefined(); \
336                     loadReference(); \
337                 } \
338                 /* modify the sequence */ \
339                 SequenceElementType elementValue = ConversionFromV8fn(engine, value); \
340                 quint32 count = c.count(); \
341                 if (index == count) { \
342                     c.append(elementValue); \
343                 } else if (index < count) { \
344                     c[index] = elementValue; \
345                 } else { \
346                     /* according to ECMA262r3 we need to insert */ \
347                     /* the value at the given index, increasing length to index+1. */ \
348                     while (index > count++) { \
349                         c.append(DefaultValue); \
350                     } \
351                     c.append(elementValue); \
352                 } \
353                 /* write back.  already checked that object is non-null, so skip that check here. */ \
354                 if (objectType == QV8SequenceResource::Reference) \
355                     storeReference(); \
356                 return value; \
357             } \
358             v8::Handle<v8::Value> indexedGetter(quint32 index) \
359             { \
360                 if (objectType == QV8SequenceResource::Reference) { \
361                     if (!object) \
362                         return v8::Undefined(); \
363                     loadReference(); \
364                 } \
365                 quint32 count = c.count(); \
366                 if (index < count) \
367                     return ConversionToV8fn(engine, c.at(index)); \
368                 return v8::Undefined(); \
369             } \
370             v8::Handle<v8::Boolean> indexedDeleter(quint32 index) \
371             { \
372                 if (objectType == QV8SequenceResource::Reference) { \
373                     if (!object) \
374                         return v8::Boolean::New(false); \
375                     void *a[] = { &c, 0 }; \
376                     QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyIndex, a); \
377                 } \
378                 if (index < c.count()) { \
379                     /* according to ECMA262r3 it should be Undefined, */ \
380                     /* but we cannot, so we insert a default-value instead. */ \
381                     c.replace(index, DefaultValue); \
382                     if (objectType == QV8SequenceResource::Reference) { \
383                         /* write back.  already checked that object is non-null, so skip that check here. */ \
384                         int status = -1; \
385                         QDeclarativePropertyPrivate::WriteFlags flags = QDeclarativePropertyPrivate::DontRemoveBinding; \
386                         void *a[] = { &c, 0, &status, &flags }; \
387                         QMetaObject::metacall(object, QMetaObject::WriteProperty, propertyIndex, a); \
388                     } \
389                     return v8::Boolean::New(true); \
390                 } \
391                 return v8::Boolean::New(false); \
392             } \
393             v8::Handle<v8::Array> indexedEnumerator() \
394             { \
395                 if (objectType == QV8SequenceResource::Reference) { \
396                     if (!object) \
397                         return v8::Handle<v8::Array>(); \
398                     loadReference(); \
399                 } \
400                 quint32 count = c.count(); \
401                 v8::Local<v8::Array> retn = v8::Array::New(count); \
402                 for (quint32 i = 0; i < count; ++i) { \
403                     retn->Set(i, v8::Integer::NewFromUnsigned(i)); \
404                 } \
405                 return retn; \
406             } \
407             v8::Handle<v8::Value> toString() \
408             { \
409                 if (objectType == QV8SequenceResource::Reference) { \
410                     if (!object) \
411                         return v8::Undefined(); \
412                     loadReference(); \
413                 } \
414                 QString str; \
415                 quint32 count = c.count(); \
416                 for (quint32 i = 0; i < count; ++i) { \
417                     str += QString(QLatin1String("%1,")).arg(ToStringfn(engine, c[i])); \
418                 } \
419                 str.chop(1); \
420                 return engine->toString(str); \
421             } \
422             void loadReference() \
423             { \
424                 Q_ASSERT(object); \
425                 Q_ASSERT(objectType == QV8SequenceResource::Reference); \
426                 void *a[] = { &c, 0 }; \
427                 QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyIndex, a); \
428             } \
429             void storeReference() \
430             { \
431                 Q_ASSERT(object); \
432                 Q_ASSERT(objectType == QV8SequenceResource::Reference); \
433                 int status = -1; \
434                 QDeclarativePropertyPrivate::WriteFlags flags = \
435                     QDeclarativePropertyPrivate::DontRemoveBinding; \
436                 void *a[] = { &c, 0, &status, &flags }; \
437                 QMetaObject::metacall(object, QMetaObject::WriteProperty, propertyIndex, a); \
438             } \
439         private: \
440             QDeclarativeGuard<QObject> object; \
441             int propertyIndex; \
442             SequenceType c; \
443     };
444
445 #define GENERATE_QML_SEQUENCE_TYPE_RESOURCE(ElementType, ElementTypeName, SequenceType, DefaultValue) \
446     QML_SEQUENCE_TYPE_RESOURCE(ElementType, ElementTypeName, SequenceType, DefaultValue, convert##ElementTypeName##ToV8Value, convertV8ValueTo##ElementTypeName, convert##ElementTypeName##ToString)
447
448 FOREACH_QML_SEQUENCE_TYPE(GENERATE_QML_SEQUENCE_TYPE_RESOURCE)
449 #undef GENERATE_QML_SEQUENCE_TYPE_RESOURCE
450 #undef QML_SEQUENCE_TYPE_RESOURCE
451
452 QT_END_NAMESPACE
453
454 #endif // QV8SEQUENCEWRAPPER_P_P_H