Optimize default property resolution in compiler
[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         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(int reserve) 
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.copyAndReserve(stringCache, reserve);
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         bool utf8 = false;
328         while (*cptr != '(') { Q_ASSERT(*cptr != 0); utf8 |= *cptr & 0x80; ++cptr; }
329
330         Data *data = &methodIndexCache[ii - methodIndexCacheStart];
331
332         data->lazyLoad(m);
333         if (data->isSignal())
334             data->flags |= signalFlags;
335         else
336             data->flags |= methodFlags;
337
338         if (!dynamicMetaObject)
339             data->flags |= Data::IsDirect;
340
341         data->metaObjectOffset = allowedRevisionCache.count() - 1;
342
343         Data **old = 0;
344
345         if (utf8) {
346             QHashedString methodName(QString::fromUtf8(signature, cptr - signature));
347             old = stringCache.value(methodName);
348             stringCache.insert(methodName, data);
349         } else {
350             QHashedCStringRef methodName(signature, cptr - signature);
351             old = stringCache.value(methodName);
352             stringCache.insert(methodName, data);
353         }
354
355         if (old) {
356             // We only overload methods in the same class, exactly like C++
357             if ((*old)->flags & Data::IsFunction && (*old)->coreIndex >= methodOffset)
358                 data->relatedIndex = (*old)->coreIndex;
359             data->overrideIndexIsProperty = !bool((*old)->flags & Data::IsFunction);
360             data->overrideIndex = (*old)->coreIndex;
361         }
362     }
363
364     int propCount = metaObject->propertyCount();
365     int propOffset = metaObject->propertyOffset();
366
367     propertyIndexCache.resize(propCount - propertyIndexCacheStart);
368     for (int ii = propOffset; ii < propCount; ++ii) {
369         QMetaProperty p = metaObject->property(ii);
370         if (!p.isScriptable())
371             continue;
372
373         const char *str = p.name();
374         bool utf8 = false;
375         const char *cptr = str;
376         while (*cptr != 0) { utf8 |= *cptr & 0x80; ++cptr; }
377
378         Data *data = &propertyIndexCache[ii - propertyIndexCacheStart];
379
380         data->lazyLoad(p, engine);
381         data->flags |= propertyFlags;
382
383         if (!dynamicMetaObject) 
384             data->flags |= Data::IsDirect;
385
386         data->metaObjectOffset = allowedRevisionCache.count() - 1;
387
388         Data **old = 0;
389
390         if (utf8) {
391             QHashedString propName(QString::fromUtf8(str, cptr - str));
392             old = stringCache.value(propName);
393             stringCache.insert(propName, data);
394         } else {
395             QHashedCStringRef propName(str, cptr - str);
396             old = stringCache.value(propName);
397             stringCache.insert(propName, data);
398         }
399
400         if (old) {
401             data->overrideIndexIsProperty = !bool((*old)->flags & Data::IsFunction);
402             data->overrideIndex = (*old)->coreIndex;
403         }
404     }
405 }
406
407 void QDeclarativePropertyCache::resolve(Data *data) const
408 {
409     Q_ASSERT(data->notFullyResolved());
410
411     data->propType = QMetaType::type(data->propTypeName);
412     if (QVariant::Type(data->propType) == QVariant::LastType)
413         data->propType = qMetaTypeId<QVariant>();
414
415
416     if (!(data->flags & Data::IsFunction))
417         data->flags |= flagsForPropertyType(data->propType, engine);
418
419     data->flags &= ~Data::NotFullyResolved;
420 }
421
422 void QDeclarativePropertyCache::updateRecur(QDeclarativeEngine *engine, const QMetaObject *metaObject)
423 {
424     if (!metaObject)
425         return;
426
427     updateRecur(engine, metaObject->superClass());
428
429     append(engine, metaObject);
430 }
431
432 void QDeclarativePropertyCache::update(QDeclarativeEngine *engine, const QMetaObject *metaObject)
433 {
434     Q_ASSERT(engine);
435     Q_ASSERT(metaObject);
436     Q_ASSERT(stringCache.isEmpty());
437
438     // Optimization to prevent unnecessary reallocation of lists
439     int pc = metaObject->propertyCount();
440     int mc = metaObject->methodCount();
441     propertyIndexCache.reserve(pc);
442     methodIndexCache.reserve(mc);
443
444     stringCache.reserve(pc + mc);
445
446     updateRecur(engine,metaObject);
447 }
448
449 QDeclarativePropertyCache::Data *
450 QDeclarativePropertyCache::property(int index) const
451 {
452     if (index < 0 || index >= (propertyIndexCacheStart + propertyIndexCache.count()))
453         return 0;
454     
455     if (index < propertyIndexCacheStart)
456         return parent->property(index);
457
458     Data *rv = const_cast<Data *>(&propertyIndexCache.at(index - propertyIndexCacheStart));
459     if (rv->notFullyResolved()) resolve(rv);
460     return rv;
461 }
462
463 QDeclarativePropertyCache::Data *
464 QDeclarativePropertyCache::method(int index) const
465 {
466     if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count()))
467         return 0;
468
469     if (index < methodIndexCacheStart)
470         return parent->method(index);
471
472     Data *rv = const_cast<Data *>(&methodIndexCache.at(index - methodIndexCacheStart));
473     if (rv->notFullyResolved()) resolve(rv);
474     return rv;
475 }
476
477 QDeclarativePropertyCache::Data *
478 QDeclarativePropertyCache::property(const QHashedStringRef &str) const
479 {
480     QDeclarativePropertyCache::Data **rv = stringCache.value(str);
481     if (rv && (*rv)->notFullyResolved()) resolve(*rv);
482     return rv?*rv:0;
483 }
484
485 QDeclarativePropertyCache::Data *
486 QDeclarativePropertyCache::property(const QHashedCStringRef &str) const
487 {
488     QDeclarativePropertyCache::Data **rv = stringCache.value(str);
489     if (rv && (*rv)->notFullyResolved()) resolve(*rv);
490     return rv?*rv:0;
491 }
492
493 QDeclarativePropertyCache::Data *
494 QDeclarativePropertyCache::property(const QString &str) const
495 {
496     QDeclarativePropertyCache::Data **rv = stringCache.value(str);
497     if (rv && (*rv)->notFullyResolved()) resolve(*rv);
498     return rv?*rv:0;
499 }
500
501 QString QDeclarativePropertyCache::Data::name(QObject *object)
502 {
503     if (!object)
504         return QString();
505
506     return name(object->metaObject());
507 }
508
509 QString QDeclarativePropertyCache::Data::name(const QMetaObject *metaObject)
510 {
511     if (!metaObject || coreIndex == -1)
512         return QString();
513
514     if (flags & IsFunction) {
515         QMetaMethod m = metaObject->method(coreIndex);
516
517         QString name = QString::fromUtf8(m.signature());
518         int parenIdx = name.indexOf(QLatin1Char('('));
519         if (parenIdx != -1)
520             name = name.left(parenIdx);
521         return name;
522     } else {
523         QMetaProperty p = metaObject->property(coreIndex);
524         return QString::fromUtf8(p.name());
525     }
526 }
527
528 QStringList QDeclarativePropertyCache::propertyNames() const
529 {
530     QStringList keys;
531     for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) 
532         keys.append(iter.key());
533     return keys;
534 }
535
536 QDeclarativePropertyCache::Data *
537 QDeclarativePropertyCache::property(QDeclarativeEngine *engine, QObject *obj, 
538                                     const QHashedV8String &name, Data &local)
539 {
540     // XXX Optimize for worker script case where engine isn't available
541     QDeclarativePropertyCache *cache = 0;
542     if (engine) {
543         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
544
545         QDeclarativeData *ddata = QDeclarativeData::get(obj);
546         if (ddata && ddata->propertyCache)
547             cache = ddata->propertyCache;
548         if (!cache) {
549             cache = ep->cache(obj);
550             if (cache && ddata && !ddata->propertyCache) { cache->addref(); ddata->propertyCache = cache; }
551         }
552     }
553
554     QDeclarativePropertyCache::Data *rv = 0;
555
556     if (cache) {
557         rv = cache->property(name);
558     } else {
559         QString strname = QV8Engine::toStringStatic(name.string());
560         // QString strname = ep->v8engine()->toString(name);
561         local = QDeclarativePropertyCache::create(obj->metaObject(), strname);
562         if (local.isValid())
563             rv = &local;
564     }
565
566     return rv;
567 }
568
569 QDeclarativePropertyCache::Data *
570 QDeclarativePropertyCache::property(QDeclarativeEngine *engine, QObject *obj, 
571                                     const QString &name, Data &local)
572 {
573     QDeclarativePropertyCache::Data *rv = 0;
574
575     if (!engine) {
576         local = QDeclarativePropertyCache::create(obj->metaObject(), name);
577         if (local.isValid())
578             rv = &local;
579     } else {
580         QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine);
581
582         QDeclarativePropertyCache *cache = 0;
583         QDeclarativeData *ddata = QDeclarativeData::get(obj);
584         if (ddata && ddata->propertyCache)
585             cache = ddata->propertyCache;
586         if (!cache) {
587             cache = enginePrivate->cache(obj);
588             if (cache && ddata && !ddata->propertyCache) { cache->addref(); ddata->propertyCache = cache; }
589         }
590
591         if (cache) {
592             rv = cache->property(name);
593         } else {
594             local = QDeclarativePropertyCache::create(obj->metaObject(), name);
595             if (local.isValid())
596                 rv = &local;
597         }
598     }
599
600     return rv;
601 }
602
603 static inline const QMetaObjectPrivate *priv(const uint* data)
604 { return reinterpret_cast<const QMetaObjectPrivate*>(data); }
605
606 bool QDeclarativePropertyCache::isDynamicMetaObject(const QMetaObject *mo)
607 {
608     return priv(mo->d.data)->revision >= 3 && priv(mo->d.data)->flags & DynamicMetaObject;
609 }
610
611 QT_END_NAMESPACE