Correct handling of QObject* and QWidget* properties
[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
225 void QDeclarativePropertyCache::clear()
226 {
227     if (parent) parent->release();
228     parent = 0;
229
230     propertyIndexCacheStart = 0;
231     methodIndexCacheStart = 0;
232
233     propertyIndexCache.clear();
234     methodIndexCache.clear();
235     stringCache.clear();
236     qPersistentDispose(constructor);
237 }
238
239 QDeclarativePropertyCache::Data QDeclarativePropertyCache::create(const QMetaObject *metaObject, 
240                                                                   const QString &property)
241 {
242     Q_ASSERT(metaObject);
243
244     QDeclarativePropertyCache::Data rv;
245     {
246         const QMetaObject *cmo = metaObject;
247         while (cmo) {
248             int idx = metaObject->indexOfProperty(property.toUtf8());
249             if (idx != -1) {
250                 QMetaProperty p = metaObject->property(idx);
251                 if (p.isScriptable()) {
252                     rv.load(metaObject->property(idx));
253                     return rv;
254                 } else {
255                     while (cmo && cmo->propertyOffset() >= idx)
256                         cmo = cmo->superClass();
257                 }
258             } else {
259                 cmo = 0;
260             }
261         }
262     }
263
264     int methodCount = metaObject->methodCount();
265     for (int ii = methodCount - 1; ii >= 3; --ii) { // >=3 to block the destroyed signal and deleteLater() slot
266         QMetaMethod m = metaObject->method(ii);
267         if (m.access() == QMetaMethod::Private)
268             continue;
269         QString methodName = QString::fromUtf8(m.signature());
270
271         int parenIdx = methodName.indexOf(QLatin1Char('('));
272         Q_ASSERT(parenIdx != -1);
273         QStringRef methodNameRef = methodName.leftRef(parenIdx);
274
275         if (methodNameRef == property) {
276             rv.load(m);
277             return rv;
278         }
279     }
280
281     return rv;
282 }
283
284 QDeclarativePropertyCache *QDeclarativePropertyCache::copy() 
285 {
286     QDeclarativePropertyCache *cache = new QDeclarativePropertyCache(engine);
287     cache->parent = this;
288     cache->parent->addref();
289     cache->propertyIndexCacheStart = propertyIndexCache.count() + propertyIndexCacheStart;
290     cache->methodIndexCacheStart = methodIndexCache.count() + methodIndexCacheStart;
291     cache->stringCache = stringCache;
292     cache->allowedRevisionCache = allowedRevisionCache;
293
294     // We specifically do *NOT* copy the constructor
295
296     return cache;
297 }
298
299 void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaObject *metaObject, 
300                                        Data::Flag propertyFlags, Data::Flag methodFlags, Data::Flag signalFlags)
301 {
302     append(engine, metaObject, -1, propertyFlags, methodFlags, signalFlags);
303 }
304
305 void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaObject *metaObject, 
306                                        int revision, 
307                                        Data::Flag propertyFlags, Data::Flag methodFlags, Data::Flag signalFlags)
308 {
309     Q_UNUSED(revision);
310
311     qPersistentDispose(constructor); // Now invalid
312
313     bool dynamicMetaObject = isDynamicMetaObject(metaObject);
314
315     allowedRevisionCache.append(0);
316
317     int methodCount = metaObject->methodCount();
318     // 3 to block the destroyed signal and the deleteLater() slot
319     int methodOffset = qMax(3, metaObject->methodOffset()); 
320
321     methodIndexCache.resize(methodCount - methodIndexCacheStart);
322     for (int ii = methodOffset; ii < methodCount; ++ii) {
323         QMetaMethod m = metaObject->method(ii);
324         if (m.access() == QMetaMethod::Private) 
325             continue;
326
327         // Extract method name
328         const char *signature = m.signature();
329         const char *cptr = signature;
330         while (*cptr != '(') { Q_ASSERT(*cptr != 0); ++cptr; }
331         QString str = dynamicMetaObject?QString::fromUtf8(signature, cptr - signature):
332                                         QString::fromLatin1(signature, cptr - signature);
333         QHashedString methodName(str);
334
335         Data *data = &methodIndexCache[ii - methodIndexCacheStart];
336
337         data->lazyLoad(m);
338         if (data->isSignal())
339             data->flags |= signalFlags;
340         else
341             data->flags |= methodFlags;
342
343         if (!dynamicMetaObject)
344             data->flags |= Data::IsDirect;
345
346         data->metaObjectOffset = allowedRevisionCache.count() - 1;
347
348         if (Data **old = stringCache.value(methodName)) {
349             // We only overload methods in the same class, exactly like C++
350             if ((*old)->flags & Data::IsFunction && (*old)->coreIndex >= methodOffset)
351                 data->relatedIndex = (*old)->coreIndex;
352             data->overrideIndexIsProperty = !bool((*old)->flags & Data::IsFunction);
353             data->overrideIndex = (*old)->coreIndex;
354         }
355
356         stringCache.insert(methodName, data);
357     }
358
359     int propCount = metaObject->propertyCount();
360     int propOffset = metaObject->propertyOffset();
361
362     propertyIndexCache.resize(propCount - propertyIndexCacheStart);
363     for (int ii = propOffset; ii < propCount; ++ii) {
364         QMetaProperty p = metaObject->property(ii);
365         if (!p.isScriptable())
366             continue;
367
368         QString str = dynamicMetaObject?QString::fromUtf8(p.name()):
369                                         QString::fromLatin1(p.name());
370         QHashedString propName(str);
371
372         Data *data = &propertyIndexCache[ii - propertyIndexCacheStart];
373
374         data->lazyLoad(p, engine);
375         data->flags |= propertyFlags;
376
377         if (!dynamicMetaObject) 
378             data->flags |= Data::IsDirect;
379
380         data->metaObjectOffset = allowedRevisionCache.count() - 1;
381
382         if (Data **old = stringCache.value(propName)) {
383             data->overrideIndexIsProperty = !bool((*old)->flags & Data::IsFunction);
384             data->overrideIndex = (*old)->coreIndex;
385         }
386
387         stringCache.insert(propName, data);
388     }
389 }
390
391 void QDeclarativePropertyCache::resolve(Data *data) const
392 {
393     Q_ASSERT(data->notFullyResolved());
394
395     data->propType = QMetaType::type(data->propTypeName);
396     if (QVariant::Type(data->propType) == QVariant::LastType)
397         data->propType = qMetaTypeId<QVariant>();
398
399
400     if (!(data->flags & Data::IsFunction))
401         data->flags |= flagsForPropertyType(data->propType, engine);
402
403     data->flags &= ~Data::NotFullyResolved;
404 }
405
406 void QDeclarativePropertyCache::updateRecur(QDeclarativeEngine *engine, const QMetaObject *metaObject)
407 {
408     if (!metaObject)
409         return;
410
411     updateRecur(engine, metaObject->superClass());
412
413     append(engine, metaObject);
414 }
415
416 void QDeclarativePropertyCache::update(QDeclarativeEngine *engine, const QMetaObject *metaObject)
417 {
418     Q_ASSERT(engine);
419     Q_ASSERT(metaObject);
420
421     clear();
422
423     // Optimization to prevent unnecessary reallocation of lists
424     propertyIndexCache.reserve(metaObject->propertyCount());
425     methodIndexCache.reserve(metaObject->methodCount());
426
427     updateRecur(engine,metaObject);
428 }
429
430 QDeclarativePropertyCache::Data *
431 QDeclarativePropertyCache::property(int index) const
432 {
433     if (index < 0 || index >= (propertyIndexCacheStart + propertyIndexCache.count()))
434         return 0;
435     
436     if (index < propertyIndexCacheStart)
437         return parent->property(index);
438
439     Data *rv = const_cast<Data *>(&propertyIndexCache.at(index - propertyIndexCacheStart));
440     if (rv->notFullyResolved()) resolve(rv);
441     return rv;
442 }
443
444 QDeclarativePropertyCache::Data *
445 QDeclarativePropertyCache::method(int index) const
446 {
447     if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count()))
448         return 0;
449
450     if (index < methodIndexCacheStart)
451         return parent->method(index);
452
453     Data *rv = const_cast<Data *>(&methodIndexCache.at(index - methodIndexCacheStart));
454     if (rv->notFullyResolved()) resolve(rv);
455     return rv;
456 }
457
458 QDeclarativePropertyCache::Data *
459 QDeclarativePropertyCache::property(const QString &str) const
460 {
461     QDeclarativePropertyCache::Data **rv = stringCache.value(str);
462     if (rv && (*rv)->notFullyResolved()) resolve(*rv);
463     return rv?*rv:0;
464 }
465
466 QString QDeclarativePropertyCache::Data::name(QObject *object)
467 {
468     if (!object)
469         return QString();
470
471     return name(object->metaObject());
472 }
473
474 QString QDeclarativePropertyCache::Data::name(const QMetaObject *metaObject)
475 {
476     if (!metaObject || coreIndex == -1)
477         return QString();
478
479     if (flags & IsFunction) {
480         QMetaMethod m = metaObject->method(coreIndex);
481
482         QString name = QString::fromUtf8(m.signature());
483         int parenIdx = name.indexOf(QLatin1Char('('));
484         if (parenIdx != -1)
485             name = name.left(parenIdx);
486         return name;
487     } else {
488         QMetaProperty p = metaObject->property(coreIndex);
489         return QString::fromUtf8(p.name());
490     }
491 }
492
493 QStringList QDeclarativePropertyCache::propertyNames() const
494 {
495     QStringList keys;
496     for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) 
497         keys.append(iter.key());
498     return keys;
499 }
500
501 QDeclarativePropertyCache::Data *
502 QDeclarativePropertyCache::property(QDeclarativeEngine *engine, QObject *obj, 
503                                     const QHashedV8String &name, Data &local)
504 {
505     // XXX Optimize for worker script case where engine isn't available
506     QDeclarativePropertyCache *cache = 0;
507     if (engine) {
508         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
509
510         QDeclarativeData *ddata = QDeclarativeData::get(obj);
511         if (ddata && ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine) // XXX aakenend
512             cache = ddata->propertyCache;
513         if (!cache) {
514             cache = ep->cache(obj);
515             if (cache && ddata && !ddata->propertyCache) { cache->addref(); ddata->propertyCache = cache; }
516         }
517     }
518
519     QDeclarativePropertyCache::Data *rv = 0;
520
521     if (cache) {
522         rv = cache->property(name);
523     } else {
524         QString strname = QV8Engine::toStringStatic(name.string());
525         // QString strname = ep->v8engine.toString(name);
526         local = QDeclarativePropertyCache::create(obj->metaObject(), strname);
527         if (local.isValid())
528             rv = &local;
529     }
530
531     return rv;
532 }
533
534 QDeclarativePropertyCache::Data *
535 QDeclarativePropertyCache::property(QDeclarativeEngine *engine, QObject *obj, 
536                                     const QString &name, Data &local)
537 {
538     QDeclarativePropertyCache::Data *rv = 0;
539
540     if (!engine) {
541         local = QDeclarativePropertyCache::create(obj->metaObject(), name);
542         if (local.isValid())
543             rv = &local;
544     } else {
545         QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine);
546
547         QDeclarativePropertyCache *cache = 0;
548         QDeclarativeData *ddata = QDeclarativeData::get(obj);
549         if (ddata && ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine)
550             cache = ddata->propertyCache;
551         if (!cache) {
552             cache = enginePrivate->cache(obj);
553             if (cache && ddata && !ddata->propertyCache) { cache->addref(); ddata->propertyCache = cache; }
554         }
555
556         if (cache) {
557             rv = cache->property(name);
558         } else {
559             local = QDeclarativePropertyCache::create(obj->metaObject(), name);
560             if (local.isValid())
561                 rv = &local;
562         }
563     }
564
565     return rv;
566 }
567
568 static inline const QMetaObjectPrivate *priv(const uint* data)
569 { return reinterpret_cast<const QMetaObjectPrivate*>(data); }
570
571 bool QDeclarativePropertyCache::isDynamicMetaObject(const QMetaObject *mo)
572 {
573     return priv(mo->d.data)->revision >= 3 && priv(mo->d.data)->flags & DynamicMetaObject;
574 }
575
576 QT_END_NAMESPACE