Support (registered) non-local enums for signal/slot params in QML.
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmlpropertycache.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qqmlpropertycache_p.h"
43
44 #include "qqmlengine_p.h"
45 #include "qqmlbinding_p.h"
46 #include <private/qv8engine_p.h>
47
48 #include <private/qmetaobject_p.h>
49 #include <private/qqmlaccessors_p.h>
50
51 #include <QtCore/qdebug.h>
52
53 #include <ctype.h> // for toupper
54
55 Q_DECLARE_METATYPE(QJSValue)
56 Q_DECLARE_METATYPE(QQmlV8Handle);
57
58 QT_BEGIN_NAMESPACE
59
60 #define Q_INT16_MAX 32767
61
62 class QQmlPropertyCacheMethodArguments 
63 {
64 public:
65     QQmlPropertyCacheMethodArguments *next;
66     int arguments[0];
67 };
68
69 // Flags that do *NOT* depend on the property's QMetaProperty::userType() and thus are quick
70 // to load
71 static QQmlPropertyData::Flags fastFlagsForProperty(const QMetaProperty &p)
72 {
73     QQmlPropertyData::Flags flags;
74
75     if (p.isConstant())
76         flags |= QQmlPropertyData::IsConstant;
77     if (p.isWritable())
78         flags |= QQmlPropertyData::IsWritable;
79     if (p.isResettable())
80         flags |= QQmlPropertyData::IsResettable;
81     if (p.isFinal())
82         flags |= QQmlPropertyData::IsFinal;
83     if (p.isEnumType())
84         flags |= QQmlPropertyData::IsEnumType;
85
86     return flags;
87 }
88
89 // Flags that do depend on the property's QMetaProperty::userType() and thus are slow to 
90 // load
91 static QQmlPropertyData::Flags flagsForPropertyType(int propType, QQmlEngine *engine)
92 {
93     Q_ASSERT(propType != -1);
94
95     QQmlPropertyData::Flags flags;
96
97     if (propType == QMetaType::QObjectStar || propType == QMetaType::QWidgetStar) {
98         flags |= QQmlPropertyData::IsQObjectDerived;
99     } else if (propType == QMetaType::QVariant) {
100         flags |= QQmlPropertyData::IsQVariant;
101     } else if (propType < (int)QVariant::UserType) {
102     } else if (propType == qMetaTypeId<QQmlBinding *>()) {
103         flags |= QQmlPropertyData::IsQmlBinding;
104     } else if (propType == qMetaTypeId<QJSValue>()) {
105         flags |= QQmlPropertyData::IsQJSValue;
106     } else if (propType == qMetaTypeId<QQmlV8Handle>()) {
107         flags |= QQmlPropertyData::IsV8Handle;
108     } else {
109         QQmlMetaType::TypeCategory cat = 
110             engine ? QQmlEnginePrivate::get(engine)->typeCategory(propType)
111                    : QQmlMetaType::typeCategory(propType);
112
113         if (cat == QQmlMetaType::Object)
114             flags |= QQmlPropertyData::IsQObjectDerived;
115         else if (cat == QQmlMetaType::List)
116             flags |= QQmlPropertyData::IsQList;
117     }
118
119     return flags;
120 }
121
122 static int metaObjectSignalCount(const QMetaObject *metaObject)
123 {
124     int signalCount = 0;
125     for (const QMetaObject *obj = metaObject; obj; obj = obj->superClass())
126         signalCount += QMetaObjectPrivate::get(obj)->signalCount;
127     return signalCount;
128 }
129
130 QQmlPropertyData::Flags
131 QQmlPropertyData::flagsForProperty(const QMetaProperty &p, QQmlEngine *engine)
132 {
133     return fastFlagsForProperty(p) | flagsForPropertyType(p.userType(), engine);
134 }
135
136 void QQmlPropertyData::lazyLoad(const QMetaProperty &p, QQmlEngine *engine)
137 {
138     Q_UNUSED(engine);
139
140     coreIndex = p.propertyIndex();
141     notifyIndex = p.notifySignalIndex();
142     Q_ASSERT(p.revision() <= Q_INT16_MAX);
143     revision = p.revision();
144
145     flags = fastFlagsForProperty(p);
146
147     int type = p.type();
148     if (type == QMetaType::QObjectStar || type == QMetaType::QWidgetStar) {
149         propType = type;
150         flags |= QQmlPropertyData::IsQObjectDerived;
151     } else if (type == QMetaType::QVariant) {
152         propType = type;
153         flags |= QQmlPropertyData::IsQVariant;
154     } else if (type == QVariant::UserType || type == -1) {
155         propTypeName = p.typeName();
156         flags |= QQmlPropertyData::NotFullyResolved;
157     } else {
158         propType = type;
159     }
160 }
161
162 void QQmlPropertyData::load(const QMetaProperty &p, QQmlEngine *engine)
163 {
164     propType = p.userType();
165     coreIndex = p.propertyIndex();
166     notifyIndex = p.notifySignalIndex();
167     flags = fastFlagsForProperty(p) | flagsForPropertyType(propType, engine);
168     Q_ASSERT(p.revision() <= Q_INT16_MAX);
169     revision = p.revision();
170 }
171
172 void QQmlPropertyData::load(const QMetaMethod &m)
173 {
174     coreIndex = m.methodIndex();
175     arguments = 0;
176     flags |= IsFunction;
177     if (m.methodType() == QMetaMethod::Signal)
178         flags |= IsSignal;
179     propType = QVariant::Invalid;
180
181     const char *returnType = m.typeName();
182     if (returnType) 
183         propType = QMetaType::type(returnType);
184
185     const char *signature = m.signature();
186     while (*signature != '(') { Q_ASSERT(*signature != 0); ++signature; }
187
188     ++signature;
189     if (*signature != ')') {
190         flags |= HasArguments;
191         if (0 == ::strcmp(signature, "QQmlV8Function*)")) {
192             flags |= IsV8Function;
193         }
194     }
195
196     Q_ASSERT(m.revision() <= Q_INT16_MAX);
197     revision = m.revision();
198 }
199
200 void QQmlPropertyData::lazyLoad(const QMetaMethod &m)
201 {
202     coreIndex = m.methodIndex();
203     arguments = 0;
204     flags |= IsFunction;
205     if (m.methodType() == QMetaMethod::Signal)
206         flags |= IsSignal;
207     propType = QVariant::Invalid;
208
209     const char *returnType = m.typeName();
210     if (returnType && *returnType) {
211         propTypeName = returnType;
212         flags |= NotFullyResolved;
213     }
214
215     const char *signature = m.signature();
216     while (*signature != '(') { Q_ASSERT(*signature != 0); ++signature; }
217
218     ++signature;
219     if (*signature != ')') {
220         flags |= HasArguments;
221         if (0 == ::strcmp(signature, "QQmlV8Function*)")) {
222             flags |= IsV8Function;
223         }
224     }
225
226     Q_ASSERT(m.revision() <= Q_INT16_MAX);
227     revision = m.revision();
228 }
229
230 /*!
231 Creates a new empty QQmlPropertyCache.
232 */
233 QQmlPropertyCache::QQmlPropertyCache(QQmlEngine *e)
234 : engine(e), parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0),
235   signalHanderIndexCacheStart(0), metaObject(0), argumentsCache(0)
236 {
237     Q_ASSERT(engine);
238 }
239
240 /*!
241 Creates a new QQmlPropertyCache of \a metaObject.
242 */
243 QQmlPropertyCache::QQmlPropertyCache(QQmlEngine *e, const QMetaObject *metaObject)
244 : engine(e), parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0),
245   signalHanderIndexCacheStart(0), metaObject(0), argumentsCache(0)
246 {
247     Q_ASSERT(engine);
248     Q_ASSERT(metaObject);
249
250     update(engine, metaObject);
251 }
252
253 QQmlPropertyCache::~QQmlPropertyCache()
254 {
255     clear();
256
257     QQmlPropertyCacheMethodArguments *args = argumentsCache;
258     while (args) {
259         QQmlPropertyCacheMethodArguments *next = args->next;
260         free(args);
261         args = next;
262     }
263
264     // We must clear this prior to releasing the parent incase it is a
265     // linked hash
266     stringCache.clear();
267     if (parent) parent->release();
268     parent = 0;
269     engine = 0;
270 }
271
272 void QQmlPropertyCache::destroy()
273 {
274     Q_ASSERT(engine || constructor.IsEmpty());
275     if (constructor.IsEmpty())
276         delete this;
277     else
278         QQmlEnginePrivate::deleteInEngineThread(engine, this);
279 }
280
281 // This is inherited from QQmlCleanup, so it should only clear the things
282 // that are tied to the specific QQmlEngine.
283 void QQmlPropertyCache::clear()
284 {
285     qPersistentDispose(constructor);
286     engine = 0;
287 }
288
289 QQmlPropertyCache *QQmlPropertyCache::copy(int reserve)
290 {
291     QQmlPropertyCache *cache = new QQmlPropertyCache(engine);
292     cache->parent = this;
293     cache->parent->addref();
294     cache->propertyIndexCacheStart = propertyIndexCache.count() + propertyIndexCacheStart;
295     cache->methodIndexCacheStart = methodIndexCache.count() + methodIndexCacheStart;
296     cache->signalHanderIndexCacheStart = signalHandlerIndexCache.count() + signalHanderIndexCacheStart;
297     cache->stringCache.linkAndReserve(stringCache, reserve);
298     cache->allowedRevisionCache = allowedRevisionCache;
299     cache->metaObject = metaObject;
300
301     // We specifically do *NOT* copy the constructor
302
303     return cache;
304 }
305
306 QQmlPropertyCache *QQmlPropertyCache::copy()
307 {
308     return copy(0);
309 }
310
311 QQmlPropertyCache *
312 QQmlPropertyCache::copyAndAppend(QQmlEngine *engine, const QMetaObject *metaObject,
313                                          QQmlPropertyData::Flag propertyFlags,
314                                          QQmlPropertyData::Flag methodFlags,
315                                          QQmlPropertyData::Flag signalFlags)
316 {
317     return copyAndAppend(engine, metaObject, -1, propertyFlags, methodFlags, signalFlags);
318 }
319
320 QQmlPropertyCache *
321 QQmlPropertyCache::copyAndAppend(QQmlEngine *engine, const QMetaObject *metaObject,
322                                          int revision,
323                                          QQmlPropertyData::Flag propertyFlags,
324                                          QQmlPropertyData::Flag methodFlags,
325                                          QQmlPropertyData::Flag signalFlags)
326 {
327     Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4);
328
329     // Reserve enough space in the name hash for all the methods (including signals), all the
330     // signal handlers and all the properties.  This assumes no name clashes, but this is the
331     // common case.
332     QQmlPropertyCache *rv = copy(QMetaObjectPrivate::get(metaObject)->methodCount +
333                                          QMetaObjectPrivate::get(metaObject)->signalCount +
334                                          QMetaObjectPrivate::get(metaObject)->propertyCount);
335
336     rv->append(engine, metaObject, revision, propertyFlags, methodFlags, signalFlags);
337
338     return rv;
339 }
340
341 void QQmlPropertyCache::append(QQmlEngine *engine, const QMetaObject *metaObject, 
342                                        QQmlPropertyData::Flag propertyFlags,
343                                        QQmlPropertyData::Flag methodFlags,
344                                        QQmlPropertyData::Flag signalFlags)
345 {
346     append(engine, metaObject, -1, propertyFlags, methodFlags, signalFlags);
347 }
348
349 void QQmlPropertyCache::append(QQmlEngine *engine, const QMetaObject *metaObject, 
350                                        int revision, 
351                                        QQmlPropertyData::Flag propertyFlags,
352                                        QQmlPropertyData::Flag methodFlags,
353                                        QQmlPropertyData::Flag signalFlags)
354 {
355     Q_UNUSED(revision);
356     Q_ASSERT(constructor.IsEmpty()); // We should not be appending to an in-use property cache
357
358     this->metaObject = metaObject;
359
360     bool dynamicMetaObject = isDynamicMetaObject(metaObject);
361
362     allowedRevisionCache.append(0);
363
364     int methodCount = metaObject->methodCount();
365     Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4);
366     int signalCount = metaObjectSignalCount(metaObject);
367     int classInfoCount = QMetaObjectPrivate::get(metaObject)->classInfoCount;
368
369     QQmlAccessorProperties::Properties accessorProperties;
370
371     // Special case QObject as we don't want to add a qt_HasQmlAccessors classinfo to it
372     if (metaObject == &QObject::staticMetaObject) {
373         accessorProperties = QQmlAccessorProperties::properties(metaObject);
374     } else if (classInfoCount) {
375         int classInfoOffset = metaObject->classInfoOffset();
376         bool hasFastProperty = false;
377         for (int ii = 0; ii < classInfoCount; ++ii) {
378             int idx = ii + classInfoOffset;
379
380             if (0 == qstrcmp(metaObject->classInfo(idx).name(), "qt_HasQmlAccessors")) {
381                 hasFastProperty = true;
382                 break;
383             }
384         }
385
386         if (hasFastProperty) {
387             accessorProperties = QQmlAccessorProperties::properties(metaObject);
388             if (accessorProperties.count == 0)
389                 qFatal("QQmlPropertyCache: %s has FastProperty class info, but has not "
390                        "installed property accessors", metaObject->className());
391         } else {
392 #ifndef QT_NO_DEBUG
393             accessorProperties = QQmlAccessorProperties::properties(metaObject);
394             if (accessorProperties.count != 0)
395                 qFatal("QQmlPropertyCache: %s has fast property accessors, but is missing "
396                        "FastProperty class info", metaObject->className());
397 #endif
398         }
399     }
400
401     // qMax(defaultMethods, methodOffset) to block the signals and slots of QObject::staticMetaObject
402     // incl. destroyed signals, objectNameChanged signal, deleteLater slot, _q_reregisterTimers slot.
403     int methodOffset = qMax(QObject::staticMetaObject.methodCount(), metaObject->methodOffset());
404     int signalOffset = signalCount - QMetaObjectPrivate::get(metaObject)->signalCount;
405
406     // update() should have reserved enough space in the vector that this doesn't cause a realloc
407     // and invalidate the stringCache.
408     methodIndexCache.resize(methodCount - methodIndexCacheStart);
409     signalHandlerIndexCache.resize(signalCount - signalHanderIndexCacheStart);
410     int signalHandlerIndex = signalOffset;
411     for (int ii = methodOffset; ii < methodCount; ++ii) {
412         QMetaMethod m = metaObject->method(ii);
413         if (m.access() == QMetaMethod::Private) 
414             continue;
415
416         // Extract method name
417         const char *signature = m.signature();
418         const char *cptr = signature;
419         char utf8 = 0;
420         while (*cptr != '(') {
421             Q_ASSERT(*cptr != 0);
422             utf8 |= *cptr & 0x80;
423             ++cptr;
424         }
425
426         QQmlPropertyData *data = &methodIndexCache[ii - methodIndexCacheStart];
427         QQmlPropertyData *sigdata = 0;
428
429         data->lazyLoad(m);
430
431         if (data->isSignal())
432             data->flags |= signalFlags;
433         else
434             data->flags |= methodFlags;
435
436         if (!dynamicMetaObject)
437             data->flags |= QQmlPropertyData::IsDirect;
438
439         Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX);
440         data->metaObjectOffset = allowedRevisionCache.count() - 1;
441
442         if (data->isSignal()) {
443             sigdata = &signalHandlerIndexCache[signalHandlerIndex - signalHanderIndexCacheStart];
444             *sigdata = *data;
445             sigdata->flags |= QQmlPropertyData::IsSignalHandler;
446         }
447
448         QQmlPropertyData *old = 0;
449
450         if (utf8) {
451             QHashedString methodName(QString::fromUtf8(signature, cptr - signature));
452             if (QQmlPropertyData **it = stringCache.value(methodName))
453                 old = *it;
454             stringCache.insert(methodName, data);
455
456             if (data->isSignal()) {
457                 QHashedString on(QStringLiteral("on") % methodName.at(0).toUpper() % methodName.midRef(1));
458                 stringCache.insert(on, sigdata);
459                 ++signalHandlerIndex;
460             }
461         } else {
462             QHashedCStringRef methodName(signature, cptr - signature);
463             if (QQmlPropertyData **it = stringCache.value(methodName))
464                 old = *it;
465             stringCache.insert(methodName, data);
466
467             if (data->isSignal()) {
468                 int length = methodName.length();
469
470                 QVarLengthArray<char, 128> str(length+3);
471                 str[0] = 'o';
472                 str[1] = 'n';
473                 str[2] = toupper(signature[0]);
474                 if (length > 1)
475                     memcpy(&str[3], &signature[1], length - 1);
476                 str[length + 2] = '\0';
477
478                 QHashedString on(QString::fromLatin1(str.data()));
479                 stringCache.insert(on, sigdata);
480                 ++signalHandlerIndex;
481             }
482         }
483
484         if (old) {
485             // We only overload methods in the same class, exactly like C++
486             if (old->isFunction() && old->coreIndex >= methodOffset)
487                 data->flags |= QQmlPropertyData::IsOverload;
488             data->overrideIndexIsProperty = !old->isFunction();
489             data->overrideIndex = old->coreIndex;
490         }
491     }
492
493     int propCount = metaObject->propertyCount();
494     int propOffset = metaObject->propertyOffset();
495
496     // update() should have reserved enough space in the vector that this doesn't cause a realloc
497     // and invalidate the stringCache.
498     propertyIndexCache.resize(propCount - propertyIndexCacheStart);
499     for (int ii = propOffset; ii < propCount; ++ii) {
500         QMetaProperty p = metaObject->property(ii);
501         if (!p.isScriptable())
502             continue;
503
504         const char *str = p.name();
505         char utf8 = 0;
506         const char *cptr = str;
507         while (*cptr != 0) {
508             utf8 |= *cptr & 0x80;
509             ++cptr;
510         }
511
512         QQmlPropertyData *data = &propertyIndexCache[ii - propertyIndexCacheStart];
513
514         data->lazyLoad(p, engine);
515         data->flags |= propertyFlags;
516
517         if (!dynamicMetaObject) 
518             data->flags |= QQmlPropertyData::IsDirect;
519
520         Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX);
521         data->metaObjectOffset = allowedRevisionCache.count() - 1;
522
523         QQmlPropertyData *old = 0;
524
525         if (utf8) {
526             QHashedString propName(QString::fromUtf8(str, cptr - str));
527             if (QQmlPropertyData **it = stringCache.value(propName))
528                 old = *it;
529             stringCache.insert(propName, data);
530         } else {
531             QHashedCStringRef propName(str, cptr - str);
532             if (QQmlPropertyData **it = stringCache.value(propName))
533                 old = *it;
534             stringCache.insert(propName, data);
535         }
536
537         QQmlAccessorProperties::Property *accessorProperty = accessorProperties.property(str);
538
539         // Fast properties may not be overrides or revisioned
540         Q_ASSERT(accessorProperty == 0 || (old == 0 && data->revision == 0));
541
542         if (accessorProperty) {
543             data->flags |= QQmlPropertyData::HasAccessors;
544             data->accessors = accessorProperty->accessors;
545             data->accessorData = accessorProperty->data;
546         } else if (old) {
547             data->overrideIndexIsProperty = !old->isFunction();
548             data->overrideIndex = old->coreIndex;
549         }
550     }
551 }
552
553 void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
554 {
555     Q_ASSERT(data->notFullyResolved());
556
557     data->propType = QMetaType::type(data->propTypeName);
558
559     if (!data->isFunction())
560         data->flags |= flagsForPropertyType(data->propType, engine);
561
562     data->flags &= ~QQmlPropertyData::NotFullyResolved;
563 }
564
565 void QQmlPropertyCache::updateRecur(QQmlEngine *engine, const QMetaObject *metaObject)
566 {
567     if (!metaObject)
568         return;
569
570     updateRecur(engine, metaObject->superClass());
571
572     append(engine, metaObject);
573 }
574
575 void QQmlPropertyCache::update(QQmlEngine *engine, const QMetaObject *metaObject)
576 {
577     Q_ASSERT(engine);
578     Q_ASSERT(metaObject);
579     Q_ASSERT(stringCache.isEmpty());
580
581     // Preallocate enough space in the index caches for all the properties/methods/signals that
582     // are not cached in a parent cache so that the caches never need to be reallocated as this
583     // would invalidate pointers stored in the stringCache.
584     int pc = metaObject->propertyCount();
585     int mc = metaObject->methodCount();
586     int sc = metaObjectSignalCount(metaObject);
587     propertyIndexCache.reserve(pc - propertyIndexCacheStart);
588     methodIndexCache.reserve(mc - methodIndexCacheStart);
589     signalHandlerIndexCache.reserve(sc - signalHanderIndexCacheStart);
590
591     // Reserve enough space in the stringCache for all properties/methods/signals including those
592     // cached in a parent cache.
593     stringCache.reserve(pc + mc + sc);
594
595     updateRecur(engine,metaObject);
596 }
597
598 QQmlPropertyData *
599 QQmlPropertyCache::property(int index) const
600 {
601     if (index < 0 || index >= (propertyIndexCacheStart + propertyIndexCache.count()))
602         return 0;
603     
604     if (index < propertyIndexCacheStart)
605         return parent->property(index);
606
607     QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&propertyIndexCache.at(index - propertyIndexCacheStart));
608     if (rv->notFullyResolved()) resolve(rv);
609     return rv;
610 }
611
612 QQmlPropertyData *
613 QQmlPropertyCache::method(int index) const
614 {
615     if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count()))
616         return 0;
617
618     if (index < methodIndexCacheStart)
619         return parent->method(index);
620
621     QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - methodIndexCacheStart));
622     if (rv->notFullyResolved()) resolve(rv);
623     return rv;
624 }
625
626 QQmlPropertyData *
627 QQmlPropertyCache::property(const QHashedStringRef &str) const
628 {
629     QQmlPropertyData **rv = stringCache.value(str);
630     if (rv && (*rv)->notFullyResolved()) resolve(*rv);
631     return rv?*rv:0;
632 }
633
634 QQmlPropertyData *
635 QQmlPropertyCache::property(const QHashedCStringRef &str) const
636 {
637     QQmlPropertyData **rv = stringCache.value(str);
638     if (rv && (*rv)->notFullyResolved()) resolve(*rv);
639     return rv?*rv:0;
640 }
641
642 QQmlPropertyData *
643 QQmlPropertyCache::property(const QString &str) const
644 {
645     QQmlPropertyData **rv = stringCache.value(str);
646     if (rv && (*rv)->notFullyResolved()) resolve(*rv);
647     return rv?*rv:0;
648 }
649
650 QString QQmlPropertyData::name(QObject *object)
651 {
652     if (!object)
653         return QString();
654
655     return name(object->metaObject());
656 }
657
658 QString QQmlPropertyData::name(const QMetaObject *metaObject)
659 {
660     if (!metaObject || coreIndex == -1)
661         return QString();
662
663     if (flags & IsFunction) {
664         QMetaMethod m = metaObject->method(coreIndex);
665
666         QString name = QString::fromUtf8(m.signature());
667         int parenIdx = name.indexOf(QLatin1Char('('));
668         if (parenIdx != -1)
669             name = name.left(parenIdx);
670         return name;
671     } else {
672         QMetaProperty p = metaObject->property(coreIndex);
673         return QString::fromUtf8(p.name());
674     }
675 }
676
677 QStringList QQmlPropertyCache::propertyNames() const
678 {
679     QStringList keys;
680     for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) 
681         keys.append(iter.key());
682     return keys;
683 }
684
685 static int EnumType(const QMetaObject *meta, const QByteArray &str)
686 {
687     QByteArray scope;
688     QByteArray name;
689     int scopeIdx = str.lastIndexOf("::");
690     if (scopeIdx != -1) {
691         scope = str.left(scopeIdx);
692         name = str.mid(scopeIdx + 2);
693     } else { 
694         name = str;
695     }
696     for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
697         QMetaEnum m = meta->enumerator(i);
698         if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
699             return QVariant::Int;
700     }
701     return QVariant::Invalid;
702 }
703
704 // Returns an array of the arguments for method \a index.  The first entry in the array
705 // is the number of arguments.
706 int *QQmlPropertyCache::methodParameterTypes(QObject *object, int index, 
707                                                      QVarLengthArray<int, 9> &dummy,
708                                                      QByteArray *unknownTypeError)
709 {
710     Q_ASSERT(object && index >= 0);
711
712     QQmlData *ddata = QQmlData::get(object, false);
713
714     if (ddata && ddata->propertyCache) {
715         typedef QQmlPropertyCacheMethodArguments A;
716
717         QQmlPropertyCache *c = ddata->propertyCache;
718         Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count());
719
720         while (index < c->methodIndexCacheStart)
721             c = c->parent;
722
723         QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart));
724
725         if (rv->arguments)  
726             return static_cast<A *>(rv->arguments)->arguments;
727
728         const QMetaObject *metaObject = object->metaObject();
729         QMetaMethod m = metaObject->method(index);
730         QList<QByteArray> argTypeNames = m.parameterTypes();
731
732         A *args = static_cast<A *>(malloc(sizeof(A) + (argTypeNames.count() + 1) * sizeof(int)));
733         args->arguments[0] = argTypeNames.count();
734
735         for (int ii = 0; ii < argTypeNames.count(); ++ii) {
736             int type = QMetaType::type(argTypeNames.at(ii));
737             if ((QMetaType::typeFlags(type) & QMetaType::IsEnumeration) == QMetaType::IsEnumeration)
738                 type = QVariant::Int;
739             else if (type == QVariant::Invalid)
740                 type = EnumType(object->metaObject(), argTypeNames.at(ii));
741             if (type == QVariant::Invalid) {
742                 if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii);
743                 free(args);
744                 return 0;
745             }
746             args->arguments[ii + 1] = type;
747         }
748
749         rv->arguments = args;
750         args->next = c->argumentsCache;
751         c->argumentsCache = args;
752         return static_cast<A *>(rv->arguments)->arguments;
753
754     } else {
755         QMetaMethod m = object->metaObject()->method(index);
756         QList<QByteArray> argTypeNames = m.parameterTypes();
757         dummy.resize(argTypeNames.count() + 1);
758         dummy[0] = argTypeNames.count();
759
760         for (int ii = 0; ii < argTypeNames.count(); ++ii) {
761             int type = QMetaType::type(argTypeNames.at(ii));
762             if ((QMetaType::typeFlags(type) & QMetaType::IsEnumeration) == QMetaType::IsEnumeration)
763                 type = QVariant::Int;
764             else if (type == QVariant::Invalid)
765                 type = EnumType(object->metaObject(), argTypeNames.at(ii));
766             if (type == QVariant::Invalid) {
767                 if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii);
768                 return 0;
769             }
770             dummy[ii + 1] = type;
771         }
772
773         return dummy.data();
774     }
775 }
776
777 QQmlPropertyData qQmlPropertyCacheCreate(const QMetaObject *metaObject,
778                                                          const QString &property)
779 {
780     Q_ASSERT(metaObject);
781
782     QQmlPropertyData rv;
783     {
784         const QMetaObject *cmo = metaObject;
785         const QByteArray propertyName = property.toUtf8();
786         while (cmo) {
787             int idx = cmo->indexOfProperty(propertyName);
788             if (idx != -1) {
789                 QMetaProperty p = cmo->property(idx);
790                 if (p.isScriptable()) {
791                     rv.load(p);
792                     return rv;
793                 } else {
794                     while (cmo && cmo->propertyOffset() >= idx)
795                         cmo = cmo->superClass();
796                 }
797             } else {
798                 cmo = 0;
799             }
800         }
801     }
802
803     int methodCount = metaObject->methodCount();
804     int defaultMethods = QObject::staticMetaObject.methodCount();
805     for (int ii = methodCount - 1; ii >= defaultMethods; --ii) {
806         // >=defaultMethods to block the signals and slots of QObject::staticMetaObject
807         // incl. destroyed signals, objectNameChanged signal, deleteLater slot, _q_reregisterTimers slot.
808         QMetaMethod m = metaObject->method(ii);
809         if (m.access() == QMetaMethod::Private)
810             continue;
811         QString methodName = QString::fromUtf8(m.signature());
812
813         int parenIdx = methodName.indexOf(QLatin1Char('('));
814         Q_ASSERT(parenIdx != -1);
815         QStringRef methodNameRef = methodName.leftRef(parenIdx);
816
817         if (methodNameRef == property) {
818             rv.load(m);
819             return rv;
820         }
821     }
822
823     return rv;
824 }
825
826 inline const QString &qQmlPropertyCacheToString(const QString &string)
827 {
828     return string;
829 }
830
831 inline QString qQmlPropertyCacheToString(const QHashedV8String &string)
832 {
833     return QV8Engine::toStringStatic(string.string());
834 }
835
836 template<typename T>
837 QQmlPropertyData *
838 qQmlPropertyCacheProperty(QQmlEngine *engine, QObject *obj,
839                                   const T &name, QQmlPropertyData &local)
840 {
841     QQmlPropertyCache *cache = 0;
842
843     if (engine) {
844         QQmlData *ddata = QQmlData::get(obj);
845
846         if (ddata && ddata->propertyCache) {
847             cache = ddata->propertyCache;
848         } else if (engine) {
849             QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
850             cache = ep->cache(obj);
851             if (cache) {
852                 ddata = QQmlData::get(obj, true);
853                 cache->addref();
854                 ddata->propertyCache = cache;
855             }
856         }
857     }
858
859     QQmlPropertyData *rv = 0;
860
861     if (cache) {
862         rv = cache->property(name);
863     } else {
864         local = qQmlPropertyCacheCreate(obj->metaObject(),
865                                                 qQmlPropertyCacheToString(name));
866         if (local.isValid())
867             rv = &local;
868     }
869
870     return rv;
871 }
872
873 QQmlPropertyData *
874 QQmlPropertyCache::property(QQmlEngine *engine, QObject *obj, 
875                                     const QHashedV8String &name, QQmlPropertyData &local)
876 {
877     return qQmlPropertyCacheProperty<QHashedV8String>(engine, obj, name, local);
878 }
879
880 QQmlPropertyData *
881 QQmlPropertyCache::property(QQmlEngine *engine, QObject *obj,
882                                     const QString &name, QQmlPropertyData &local)
883 {
884     return qQmlPropertyCacheProperty<QString>(engine, obj, name, local);
885 }
886
887 static inline const QMetaObjectPrivate *priv(const uint* data)
888 { return reinterpret_cast<const QMetaObjectPrivate*>(data); }
889
890 bool QQmlPropertyCache::isDynamicMetaObject(const QMetaObject *mo)
891 {
892     return priv(mo->d.data)->revision >= 3 && priv(mo->d.data)->flags & DynamicMetaObject;
893 }
894
895 QT_END_NAMESPACE