Change copyrights from Nokia to Digia
[profile/ivi/qtdeclarative.git] / src / qml / qml / v8 / qv8sequencewrapper_p_p.h
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
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.
16 **
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.
24 **
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.
28 **
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.
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/qqmlengine_p.h>
57 #include <private/qqmlmetatype_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     virtual void sort(v8::Handle<v8::Function> comparer) = 0;
92
93     ObjectType objectType;
94     QByteArray typeName;
95     int sequenceMetaTypeId;
96     int elementMetaTypeId;
97
98 protected:
99     QV8SequenceResource(QV8Engine *engine, ObjectType type, const QByteArray &name, int sequenceId, int elementId)
100         : QV8ObjectResource(engine), objectType(type), typeName(name), sequenceMetaTypeId(sequenceId), elementMetaTypeId(elementId)
101     {
102     }
103 };
104
105 // helper function to generate valid warnings if errors occur during sequence operations.
106 static void generateWarning(QV8Engine *engine, const QString& description)
107 {
108     if (!engine)
109         return;
110     v8::Local<v8::StackTrace> currStack = v8::StackTrace::CurrentStackTrace(1);
111     if (currStack.IsEmpty())
112         return;
113     v8::Local<v8::StackFrame> currFrame = currStack->GetFrame(0);
114     if (currFrame.IsEmpty())
115         return;
116
117     QQmlError retn;
118     retn.setDescription(description);
119     retn.setLine(currFrame->GetLineNumber());
120     retn.setUrl(QUrl(engine->toString(currFrame->GetScriptName())));
121     QQmlEnginePrivate::warning(engine->engine(), retn);
122 }
123
124
125 static int convertV8ValueToInt(QV8Engine *, v8::Handle<v8::Value> v)
126 {
127     return v->Int32Value();
128 }
129
130 static v8::Handle<v8::Value> convertIntToV8Value(QV8Engine *, int v)
131 {
132     return v8::Integer::New(v);
133 }
134
135 static QString convertIntToString(QV8Engine *, int v)
136 {
137     return QString::number(v);
138 }
139
140 static qreal convertV8ValueToReal(QV8Engine *, v8::Handle<v8::Value> v)
141 {
142     return v->NumberValue();
143 }
144
145 static v8::Handle<v8::Value> convertRealToV8Value(QV8Engine *, qreal v)
146 {
147     return v8::Number::New(v);
148 }
149
150 static QString convertRealToString(QV8Engine *, qreal v)
151 {
152     return QString::number(v);
153 }
154
155 static bool convertV8ValueToBool(QV8Engine *, v8::Handle<v8::Value> v)
156 {
157     return v->BooleanValue();
158 }
159
160 static v8::Handle<v8::Value> convertBoolToV8Value(QV8Engine *, bool v)
161 {
162     return v8::Boolean::New(v);
163 }
164
165 static QString convertBoolToString(QV8Engine *, bool v)
166 {
167     if (v)
168         return QLatin1String("true");
169     return QLatin1String("false");
170 }
171
172 static QString convertV8ValueToString(QV8Engine *e, v8::Handle<v8::Value> v)
173 {
174     return e->toString(v->ToString());
175 }
176
177 static v8::Handle<v8::Value> convertStringToV8Value(QV8Engine *e, const QString &v)
178 {
179     return e->toString(v);
180 }
181
182 static QString convertStringToString(QV8Engine *, const QString &v)
183 {
184     return v;
185 }
186
187 static QString convertV8ValueToQString(QV8Engine *e, v8::Handle<v8::Value> v)
188 {
189     return e->toString(v->ToString());
190 }
191
192 static v8::Handle<v8::Value> convertQStringToV8Value(QV8Engine *e, const QString &v)
193 {
194     return e->toString(v);
195 }
196
197 static QString convertQStringToString(QV8Engine *, const QString &v)
198 {
199     return v;
200 }
201
202 static QUrl convertV8ValueToUrl(QV8Engine *e, v8::Handle<v8::Value> v)
203 {
204     return QUrl(e->toString(v->ToString()));
205 }
206
207 static v8::Handle<v8::Value> convertUrlToV8Value(QV8Engine *e, const QUrl &v)
208 {
209     return e->toString(QLatin1String(v.toEncoded().data()));
210 }
211
212 static QString convertUrlToString(QV8Engine *, const QUrl &v)
213 {
214     return v.toString();
215 }
216
217
218 /*
219   \internal
220   \class QV8<Type>SequenceResource
221   \brief The external resource used in sequence type objects
222
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.
226
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.
230
231   There exists one QV8<Type>SequenceResource instance for every JavaScript Object
232   (sequence) instance returned from QV8SequenceWrapper::newSequence() or
233   QV8SequenceWrapper::fromVariant().
234  */
235
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())
244
245 #define QML_SEQUENCE_TYPE_RESOURCE(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue, ConversionToV8fn, ConversionFromV8fn, ToStringfn) \
246     QT_END_NAMESPACE \
247     Q_DECLARE_METATYPE(SequenceType) \
248     QT_BEGIN_NAMESPACE \
249     class QV8##SequenceElementTypeName##SequenceResource : public QV8SequenceResource { \
250         public:\
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) \
254             { \
255             } \
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) \
259             { \
260             } \
261             ~QV8##SequenceElementTypeName##SequenceResource() \
262             { \
263             } \
264             static QVariant toVariant(QV8Engine *e, v8::Handle<v8::Array> array, uint32_t length, bool *succeeded) \
265             { \
266                 SequenceType list; \
267                 list.reserve(length); \
268                 for (uint32_t ii = 0; ii < length; ++ii) { \
269                     list.append(ConversionFromV8fn(e, array->Get(ii))); \
270                 } \
271                 *succeeded = true; \
272                 return QVariant::fromValue<SequenceType>(list); \
273             } \
274             QVariant toVariant() \
275             { \
276                 if (objectType == QV8SequenceResource::Reference) { \
277                     if (!object) \
278                         return QVariant(); \
279                     loadReference(); \
280                 } \
281                 return QVariant::fromValue<SequenceType>(c); \
282             } \
283             bool isEqual(const QV8SequenceResource *v) \
284             { \
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); \
293                     } \
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); \
298                     } \
299                 } \
300                 return false; \
301             } \
302             quint32 lengthGetter() \
303             { \
304                 if (objectType == QV8SequenceResource::Reference) { \
305                     if (!object) \
306                         return 0; \
307                     loadReference(); \
308                 } \
309                 return static_cast<quint32>(c.count()); \
310             } \
311             void lengthSetter(v8::Handle<v8::Value> value) \
312             { \
313                 /* Get the new required length */ \
314                 if (value.IsEmpty() || !value->IsUint32()) \
315                     return; \
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")); \
320                     return; \
321                 } \
322                 /* Read the sequence from the QObject property if we're a reference */ \
323                 if (objectType == QV8SequenceResource::Reference) { \
324                     if (!object) \
325                         return; \
326                     loadReference(); \
327                 } \
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) { \
332                     return; \
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); \
340                     } \
341                 } else { \
342                     /* according to ECMA262r3 we need to remove */ \
343                     /* elements until the sequence is the required length. */ \
344                     while (newCount < count) { \
345                         count--; \
346                         c.removeAt(count); \
347                     } \
348                 } \
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. */ \
352                     storeReference(); \
353                 } \
354                 return; \
355             } \
356             v8::Handle<v8::Value> indexedSetter(quint32 index, v8::Handle<v8::Value> value) \
357             { \
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(); \
362                 } \
363                 if (objectType == QV8SequenceResource::Reference) { \
364                     if (!object) \
365                         return v8::Undefined(); \
366                     loadReference(); \
367                 } \
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; \
376                 } else { \
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); \
382                     } \
383                     c.append(elementValue); \
384                 } \
385                 /* write back.  already checked that object is non-null, so skip that check here. */ \
386                 if (objectType == QV8SequenceResource::Reference) \
387                     storeReference(); \
388                 return value; \
389             } \
390             v8::Handle<v8::Value> indexedGetter(quint32 index) \
391             { \
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(); \
396                 } \
397                 if (objectType == QV8SequenceResource::Reference) { \
398                     if (!object) \
399                         return v8::Undefined(); \
400                     loadReference(); \
401                 } \
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(); \
407             } \
408             v8::Handle<v8::Boolean> indexedDeleter(quint32 index) \
409             { \
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) { \
415                     if (!object) \
416                         return v8::Boolean::New(false); \
417                     loadReference(); \
418                 } \
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. */ \
426                         storeReference(); \
427                     } \
428                     return v8::Boolean::New(true); \
429                 } \
430                 return v8::Boolean::New(false); \
431             } \
432             v8::Handle<v8::Array> indexedEnumerator() \
433             { \
434                 if (objectType == QV8SequenceResource::Reference) { \
435                     if (!object) \
436                         return v8::Handle<v8::Array>(); \
437                     loadReference(); \
438                 } \
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))); \
443                 } \
444                 return retn; \
445             } \
446             v8::Handle<v8::Value> toString() \
447             { \
448                 if (objectType == QV8SequenceResource::Reference) { \
449                     if (!object) \
450                         return v8::Undefined(); \
451                     loadReference(); \
452                 } \
453                 QString str; \
454                 qint32 count = c.count(); \
455                 for (qint32 i = 0; i < count; ++i) { \
456                     str += QString(QLatin1String("%1,")).arg(ToStringfn(engine, c[i])); \
457                 } \
458                 str.chop(1); \
459                 return engine->toString(str); \
460             } \
461             void loadReference() \
462             { \
463                 Q_ASSERT(object); \
464                 Q_ASSERT(objectType == QV8SequenceResource::Reference); \
465                 void *a[] = { &c, 0 }; \
466                 QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyIndex, a); \
467             } \
468             void storeReference() \
469             { \
470                 Q_ASSERT(object); \
471                 Q_ASSERT(objectType == QV8SequenceResource::Reference); \
472                 int status = -1; \
473                 QQmlPropertyPrivate::WriteFlags flags = \
474                     QQmlPropertyPrivate::DontRemoveBinding; \
475                 void *a[] = { &c, 0, &status, &flags }; \
476                 QMetaObject::metacall(object, QMetaObject::WriteProperty, propertyIndex, a); \
477             } \
478             class CompareFunctor \
479             { \
480             public: \
481                 CompareFunctor(QV8Engine *engine, v8::Handle<v8::Function> f) : jsFn(f), eng(engine) {} \
482                 bool operator()(SequenceElementType e0, SequenceElementType e1) \
483                 { \
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; \
487                 } \
488             private: \
489                 v8::Handle<v8::Function> jsFn; \
490                 QV8Engine *eng; \
491             }; \
492             void sort(v8::Handle<v8::Function> jsCompareFunction) \
493             { \
494                 CompareFunctor cf(engine, jsCompareFunction); \
495                 qSort(c.begin(), c.end(), cf); \
496             } \
497         private: \
498             QQmlGuard<QObject> object; \
499             int propertyIndex; \
500             SequenceType c; \
501     };
502
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)
505
506 FOREACH_QML_SEQUENCE_TYPE(GENERATE_QML_SEQUENCE_TYPE_RESOURCE)
507 #undef GENERATE_QML_SEQUENCE_TYPE_RESOURCE
508 #undef QML_SEQUENCE_TYPE_RESOURCE
509
510 QT_END_NAMESPACE
511
512 #endif // QV8SEQUENCEWRAPPER_P_P_H