Merge branch 'qtquick2' of scm.dev.nokia.troll.no:qt/qtdeclarative-staging into v8
[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 QDeclarativePropertyCache::Data::Flags QDeclarativePropertyCache::Data::flagsForProperty(const QMetaProperty &p, QDeclarativeEngine *engine) 
58 {
59     int propType = p.userType();
60
61     Flags flags;
62
63     if (p.isConstant())
64         flags |= Data::IsConstant;
65     if (p.isWritable())
66         flags |= Data::IsWritable;
67     if (p.isResettable())
68         flags |= Data::IsResettable;
69     if (p.isFinal())
70         flags |= Data::IsFinal;
71
72     if (propType == qMetaTypeId<QDeclarativeBinding *>()) {
73         flags |= Data::IsQmlBinding;
74     } else if (propType == qMetaTypeId<QScriptValue>()) {
75         flags |= Data::IsQScriptValue;
76     } else if (propType == qMetaTypeId<QDeclarativeV8Handle>()) {
77         flags |= Data::IsV8Handle;
78     } else if (p.isEnumType()) {
79         flags |= Data::IsEnumType;
80     } else {
81         QDeclarativeMetaType::TypeCategory cat = engine ? QDeclarativeEnginePrivate::get(engine)->typeCategory(propType)
82                                                : QDeclarativeMetaType::typeCategory(propType);
83         if (cat == QDeclarativeMetaType::Object)
84             flags |= Data::IsQObjectDerived;
85         else if (cat == QDeclarativeMetaType::List)
86             flags |= Data::IsQList;
87     }
88
89     return flags;
90 }
91
92 void QDeclarativePropertyCache::Data::load(const QMetaProperty &p, QDeclarativeEngine *engine)
93 {
94     propType = p.userType();
95     if (QVariant::Type(propType) == QVariant::LastType)
96         propType = qMetaTypeId<QVariant>();
97     coreIndex = p.propertyIndex();
98     notifyIndex = p.notifySignalIndex();
99     flags = flagsForProperty(p, engine);
100     revision = p.revision();
101 }
102
103 void QDeclarativePropertyCache::Data::load(const QMetaMethod &m)
104 {
105     coreIndex = m.methodIndex();
106     relatedIndex = -1;
107     flags |= Data::IsFunction;
108     if (m.methodType() == QMetaMethod::Signal)
109         flags |= Data::IsSignal;
110     propType = QVariant::Invalid;
111
112     const char *returnType = m.typeName();
113     if (returnType) 
114         propType = QMetaType::type(returnType);
115
116     QList<QByteArray> params = m.parameterTypes();
117     if (!params.isEmpty()) {
118         flags |= Data::HasArguments;
119         if (params.at(0).length() == 23 && 
120             0 == qstrcmp(params.at(0).constData(), "QDeclarativeV8Function*")) {
121             flags |= Data::IsV8Function;
122         }
123     }
124     revision = m.revision();
125 }
126
127
128 /*!
129 Creates a new empty QDeclarativePropertyCache.
130 */
131 QDeclarativePropertyCache::QDeclarativePropertyCache(QDeclarativeEngine *e)
132 : QDeclarativeCleanup(e), engine(e)
133 {
134     Q_ASSERT(engine);
135 }
136
137 /*!
138 Creates a new QDeclarativePropertyCache of \a metaObject.
139 */
140 QDeclarativePropertyCache::QDeclarativePropertyCache(QDeclarativeEngine *e, const QMetaObject *metaObject)
141 : QDeclarativeCleanup(e), engine(e)
142 {
143     Q_ASSERT(engine);
144     Q_ASSERT(metaObject);
145
146     update(engine, metaObject);
147 }
148
149 QDeclarativePropertyCache::~QDeclarativePropertyCache()
150 {
151     clear();
152 }
153
154 void QDeclarativePropertyCache::clear()
155 {
156     for (int ii = 0; ii < indexCache.count(); ++ii) {
157         if (indexCache.at(ii)) indexCache.at(ii)->release();
158     }
159
160     for (int ii = 0; ii < methodIndexCache.count(); ++ii) {
161         RData *data = methodIndexCache.at(ii);
162         if (data) data->release(); 
163     }
164
165     for (StringCache::ConstIterator iter = stringCache.begin(); 
166             iter != stringCache.end(); ++iter) {
167         RData *data = (*iter);
168         data->release(); 
169     }
170
171     indexCache.clear();
172     methodIndexCache.clear();
173     stringCache.clear();
174     qPersistentDispose(constructor);
175 }
176
177 QDeclarativePropertyCache::Data QDeclarativePropertyCache::create(const QMetaObject *metaObject, 
178                                                                   const QString &property)
179 {
180     Q_ASSERT(metaObject);
181
182     QDeclarativePropertyCache::Data rv;
183     {
184         const QMetaObject *cmo = metaObject;
185         while (cmo) {
186             int idx = metaObject->indexOfProperty(property.toUtf8());
187             if (idx != -1) {
188                 QMetaProperty p = metaObject->property(idx);
189                 if (p.isScriptable()) {
190                     rv.load(metaObject->property(idx));
191                     return rv;
192                 } else {
193                     while (cmo && cmo->propertyOffset() >= idx)
194                         cmo = cmo->superClass();
195                 }
196             } else {
197                 cmo = 0;
198             }
199         }
200     }
201
202     int methodCount = metaObject->methodCount();
203     for (int ii = methodCount - 1; ii >= 3; --ii) { // >=3 to block the destroyed signal and deleteLater() slot
204         QMetaMethod m = metaObject->method(ii);
205         if (m.access() == QMetaMethod::Private)
206             continue;
207         QString methodName = QString::fromUtf8(m.signature());
208
209         int parenIdx = methodName.indexOf(QLatin1Char('('));
210         Q_ASSERT(parenIdx != -1);
211         QStringRef methodNameRef = methodName.leftRef(parenIdx);
212
213         if (methodNameRef == property) {
214             rv.load(m);
215             return rv;
216         }
217     }
218
219     return rv;
220 }
221
222 QDeclarativePropertyCache *QDeclarativePropertyCache::copy() const
223 {
224     QDeclarativePropertyCache *cache = new QDeclarativePropertyCache(engine);
225     cache->indexCache = indexCache;
226     cache->methodIndexCache = methodIndexCache;
227     cache->stringCache = stringCache;
228     cache->allowedRevisionCache = allowedRevisionCache;
229
230     for (int ii = 0; ii < indexCache.count(); ++ii) {
231         if (indexCache.at(ii)) indexCache.at(ii)->addref();
232     }
233     for (int ii = 0; ii < methodIndexCache.count(); ++ii) {
234         if (methodIndexCache.at(ii)) methodIndexCache.at(ii)->addref();
235     }
236     for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter)
237         (*iter)->addref();
238
239     // We specifically do *NOT* copy the constructor
240
241     return cache;
242 }
243
244 void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaObject *metaObject, 
245                                        Data::Flag propertyFlags, Data::Flag methodFlags, Data::Flag signalFlags)
246 {
247     append(engine, metaObject, -1, propertyFlags, methodFlags, signalFlags);
248 }
249
250 void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaObject *metaObject, 
251                                        int revision, 
252                                        Data::Flag propertyFlags, Data::Flag methodFlags, Data::Flag signalFlags)
253 {
254     Q_UNUSED(revision);
255
256     qPersistentDispose(constructor); // Now invalid
257
258     bool dynamicMetaObject = isDynamicMetaObject(metaObject);
259
260     allowedRevisionCache.append(0);
261
262     QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine);
263     int methodCount = metaObject->methodCount();
264     // 3 to block the destroyed signal and the deleteLater() slot
265     int methodOffset = qMax(3, metaObject->methodOffset()); 
266
267     methodIndexCache.resize(methodCount);
268     for (int ii = methodOffset; ii < methodCount; ++ii) {
269         QMetaMethod m = metaObject->method(ii);
270         if (m.access() == QMetaMethod::Private) 
271             continue;
272         QString methodName = QString::fromUtf8(m.signature());
273
274         int parenIdx = methodName.indexOf(QLatin1Char('('));
275         Q_ASSERT(parenIdx != -1);
276         methodName = methodName.left(parenIdx);
277
278         RData *data = new RData;
279         methodIndexCache[ii] = data;  
280
281         data->load(m);
282         if (m.methodType() == QMetaMethod::Slot || m.methodType() == QMetaMethod::Method) 
283             data->flags |= methodFlags;
284         else if (m.methodType() == QMetaMethod::Signal)
285             data->flags |= signalFlags;
286
287         if (!dynamicMetaObject)
288             data->flags |= Data::IsDirect;
289
290         data->metaObjectOffset = allowedRevisionCache.count() - 1;
291
292         if (stringCache.contains(methodName)) {
293             RData *old = stringCache[methodName];
294             // We only overload methods in the same class, exactly like C++
295             if (old->flags & Data::IsFunction && old->coreIndex >= methodOffset)
296                 data->relatedIndex = old->coreIndex;
297             data->overrideIndexIsProperty = !bool(old->flags & Data::IsFunction);
298             data->overrideIndex = old->coreIndex;
299             stringCache[methodName]->release();
300         }
301
302         stringCache.insert(methodName, data);
303         data->addref();
304     }
305
306     int propCount = metaObject->propertyCount();
307     int propOffset = metaObject->propertyOffset();
308
309     indexCache.resize(propCount);
310     for (int ii = propOffset; ii < propCount; ++ii) {
311         QMetaProperty p = metaObject->property(ii);
312         if (!p.isScriptable())
313             continue;
314
315         QString propName = QString::fromUtf8(p.name());
316
317         RData *data = new RData;
318         indexCache[ii] = data;
319
320         data->load(p, engine);
321         data->flags |= propertyFlags;
322
323         if (!dynamicMetaObject) 
324             data->flags |= Data::IsDirect;
325
326         data->metaObjectOffset = allowedRevisionCache.count() - 1;
327
328         if (stringCache.contains(propName)) {
329             RData *old = stringCache[propName];
330             data->overrideIndexIsProperty = !bool(old->flags & Data::IsFunction);
331             data->overrideIndex = old->coreIndex;
332             stringCache[propName]->release();
333         }
334
335         stringCache.insert(propName, data);
336         data->addref();
337     }
338 }
339
340 void QDeclarativePropertyCache::updateRecur(QDeclarativeEngine *engine, const QMetaObject *metaObject)
341 {
342     if (!metaObject)
343         return;
344
345     updateRecur(engine, metaObject->superClass());
346
347     append(engine, metaObject);
348 }
349
350 void QDeclarativePropertyCache::update(QDeclarativeEngine *engine, const QMetaObject *metaObject)
351 {
352     Q_ASSERT(engine);
353     Q_ASSERT(metaObject);
354
355     clear();
356
357     // Optimization to prevent unnecessary reallocation of lists
358     indexCache.reserve(metaObject->propertyCount());
359     methodIndexCache.reserve(metaObject->methodCount());
360
361     updateRecur(engine,metaObject);
362 }
363
364 QDeclarativePropertyCache::Data *
365 QDeclarativePropertyCache::property(int index) const
366 {
367     if (index < 0 || index >= indexCache.count())
368         return 0;
369
370     return indexCache.at(index);
371 }
372
373 QDeclarativePropertyCache::Data *
374 QDeclarativePropertyCache::method(int index) const
375 {
376     if (index < 0 || index >= methodIndexCache.count())
377         return 0;
378
379     return methodIndexCache.at(index);
380 }
381
382 QDeclarativePropertyCache::Data *
383 QDeclarativePropertyCache::property(const QString &str) const
384 {
385     QDeclarativePropertyCache::RData **rv = stringCache.value(str);
386     return rv?*rv:0;
387 }
388
389 QString QDeclarativePropertyCache::Data::name(QObject *object)
390 {
391     if (!object)
392         return QString();
393
394     return name(object->metaObject());
395 }
396
397 QString QDeclarativePropertyCache::Data::name(const QMetaObject *metaObject)
398 {
399     if (!metaObject || coreIndex == -1)
400         return QString();
401
402     if (flags & IsFunction) {
403         QMetaMethod m = metaObject->method(coreIndex);
404
405         QString name = QString::fromUtf8(m.signature());
406         int parenIdx = name.indexOf(QLatin1Char('('));
407         if (parenIdx != -1)
408             name = name.left(parenIdx);
409         return name;
410     } else {
411         QMetaProperty p = metaObject->property(coreIndex);
412         return QString::fromUtf8(p.name());
413     }
414 }
415
416 QStringList QDeclarativePropertyCache::propertyNames() const
417 {
418     QStringList keys;
419     for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) 
420         keys.append(iter.key());
421     return keys;
422 }
423
424 QDeclarativePropertyCache::Data *
425 QDeclarativePropertyCache::property(QDeclarativeEngine *engine, QObject *obj, 
426                                     const QHashedV8String &name, Data &local)
427 {
428     // XXX Optimize for worker script case where engine isn't available
429     QDeclarativePropertyCache *cache = 0;
430     if (engine) {
431         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
432
433         QDeclarativeData *ddata = QDeclarativeData::get(obj);
434         if (ddata && ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine) // XXX aakenend
435             cache = ddata->propertyCache;
436         if (!cache) {
437             cache = ep->cache(obj);
438             if (cache && ddata && !ddata->propertyCache) { cache->addref(); ddata->propertyCache = cache; }
439         }
440     }
441
442     QDeclarativePropertyCache::Data *rv = 0;
443
444     if (cache) {
445         rv = cache->property(name);
446     } else {
447         QString strname = QV8Engine::toStringStatic(name.string());
448         // QString strname = ep->v8engine.toString(name);
449         local = QDeclarativePropertyCache::create(obj->metaObject(), strname);
450         if (local.isValid())
451             rv = &local;
452     }
453
454     return rv;
455 }
456
457 QDeclarativePropertyCache::Data *
458 QDeclarativePropertyCache::property(QDeclarativeEngine *engine, QObject *obj, 
459                                     const QString &name, Data &local)
460 {
461     QDeclarativePropertyCache::Data *rv = 0;
462
463     if (!engine) {
464         local = QDeclarativePropertyCache::create(obj->metaObject(), name);
465         if (local.isValid())
466             rv = &local;
467     } else {
468         QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine);
469
470         QDeclarativePropertyCache *cache = 0;
471         QDeclarativeData *ddata = QDeclarativeData::get(obj);
472         if (ddata && ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine)
473             cache = ddata->propertyCache;
474         if (!cache) {
475             cache = enginePrivate->cache(obj);
476             if (cache && ddata && !ddata->propertyCache) { cache->addref(); ddata->propertyCache = cache; }
477         }
478
479         if (cache) {
480             rv = cache->property(name);
481         } else {
482             local = QDeclarativePropertyCache::create(obj->metaObject(), name);
483             if (local.isValid())
484                 rv = &local;
485         }
486     }
487
488     return rv;
489 }
490
491 static inline const QMetaObjectPrivate *priv(const uint* data)
492 { return reinterpret_cast<const QMetaObjectPrivate*>(data); }
493
494 bool QDeclarativePropertyCache::isDynamicMetaObject(const QMetaObject *mo)
495 {
496     return priv(mo->d.data)->revision >= 3 && priv(mo->d.data)->flags & DynamicMetaObject;
497 }
498
499 QT_END_NAMESPACE