Adapt to QMetaMethod::typeName() behavioral change for "void"
[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 = m.returnType();
180
181     if (m.parameterCount()) {
182         flags |= HasArguments;
183         if ((m.parameterCount() == 1) && (m.parameterTypes().first() == "QQmlV8Function*")) {
184             flags |= IsV8Function;
185         }
186     }
187
188     Q_ASSERT(m.revision() <= Q_INT16_MAX);
189     revision = m.revision();
190 }
191
192 void QQmlPropertyData::lazyLoad(const QMetaMethod &m)
193 {
194     coreIndex = m.methodIndex();
195     arguments = 0;
196     flags |= IsFunction;
197     if (m.methodType() == QMetaMethod::Signal)
198         flags |= IsSignal;
199     propType = QMetaType::Void;
200
201     const char *returnType = m.typeName();
202     Q_ASSERT(returnType != 0);
203     if ((*returnType != 'v') || (qstrcmp(returnType+1, "oid") != 0)) {
204         propTypeName = returnType;
205         flags |= NotFullyResolved;
206     }
207
208     if (m.parameterCount()) {
209         flags |= HasArguments;
210         if ((m.parameterCount() == 1) && (m.parameterTypes().first() == "QQmlV8Function*")) {
211             flags |= IsV8Function;
212         }
213     }
214
215     Q_ASSERT(m.revision() <= Q_INT16_MAX);
216     revision = m.revision();
217 }
218
219 /*!
220 Creates a new empty QQmlPropertyCache.
221 */
222 QQmlPropertyCache::QQmlPropertyCache(QQmlEngine *e)
223 : engine(e), parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0),
224   signalHanderIndexCacheStart(0), metaObject(0), argumentsCache(0)
225 {
226     Q_ASSERT(engine);
227 }
228
229 /*!
230 Creates a new QQmlPropertyCache of \a metaObject.
231 */
232 QQmlPropertyCache::QQmlPropertyCache(QQmlEngine *e, const QMetaObject *metaObject)
233 : engine(e), parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0),
234   signalHanderIndexCacheStart(0), metaObject(0), argumentsCache(0)
235 {
236     Q_ASSERT(engine);
237     Q_ASSERT(metaObject);
238
239     update(engine, metaObject);
240 }
241
242 QQmlPropertyCache::~QQmlPropertyCache()
243 {
244     clear();
245
246     QQmlPropertyCacheMethodArguments *args = argumentsCache;
247     while (args) {
248         QQmlPropertyCacheMethodArguments *next = args->next;
249         free(args);
250         args = next;
251     }
252
253     // We must clear this prior to releasing the parent incase it is a
254     // linked hash
255     stringCache.clear();
256     if (parent) parent->release();
257     parent = 0;
258     engine = 0;
259 }
260
261 void QQmlPropertyCache::destroy()
262 {
263     Q_ASSERT(engine || constructor.IsEmpty());
264     if (constructor.IsEmpty())
265         delete this;
266     else
267         QQmlEnginePrivate::deleteInEngineThread(engine, this);
268 }
269
270 // This is inherited from QQmlCleanup, so it should only clear the things
271 // that are tied to the specific QQmlEngine.
272 void QQmlPropertyCache::clear()
273 {
274     qPersistentDispose(constructor);
275     engine = 0;
276 }
277
278 QQmlPropertyCache *QQmlPropertyCache::copy(int reserve)
279 {
280     QQmlPropertyCache *cache = new QQmlPropertyCache(engine);
281     cache->parent = this;
282     cache->parent->addref();
283     cache->propertyIndexCacheStart = propertyIndexCache.count() + propertyIndexCacheStart;
284     cache->methodIndexCacheStart = methodIndexCache.count() + methodIndexCacheStart;
285     cache->signalHanderIndexCacheStart = signalHandlerIndexCache.count() + signalHanderIndexCacheStart;
286     cache->stringCache.linkAndReserve(stringCache, reserve);
287     cache->allowedRevisionCache = allowedRevisionCache;
288     cache->metaObject = metaObject;
289
290     // We specifically do *NOT* copy the constructor
291
292     return cache;
293 }
294
295 QQmlPropertyCache *QQmlPropertyCache::copy()
296 {
297     return copy(0);
298 }
299
300 QQmlPropertyCache *
301 QQmlPropertyCache::copyAndAppend(QQmlEngine *engine, const QMetaObject *metaObject,
302                                          QQmlPropertyData::Flag propertyFlags,
303                                          QQmlPropertyData::Flag methodFlags,
304                                          QQmlPropertyData::Flag signalFlags)
305 {
306     return copyAndAppend(engine, metaObject, -1, propertyFlags, methodFlags, signalFlags);
307 }
308
309 QQmlPropertyCache *
310 QQmlPropertyCache::copyAndAppend(QQmlEngine *engine, const QMetaObject *metaObject,
311                                          int revision,
312                                          QQmlPropertyData::Flag propertyFlags,
313                                          QQmlPropertyData::Flag methodFlags,
314                                          QQmlPropertyData::Flag signalFlags)
315 {
316     Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4);
317
318     // Reserve enough space in the name hash for all the methods (including signals), all the
319     // signal handlers and all the properties.  This assumes no name clashes, but this is the
320     // common case.
321     QQmlPropertyCache *rv = copy(QMetaObjectPrivate::get(metaObject)->methodCount +
322                                          QMetaObjectPrivate::get(metaObject)->signalCount +
323                                          QMetaObjectPrivate::get(metaObject)->propertyCount);
324
325     rv->append(engine, metaObject, revision, propertyFlags, methodFlags, signalFlags);
326
327     return rv;
328 }
329
330 void QQmlPropertyCache::append(QQmlEngine *engine, const QMetaObject *metaObject, 
331                                        QQmlPropertyData::Flag propertyFlags,
332                                        QQmlPropertyData::Flag methodFlags,
333                                        QQmlPropertyData::Flag signalFlags)
334 {
335     append(engine, metaObject, -1, propertyFlags, methodFlags, signalFlags);
336 }
337
338 void QQmlPropertyCache::append(QQmlEngine *engine, const QMetaObject *metaObject, 
339                                        int revision, 
340                                        QQmlPropertyData::Flag propertyFlags,
341                                        QQmlPropertyData::Flag methodFlags,
342                                        QQmlPropertyData::Flag signalFlags)
343 {
344     Q_UNUSED(revision);
345     Q_ASSERT(constructor.IsEmpty()); // We should not be appending to an in-use property cache
346
347     this->metaObject = metaObject;
348
349     bool dynamicMetaObject = isDynamicMetaObject(metaObject);
350
351     allowedRevisionCache.append(0);
352
353     int methodCount = metaObject->methodCount();
354     Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4);
355     int signalCount = metaObjectSignalCount(metaObject);
356     int classInfoCount = QMetaObjectPrivate::get(metaObject)->classInfoCount;
357
358     QQmlAccessorProperties::Properties accessorProperties;
359
360     // Special case QObject as we don't want to add a qt_HasQmlAccessors classinfo to it
361     if (metaObject == &QObject::staticMetaObject) {
362         accessorProperties = QQmlAccessorProperties::properties(metaObject);
363     } else if (classInfoCount) {
364         int classInfoOffset = metaObject->classInfoOffset();
365         bool hasFastProperty = false;
366         for (int ii = 0; ii < classInfoCount; ++ii) {
367             int idx = ii + classInfoOffset;
368
369             if (0 == qstrcmp(metaObject->classInfo(idx).name(), "qt_HasQmlAccessors")) {
370                 hasFastProperty = true;
371                 break;
372             }
373         }
374
375         if (hasFastProperty) {
376             accessorProperties = QQmlAccessorProperties::properties(metaObject);
377             if (accessorProperties.count == 0)
378                 qFatal("QQmlPropertyCache: %s has FastProperty class info, but has not "
379                        "installed property accessors", metaObject->className());
380         } else {
381 #ifndef QT_NO_DEBUG
382             accessorProperties = QQmlAccessorProperties::properties(metaObject);
383             if (accessorProperties.count != 0)
384                 qFatal("QQmlPropertyCache: %s has fast property accessors, but is missing "
385                        "FastProperty class info", metaObject->className());
386 #endif
387         }
388     }
389
390     // qMax(defaultMethods, methodOffset) to block the signals and slots of QObject::staticMetaObject
391     // incl. destroyed signals, objectNameChanged signal, deleteLater slot, _q_reregisterTimers slot.
392     int methodOffset = qMax(QObject::staticMetaObject.methodCount(), metaObject->methodOffset());
393     int signalOffset = signalCount - QMetaObjectPrivate::get(metaObject)->signalCount;
394
395     // update() should have reserved enough space in the vector that this doesn't cause a realloc
396     // and invalidate the stringCache.
397     methodIndexCache.resize(methodCount - methodIndexCacheStart);
398     signalHandlerIndexCache.resize(signalCount - signalHanderIndexCacheStart);
399     int signalHandlerIndex = signalOffset;
400     for (int ii = methodOffset; ii < methodCount; ++ii) {
401         QMetaMethod m = metaObject->method(ii);
402         if (m.access() == QMetaMethod::Private) 
403             continue;
404
405         // Extract method name
406         const char *signature;
407         if (QMetaObjectPrivate::get(metaObject)->revision >= 7) {
408             // Safe to use the raw name pointer
409             signature = m.name().constData();
410         } else {
411             // Safe to use the raw signature pointer
412             signature = m.methodSignature().constData();
413         }
414         const char *cptr = signature;
415         char utf8 = 0;
416         while (*cptr && *cptr != '(') {
417             Q_ASSERT(*cptr != 0);
418             utf8 |= *cptr & 0x80;
419             ++cptr;
420         }
421
422         QQmlPropertyData *data = &methodIndexCache[ii - methodIndexCacheStart];
423         QQmlPropertyData *sigdata = 0;
424
425         data->lazyLoad(m);
426
427         if (data->isSignal())
428             data->flags |= signalFlags;
429         else
430             data->flags |= methodFlags;
431
432         if (!dynamicMetaObject)
433             data->flags |= QQmlPropertyData::IsDirect;
434
435         Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX);
436         data->metaObjectOffset = allowedRevisionCache.count() - 1;
437
438         if (data->isSignal()) {
439             sigdata = &signalHandlerIndexCache[signalHandlerIndex - signalHanderIndexCacheStart];
440             *sigdata = *data;
441             sigdata->flags |= QQmlPropertyData::IsSignalHandler;
442         }
443
444         QQmlPropertyData *old = 0;
445
446         if (utf8) {
447             QHashedString methodName(QString::fromUtf8(signature, cptr - signature));
448             if (QQmlPropertyData **it = stringCache.value(methodName))
449                 old = *it;
450             stringCache.insert(methodName, data);
451
452             if (data->isSignal()) {
453                 QHashedString on(QStringLiteral("on") % methodName.at(0).toUpper() % methodName.midRef(1));
454                 stringCache.insert(on, sigdata);
455                 ++signalHandlerIndex;
456             }
457         } else {
458             QHashedCStringRef methodName(signature, cptr - signature);
459             if (QQmlPropertyData **it = stringCache.value(methodName))
460                 old = *it;
461             stringCache.insert(methodName, data);
462
463             if (data->isSignal()) {
464                 int length = methodName.length();
465
466                 QVarLengthArray<char, 128> str(length+3);
467                 str[0] = 'o';
468                 str[1] = 'n';
469                 str[2] = toupper(signature[0]);
470                 if (length > 1)
471                     memcpy(&str[3], &signature[1], length - 1);
472                 str[length + 2] = '\0';
473
474                 QHashedString on(QString::fromLatin1(str.data()));
475                 stringCache.insert(on, sigdata);
476                 ++signalHandlerIndex;
477             }
478         }
479
480         if (old) {
481             // We only overload methods in the same class, exactly like C++
482             if (old->isFunction() && old->coreIndex >= methodOffset)
483                 data->flags |= QQmlPropertyData::IsOverload;
484             data->overrideIndexIsProperty = !old->isFunction();
485             data->overrideIndex = old->coreIndex;
486         }
487     }
488
489     int propCount = metaObject->propertyCount();
490     int propOffset = metaObject->propertyOffset();
491
492     // update() should have reserved enough space in the vector that this doesn't cause a realloc
493     // and invalidate the stringCache.
494     propertyIndexCache.resize(propCount - propertyIndexCacheStart);
495     for (int ii = propOffset; ii < propCount; ++ii) {
496         QMetaProperty p = metaObject->property(ii);
497         if (!p.isScriptable())
498             continue;
499
500         const char *str = p.name();
501         char utf8 = 0;
502         const char *cptr = str;
503         while (*cptr != 0) {
504             utf8 |= *cptr & 0x80;
505             ++cptr;
506         }
507
508         QQmlPropertyData *data = &propertyIndexCache[ii - propertyIndexCacheStart];
509
510         data->lazyLoad(p, engine);
511         data->flags |= propertyFlags;
512
513         if (!dynamicMetaObject) 
514             data->flags |= QQmlPropertyData::IsDirect;
515
516         Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX);
517         data->metaObjectOffset = allowedRevisionCache.count() - 1;
518
519         QQmlPropertyData *old = 0;
520
521         if (utf8) {
522             QHashedString propName(QString::fromUtf8(str, cptr - str));
523             if (QQmlPropertyData **it = stringCache.value(propName))
524                 old = *it;
525             stringCache.insert(propName, data);
526         } else {
527             QHashedCStringRef propName(str, cptr - str);
528             if (QQmlPropertyData **it = stringCache.value(propName))
529                 old = *it;
530             stringCache.insert(propName, data);
531         }
532
533         QQmlAccessorProperties::Property *accessorProperty = accessorProperties.property(str);
534
535         // Fast properties may not be overrides or revisioned
536         Q_ASSERT(accessorProperty == 0 || (old == 0 && data->revision == 0));
537
538         if (accessorProperty) {
539             data->flags |= QQmlPropertyData::HasAccessors;
540             data->accessors = accessorProperty->accessors;
541             data->accessorData = accessorProperty->data;
542         } else if (old) {
543             data->overrideIndexIsProperty = !old->isFunction();
544             data->overrideIndex = old->coreIndex;
545         }
546     }
547 }
548
549 void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
550 {
551     Q_ASSERT(data->notFullyResolved());
552
553     data->propType = QMetaType::type(data->propTypeName);
554
555     if (!data->isFunction())
556         data->flags |= flagsForPropertyType(data->propType, engine);
557
558     data->flags &= ~QQmlPropertyData::NotFullyResolved;
559 }
560
561 void QQmlPropertyCache::updateRecur(QQmlEngine *engine, const QMetaObject *metaObject)
562 {
563     if (!metaObject)
564         return;
565
566     updateRecur(engine, metaObject->superClass());
567
568     append(engine, metaObject);
569 }
570
571 void QQmlPropertyCache::update(QQmlEngine *engine, const QMetaObject *metaObject)
572 {
573     Q_ASSERT(engine);
574     Q_ASSERT(metaObject);
575     Q_ASSERT(stringCache.isEmpty());
576
577     // Preallocate enough space in the index caches for all the properties/methods/signals that
578     // are not cached in a parent cache so that the caches never need to be reallocated as this
579     // would invalidate pointers stored in the stringCache.
580     int pc = metaObject->propertyCount();
581     int mc = metaObject->methodCount();
582     int sc = metaObjectSignalCount(metaObject);
583     propertyIndexCache.reserve(pc - propertyIndexCacheStart);
584     methodIndexCache.reserve(mc - methodIndexCacheStart);
585     signalHandlerIndexCache.reserve(sc - signalHanderIndexCacheStart);
586
587     // Reserve enough space in the stringCache for all properties/methods/signals including those
588     // cached in a parent cache.
589     stringCache.reserve(pc + mc + sc);
590
591     updateRecur(engine,metaObject);
592 }
593
594 QQmlPropertyData *
595 QQmlPropertyCache::property(int index) const
596 {
597     if (index < 0 || index >= (propertyIndexCacheStart + propertyIndexCache.count()))
598         return 0;
599     
600     if (index < propertyIndexCacheStart)
601         return parent->property(index);
602
603     QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&propertyIndexCache.at(index - propertyIndexCacheStart));
604     if (rv->notFullyResolved()) resolve(rv);
605     return rv;
606 }
607
608 QQmlPropertyData *
609 QQmlPropertyCache::method(int index) const
610 {
611     if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count()))
612         return 0;
613
614     if (index < methodIndexCacheStart)
615         return parent->method(index);
616
617     QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - methodIndexCacheStart));
618     if (rv->notFullyResolved()) resolve(rv);
619     return rv;
620 }
621
622 QQmlPropertyData *
623 QQmlPropertyCache::property(const QHashedStringRef &str) const
624 {
625     QQmlPropertyData **rv = stringCache.value(str);
626     if (rv && (*rv)->notFullyResolved()) resolve(*rv);
627     return rv?*rv:0;
628 }
629
630 QQmlPropertyData *
631 QQmlPropertyCache::property(const QHashedCStringRef &str) const
632 {
633     QQmlPropertyData **rv = stringCache.value(str);
634     if (rv && (*rv)->notFullyResolved()) resolve(*rv);
635     return rv?*rv:0;
636 }
637
638 QQmlPropertyData *
639 QQmlPropertyCache::property(const QString &str) const
640 {
641     QQmlPropertyData **rv = stringCache.value(str);
642     if (rv && (*rv)->notFullyResolved()) resolve(*rv);
643     return rv?*rv:0;
644 }
645
646 QString QQmlPropertyData::name(QObject *object)
647 {
648     if (!object)
649         return QString();
650
651     return name(object->metaObject());
652 }
653
654 QString QQmlPropertyData::name(const QMetaObject *metaObject)
655 {
656     if (!metaObject || coreIndex == -1)
657         return QString();
658
659     if (flags & IsFunction) {
660         QMetaMethod m = metaObject->method(coreIndex);
661
662         return QString::fromUtf8(m.name().constData());
663     } else {
664         QMetaProperty p = metaObject->property(coreIndex);
665         return QString::fromUtf8(p.name());
666     }
667 }
668
669 QStringList QQmlPropertyCache::propertyNames() const
670 {
671     QStringList keys;
672     for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) 
673         keys.append(iter.key());
674     return keys;
675 }
676
677 static int EnumType(const QMetaObject *meta, const QByteArray &str)
678 {
679     QByteArray scope;
680     QByteArray name;
681     int scopeIdx = str.lastIndexOf("::");
682     if (scopeIdx != -1) {
683         scope = str.left(scopeIdx);
684         name = str.mid(scopeIdx + 2);
685     } else { 
686         name = str;
687     }
688     for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
689         QMetaEnum m = meta->enumerator(i);
690         if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
691             return QVariant::Int;
692     }
693     return QVariant::Invalid;
694 }
695
696 // Returns an array of the arguments for method \a index.  The first entry in the array
697 // is the number of arguments.
698 int *QQmlPropertyCache::methodParameterTypes(QObject *object, int index, 
699                                                      QVarLengthArray<int, 9> &dummy,
700                                                      QByteArray *unknownTypeError)
701 {
702     Q_ASSERT(object && index >= 0);
703
704     QQmlData *ddata = QQmlData::get(object, false);
705
706     if (ddata && ddata->propertyCache) {
707         typedef QQmlPropertyCacheMethodArguments A;
708
709         QQmlPropertyCache *c = ddata->propertyCache;
710         Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count());
711
712         while (index < c->methodIndexCacheStart)
713             c = c->parent;
714
715         QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart));
716
717         if (rv->arguments)  
718             return static_cast<A *>(rv->arguments)->arguments;
719
720         const QMetaObject *metaObject = object->metaObject();
721         QMetaMethod m = metaObject->method(index);
722
723         int argc = m.parameterCount();
724         A *args = static_cast<A *>(malloc(sizeof(A) + (argc + 1) * sizeof(int)));
725         args->arguments[0] = argc;
726         QList<QByteArray> argTypeNames; // Only loaded if needed
727
728         for (int ii = 0; ii < argc; ++ii) {
729             int type = m.parameterType(ii);
730             if (type == QVariant::Invalid) {
731                 if (argTypeNames.isEmpty())
732                     argTypeNames = m.parameterTypes();
733                 type = EnumType(object->metaObject(), argTypeNames.at(ii));
734             }
735             if (type == QVariant::Invalid) {
736                 if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii);
737                 free(args);
738                 return 0;
739             }
740             args->arguments[ii + 1] = type;
741         }
742
743         rv->arguments = args;
744         args->next = c->argumentsCache;
745         c->argumentsCache = args;
746         return static_cast<A *>(rv->arguments)->arguments;
747
748     } else {
749         QMetaMethod m = object->metaObject()->method(index);
750         int argc = m.parameterCount();
751         dummy.resize(argc + 1);
752         dummy[0] = argc;
753         QList<QByteArray> argTypeNames; // Only loaded if needed
754
755         for (int ii = 0; ii < argc; ++ii) {
756             int type = m.parameterType(ii);
757             if (type == QVariant::Invalid) {
758                 if (argTypeNames.isEmpty())
759                     argTypeNames = m.parameterTypes();
760                 type = EnumType(object->metaObject(), argTypeNames.at(ii));
761             }
762             if (type == QVariant::Invalid) {
763                 if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii);
764                 return 0;
765             }
766             dummy[ii + 1] = type;
767         }
768
769         return dummy.data();
770     }
771 }
772
773 QQmlPropertyData qQmlPropertyCacheCreate(const QMetaObject *metaObject,
774                                                          const QString &property)
775 {
776     Q_ASSERT(metaObject);
777
778     QQmlPropertyData rv;
779     {
780         const QMetaObject *cmo = metaObject;
781         const QByteArray propertyName = property.toUtf8();
782         while (cmo) {
783             int idx = cmo->indexOfProperty(propertyName);
784             if (idx != -1) {
785                 QMetaProperty p = cmo->property(idx);
786                 if (p.isScriptable()) {
787                     rv.load(p);
788                     return rv;
789                 } else {
790                     while (cmo && cmo->propertyOffset() >= idx)
791                         cmo = cmo->superClass();
792                 }
793             } else {
794                 cmo = 0;
795             }
796         }
797     }
798
799     int methodCount = metaObject->methodCount();
800     int defaultMethods = QObject::staticMetaObject.methodCount();
801     for (int ii = methodCount - 1; ii >= defaultMethods; --ii) {
802         // >=defaultMethods to block the signals and slots of QObject::staticMetaObject
803         // incl. destroyed signals, objectNameChanged signal, deleteLater slot, _q_reregisterTimers slot.
804         QMetaMethod m = metaObject->method(ii);
805         if (m.access() == QMetaMethod::Private)
806             continue;
807         QString methodName = QString::fromUtf8(m.name().constData());
808
809         if (methodName == property) {
810             rv.load(m);
811             return rv;
812         }
813     }
814
815     return rv;
816 }
817
818 inline const QString &qQmlPropertyCacheToString(const QString &string)
819 {
820     return string;
821 }
822
823 inline QString qQmlPropertyCacheToString(const QHashedV8String &string)
824 {
825     return QV8Engine::toStringStatic(string.string());
826 }
827
828 template<typename T>
829 QQmlPropertyData *
830 qQmlPropertyCacheProperty(QQmlEngine *engine, QObject *obj,
831                                   const T &name, QQmlPropertyData &local)
832 {
833     QQmlPropertyCache *cache = 0;
834
835     if (engine) {
836         QQmlData *ddata = QQmlData::get(obj);
837
838         if (ddata && ddata->propertyCache) {
839             cache = ddata->propertyCache;
840         } else if (engine) {
841             QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
842             cache = ep->cache(obj);
843             if (cache) {
844                 ddata = QQmlData::get(obj, true);
845                 cache->addref();
846                 ddata->propertyCache = cache;
847             }
848         }
849     }
850
851     QQmlPropertyData *rv = 0;
852
853     if (cache) {
854         rv = cache->property(name);
855     } else {
856         local = qQmlPropertyCacheCreate(obj->metaObject(),
857                                                 qQmlPropertyCacheToString(name));
858         if (local.isValid())
859             rv = &local;
860     }
861
862     return rv;
863 }
864
865 QQmlPropertyData *
866 QQmlPropertyCache::property(QQmlEngine *engine, QObject *obj, 
867                                     const QHashedV8String &name, QQmlPropertyData &local)
868 {
869     return qQmlPropertyCacheProperty<QHashedV8String>(engine, obj, name, local);
870 }
871
872 QQmlPropertyData *
873 QQmlPropertyCache::property(QQmlEngine *engine, QObject *obj,
874                                     const QString &name, QQmlPropertyData &local)
875 {
876     return qQmlPropertyCacheProperty<QString>(engine, obj, name, local);
877 }
878
879 static inline const QMetaObjectPrivate *priv(const uint* data)
880 { return reinterpret_cast<const QMetaObjectPrivate*>(data); }
881
882 bool QQmlPropertyCache::isDynamicMetaObject(const QMetaObject *mo)
883 {
884     return priv(mo->d.data)->revision >= 3 && priv(mo->d.data)->flags & DynamicMetaObject;
885 }
886
887 QT_END_NAMESPACE