6ff47d7b5735a0f17406c4005e16e7a13d6236d5
[profile/ivi/qtdeclarative.git] / src / declarative / qml / qdeclarativepropertycache.cpp
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 #include "private/qdeclarativepropertycache_p.h"
43
44 #include "private/qdeclarativeengine_p.h"
45 #include "private/qdeclarativebinding_p.h"
46 #include "private/qv8engine_p.h"
47
48 #include <private/qmetaobject_p.h>
49
50 #include <QtCore/qdebug.h>
51
52 Q_DECLARE_METATYPE(QScriptValue)
53 Q_DECLARE_METATYPE(QDeclarativeV8Handle);
54
55 QT_BEGIN_NAMESPACE
56
57 // Flags that do *NOT* depend on the property's QMetaProperty::userType() and thus are quick
58 // to load
59 static QDeclarativePropertyCache::Data::Flags fastFlagsForProperty(const QMetaProperty &p)
60 {
61     QDeclarativePropertyCache::Data::Flags flags;
62
63     if (p.isConstant())
64         flags |= QDeclarativePropertyCache::Data::IsConstant;
65     if (p.isWritable())
66         flags |= QDeclarativePropertyCache::Data::IsWritable;
67     if (p.isResettable())
68         flags |= QDeclarativePropertyCache::Data::IsResettable;
69     if (p.isFinal())
70         flags |= QDeclarativePropertyCache::Data::IsFinal;
71     if (p.isEnumType())
72         flags |= QDeclarativePropertyCache::Data::IsEnumType;
73
74     return flags;
75 }
76
77 // Flags that do depend on the property's QMetaProperty::userType() and thus are slow to 
78 // load
79 static QDeclarativePropertyCache::Data::Flags flagsForPropertyType(int propType, QDeclarativeEngine *engine)
80 {
81     QDeclarativePropertyCache::Data::Flags flags;
82
83     if (propType < QMetaType::User && propType != QMetaType::QObjectStar && propType != QMetaType::QWidgetStar) {
84     } else if (propType == qMetaTypeId<QDeclarativeBinding *>()) {
85         flags |= QDeclarativePropertyCache::Data::IsQmlBinding;
86     } else if (propType == qMetaTypeId<QScriptValue>()) {
87         flags |= QDeclarativePropertyCache::Data::IsQScriptValue;
88     } else if (propType == qMetaTypeId<QDeclarativeV8Handle>()) {
89         flags |= QDeclarativePropertyCache::Data::IsV8Handle;
90     } else {
91         QDeclarativeMetaType::TypeCategory cat = 
92             engine ? QDeclarativeEnginePrivate::get(engine)->typeCategory(propType)
93                    : QDeclarativeMetaType::typeCategory(propType);
94
95         if (cat == QDeclarativeMetaType::Object)
96             flags |= QDeclarativePropertyCache::Data::IsQObjectDerived;
97         else if (cat == QDeclarativeMetaType::List)
98             flags |= QDeclarativePropertyCache::Data::IsQList;
99     }
100
101     return flags;
102 }
103
104 QDeclarativePropertyCache::Data::Flags 
105 QDeclarativePropertyCache::Data::flagsForProperty(const QMetaProperty &p, QDeclarativeEngine *engine) 
106 {
107     return fastFlagsForProperty(p) | flagsForPropertyType(p.userType(), engine);
108 }
109
110 void QDeclarativePropertyCache::Data::lazyLoad(const QMetaProperty &p, QDeclarativeEngine *engine)
111 {
112     Q_UNUSED(engine);
113
114     coreIndex = p.propertyIndex();
115     notifyIndex = p.notifySignalIndex();
116     revision = p.revision();
117
118     flags = fastFlagsForProperty(p);
119
120     int type = p.type();
121     if (type == QMetaType::QObjectStar || type == QMetaType::QWidgetStar) {
122         propType = type;
123         flags |= QDeclarativePropertyCache::Data::IsQObjectDerived;
124     } else if (type == QVariant::UserType || type == -1) {
125         propTypeName = p.typeName();
126         flags |= QDeclarativePropertyCache::Data::NotFullyResolved;
127     } else {
128         propType = type;
129     }
130 }
131
132 void QDeclarativePropertyCache::Data::load(const QMetaProperty &p, QDeclarativeEngine *engine)
133 {
134     propType = p.userType();
135     if (QVariant::Type(propType) == QVariant::LastType)
136         propType = qMetaTypeId<QVariant>();
137     coreIndex = p.propertyIndex();
138     notifyIndex = p.notifySignalIndex();
139     flags = fastFlagsForProperty(p) | flagsForPropertyType(propType, engine);
140     revision = p.revision();
141 }
142
143 void QDeclarativePropertyCache::Data::load(const QMetaMethod &m)
144 {
145     coreIndex = m.methodIndex();
146     relatedIndex = -1;
147     flags |= Data::IsFunction;
148     if (m.methodType() == QMetaMethod::Signal)
149         flags |= Data::IsSignal;
150     propType = QVariant::Invalid;
151
152     const char *returnType = m.typeName();
153     if (returnType) 
154         propType = QMetaType::type(returnType);
155
156     const char *signature = m.signature();
157     while (*signature != '(') { Q_ASSERT(*signature != 0); ++signature; }
158
159     ++signature;
160     if (*signature != ')') {
161         flags |= Data::HasArguments;
162         if (0 == ::strcmp(signature, "QDeclarativeV8Function*)")) {
163             flags |= Data::IsV8Function;
164         }
165     }
166
167     revision = m.revision();
168 }
169
170 void QDeclarativePropertyCache::Data::lazyLoad(const QMetaMethod &m)
171 {
172     coreIndex = m.methodIndex();
173     relatedIndex = -1;
174     flags |= Data::IsFunction;
175     if (m.methodType() == QMetaMethod::Signal)
176         flags |= Data::IsSignal;
177     propType = QVariant::Invalid;
178
179     const char *returnType = m.typeName();
180     if (returnType && *returnType) {
181         propTypeName = returnType;
182         flags |= Data::NotFullyResolved;
183     }
184
185     const char *signature = m.signature();
186     while (*signature != '(') { Q_ASSERT(*signature != 0); ++signature; }
187
188     ++signature;
189     if (*signature != ')') {
190         flags |= Data::HasArguments;
191         if (0 == ::strcmp(signature, "QDeclarativeV8Function*)")) {
192             flags |= Data::IsV8Function;
193         }
194     }
195
196     revision = m.revision();
197 }
198
199 /*!
200 Creates a new empty QDeclarativePropertyCache.
201 */
202 QDeclarativePropertyCache::QDeclarativePropertyCache(QDeclarativeEngine *e)
203 : QDeclarativeCleanup(e), engine(e), parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0)
204 {
205     Q_ASSERT(engine);
206 }
207
208 /*!
209 Creates a new QDeclarativePropertyCache of \a metaObject.
210 */
211 QDeclarativePropertyCache::QDeclarativePropertyCache(QDeclarativeEngine *e, const QMetaObject *metaObject)
212 : QDeclarativeCleanup(e), engine(e), parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0)
213 {
214     Q_ASSERT(engine);
215     Q_ASSERT(metaObject);
216
217     update(engine, metaObject);
218 }
219
220 QDeclarativePropertyCache::~QDeclarativePropertyCache()
221 {
222     clear();
223
224     if (parent) parent->release();
225     parent = 0;
226 }
227
228 // This is inherited from QDeclarativeCleanup, so it should only clear the things
229 // that are tied to the specific QDeclarativeEngine.
230 void QDeclarativePropertyCache::clear()
231 {
232     qPersistentDispose(constructor);
233     engine = 0;
234 }
235
236 QDeclarativePropertyCache::Data QDeclarativePropertyCache::create(const QMetaObject *metaObject, 
237                                                                   const QString &property)
238 {
239     Q_ASSERT(metaObject);
240
241     QDeclarativePropertyCache::Data rv;
242     {
243         const QMetaObject *cmo = metaObject;
244         while (cmo) {
245             int idx = metaObject->indexOfProperty(property.toUtf8());
246             if (idx != -1) {
247                 QMetaProperty p = metaObject->property(idx);
248                 if (p.isScriptable()) {
249                     rv.load(metaObject->property(idx));
250                     return rv;
251                 } else {
252                     while (cmo && cmo->propertyOffset() >= idx)
253                         cmo = cmo->superClass();
254                 }
255             } else {
256                 cmo = 0;
257             }
258         }
259     }
260
261     int methodCount = metaObject->methodCount();
262     for (int ii = methodCount - 1; ii >= 3; --ii) { // >=3 to block the destroyed signal and deleteLater() slot
263         QMetaMethod m = metaObject->method(ii);
264         if (m.access() == QMetaMethod::Private)
265             continue;
266         QString methodName = QString::fromUtf8(m.signature());
267
268         int parenIdx = methodName.indexOf(QLatin1Char('('));
269         Q_ASSERT(parenIdx != -1);
270         QStringRef methodNameRef = methodName.leftRef(parenIdx);
271
272         if (methodNameRef == property) {
273             rv.load(m);
274             return rv;
275         }
276     }
277
278     return rv;
279 }
280
281 QDeclarativePropertyCache *QDeclarativePropertyCache::copy() 
282 {
283     QDeclarativePropertyCache *cache = new QDeclarativePropertyCache(engine);
284     cache->parent = this;
285     cache->parent->addref();
286     cache->propertyIndexCacheStart = propertyIndexCache.count() + propertyIndexCacheStart;
287     cache->methodIndexCacheStart = methodIndexCache.count() + methodIndexCacheStart;
288     cache->stringCache = stringCache;
289     cache->allowedRevisionCache = allowedRevisionCache;
290
291     // We specifically do *NOT* copy the constructor
292
293     return cache;
294 }
295
296 void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaObject *metaObject, 
297                                        Data::Flag propertyFlags, Data::Flag methodFlags, Data::Flag signalFlags)
298 {
299     append(engine, metaObject, -1, propertyFlags, methodFlags, signalFlags);
300 }
301
302 void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaObject *metaObject, 
303                                        int revision, 
304                                        Data::Flag propertyFlags, Data::Flag methodFlags, Data::Flag signalFlags)
305 {
306     Q_UNUSED(revision);
307
308     qPersistentDispose(constructor); // Now invalid
309
310     bool dynamicMetaObject = isDynamicMetaObject(metaObject);
311
312     allowedRevisionCache.append(0);
313
314     int methodCount = metaObject->methodCount();
315     // 3 to block the destroyed signal and the deleteLater() slot
316     int methodOffset = qMax(3, metaObject->methodOffset()); 
317
318     methodIndexCache.resize(methodCount - methodIndexCacheStart);
319     for (int ii = methodOffset; ii < methodCount; ++ii) {
320         QMetaMethod m = metaObject->method(ii);
321         if (m.access() == QMetaMethod::Private) 
322             continue;
323
324         // Extract method name
325         const char *signature = m.signature();
326         const char *cptr = signature;
327         while (*cptr != '(') { Q_ASSERT(*cptr != 0); ++cptr; }
328         QString str = dynamicMetaObject?QString::fromUtf8(signature, cptr - signature):
329                                         QString::fromLatin1(signature, cptr - signature);
330         QHashedString methodName(str);
331
332         Data *data = &methodIndexCache[ii - methodIndexCacheStart];
333
334         data->lazyLoad(m);
335         if (data->isSignal())
336             data->flags |= signalFlags;
337         else
338             data->flags |= methodFlags;
339
340         if (!dynamicMetaObject)
341             data->flags |= Data::IsDirect;
342
343         data->metaObjectOffset = allowedRevisionCache.count() - 1;
344
345         if (Data **old = stringCache.value(methodName)) {
346             // We only overload methods in the same class, exactly like C++
347             if ((*old)->flags & Data::IsFunction && (*old)->coreIndex >= methodOffset)
348                 data->relatedIndex = (*old)->coreIndex;
349             data->overrideIndexIsProperty = !bool((*old)->flags & Data::IsFunction);
350             data->overrideIndex = (*old)->coreIndex;
351         }
352
353         stringCache.insert(methodName, data);
354     }
355
356     int propCount = metaObject->propertyCount();
357     int propOffset = metaObject->propertyOffset();
358
359     propertyIndexCache.resize(propCount - propertyIndexCacheStart);
360     for (int ii = propOffset; ii < propCount; ++ii) {
361         QMetaProperty p = metaObject->property(ii);
362         if (!p.isScriptable())
363             continue;
364
365         QString str = dynamicMetaObject?QString::fromUtf8(p.name()):
366                                         QString::fromLatin1(p.name());
367         QHashedString propName(str);
368
369         Data *data = &propertyIndexCache[ii - propertyIndexCacheStart];
370
371         data->lazyLoad(p, engine);
372         data->flags |= propertyFlags;
373
374         if (!dynamicMetaObject) 
375             data->flags |= Data::IsDirect;
376
377         data->metaObjectOffset = allowedRevisionCache.count() - 1;
378
379         if (Data **old = stringCache.value(propName)) {
380             data->overrideIndexIsProperty = !bool((*old)->flags & Data::IsFunction);
381             data->overrideIndex = (*old)->coreIndex;
382         }
383
384         stringCache.insert(propName, data);
385     }
386 }
387
388 void QDeclarativePropertyCache::resolve(Data *data) const
389 {
390     Q_ASSERT(data->notFullyResolved());
391
392     data->propType = QMetaType::type(data->propTypeName);
393     if (QVariant::Type(data->propType) == QVariant::LastType)
394         data->propType = qMetaTypeId<QVariant>();
395
396
397     if (!(data->flags & Data::IsFunction))
398         data->flags |= flagsForPropertyType(data->propType, engine);
399
400     data->flags &= ~Data::NotFullyResolved;
401 }
402
403 void QDeclarativePropertyCache::updateRecur(QDeclarativeEngine *engine, const QMetaObject *metaObject)
404 {
405     if (!metaObject)
406         return;
407
408     updateRecur(engine, metaObject->superClass());
409
410     append(engine, metaObject);
411 }
412
413 void QDeclarativePropertyCache::update(QDeclarativeEngine *engine, const QMetaObject *metaObject)
414 {
415     Q_ASSERT(engine);
416     Q_ASSERT(metaObject);
417     Q_ASSERT(stringCache.isEmpty());
418
419     // Optimization to prevent unnecessary reallocation of lists
420     propertyIndexCache.reserve(metaObject->propertyCount());
421     methodIndexCache.reserve(metaObject->methodCount());
422
423     updateRecur(engine,metaObject);
424 }
425
426 QDeclarativePropertyCache::Data *
427 QDeclarativePropertyCache::property(int index) const
428 {
429     if (index < 0 || index >= (propertyIndexCacheStart + propertyIndexCache.count()))
430         return 0;
431     
432     if (index < propertyIndexCacheStart)
433         return parent->property(index);
434
435     Data *rv = const_cast<Data *>(&propertyIndexCache.at(index - propertyIndexCacheStart));
436     if (rv->notFullyResolved()) resolve(rv);
437     return rv;
438 }
439
440 QDeclarativePropertyCache::Data *
441 QDeclarativePropertyCache::method(int index) const
442 {
443     if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count()))
444         return 0;
445
446     if (index < methodIndexCacheStart)
447         return parent->method(index);
448
449     Data *rv = const_cast<Data *>(&methodIndexCache.at(index - methodIndexCacheStart));
450     if (rv->notFullyResolved()) resolve(rv);
451     return rv;
452 }
453
454 QDeclarativePropertyCache::Data *
455 QDeclarativePropertyCache::property(const QString &str) const
456 {
457     QDeclarativePropertyCache::Data **rv = stringCache.value(str);
458     if (rv && (*rv)->notFullyResolved()) resolve(*rv);
459     return rv?*rv:0;
460 }
461
462 QString QDeclarativePropertyCache::Data::name(QObject *object)
463 {
464     if (!object)
465         return QString();
466
467     return name(object->metaObject());
468 }
469
470 QString QDeclarativePropertyCache::Data::name(const QMetaObject *metaObject)
471 {
472     if (!metaObject || coreIndex == -1)
473         return QString();
474
475     if (flags & IsFunction) {
476         QMetaMethod m = metaObject->method(coreIndex);
477
478         QString name = QString::fromUtf8(m.signature());
479         int parenIdx = name.indexOf(QLatin1Char('('));
480         if (parenIdx != -1)
481             name = name.left(parenIdx);
482         return name;
483     } else {
484         QMetaProperty p = metaObject->property(coreIndex);
485         return QString::fromUtf8(p.name());
486     }
487 }
488
489 QStringList QDeclarativePropertyCache::propertyNames() const
490 {
491     QStringList keys;
492     for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) 
493         keys.append(iter.key());
494     return keys;
495 }
496
497 QDeclarativePropertyCache::Data *
498 QDeclarativePropertyCache::property(QDeclarativeEngine *engine, QObject *obj, 
499                                     const QHashedV8String &name, Data &local)
500 {
501     // XXX Optimize for worker script case where engine isn't available
502     QDeclarativePropertyCache *cache = 0;
503     if (engine) {
504         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
505
506         QDeclarativeData *ddata = QDeclarativeData::get(obj);
507         if (ddata && ddata->propertyCache)
508             cache = ddata->propertyCache;
509         if (!cache) {
510             cache = ep->cache(obj);
511             if (cache && ddata && !ddata->propertyCache) { cache->addref(); ddata->propertyCache = cache; }
512         }
513     }
514
515     QDeclarativePropertyCache::Data *rv = 0;
516
517     if (cache) {
518         rv = cache->property(name);
519     } else {
520         QString strname = QV8Engine::toStringStatic(name.string());
521         // QString strname = ep->v8engine.toString(name);
522         local = QDeclarativePropertyCache::create(obj->metaObject(), strname);
523         if (local.isValid())
524             rv = &local;
525     }
526
527     return rv;
528 }
529
530 QDeclarativePropertyCache::Data *
531 QDeclarativePropertyCache::property(QDeclarativeEngine *engine, QObject *obj, 
532                                     const QString &name, Data &local)
533 {
534     QDeclarativePropertyCache::Data *rv = 0;
535
536     if (!engine) {
537         local = QDeclarativePropertyCache::create(obj->metaObject(), name);
538         if (local.isValid())
539             rv = &local;
540     } else {
541         QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine);
542
543         QDeclarativePropertyCache *cache = 0;
544         QDeclarativeData *ddata = QDeclarativeData::get(obj);
545         if (ddata && ddata->propertyCache)
546             cache = ddata->propertyCache;
547         if (!cache) {
548             cache = enginePrivate->cache(obj);
549             if (cache && ddata && !ddata->propertyCache) { cache->addref(); ddata->propertyCache = cache; }
550         }
551
552         if (cache) {
553             rv = cache->property(name);
554         } else {
555             local = QDeclarativePropertyCache::create(obj->metaObject(), name);
556             if (local.isValid())
557                 rv = &local;
558         }
559     }
560
561     return rv;
562 }
563
564 static inline const QMetaObjectPrivate *priv(const uint* data)
565 { return reinterpret_cast<const QMetaObjectPrivate*>(data); }
566
567 bool QDeclarativePropertyCache::isDynamicMetaObject(const QMetaObject *mo)
568 {
569     return priv(mo->d.data)->revision >= 3 && priv(mo->d.data)->flags & DynamicMetaObject;
570 }
571
572 QT_END_NAMESPACE