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