Merge master into api_changes
[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 struct StaticQtMetaObject : public QObject
678 {
679     static const QMetaObject *get()
680         { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; }
681 };
682
683 static int EnumType(const QMetaObject *metaobj, const QByteArray &str)
684 {
685     QByteArray scope;
686     QByteArray name;
687     int scopeIdx = str.lastIndexOf("::");
688     if (scopeIdx != -1) {
689         scope = str.left(scopeIdx);
690         name = str.mid(scopeIdx + 2);
691     } else { 
692         name = str;
693     }
694     const QMetaObject *meta;
695     if (scope == "Qt")
696         meta = StaticQtMetaObject::get();
697     else
698         meta = metaobj;
699     for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
700         QMetaEnum m = meta->enumerator(i);
701         if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
702             return QVariant::Int;
703     }
704     return QVariant::Invalid;
705 }
706
707 // Returns an array of the arguments for method \a index.  The first entry in the array
708 // is the number of arguments.
709 int *QQmlPropertyCache::methodParameterTypes(QObject *object, int index, 
710                                                      QVarLengthArray<int, 9> &dummy,
711                                                      QByteArray *unknownTypeError)
712 {
713     Q_ASSERT(object && index >= 0);
714
715     QQmlData *ddata = QQmlData::get(object, false);
716
717     if (ddata && ddata->propertyCache) {
718         typedef QQmlPropertyCacheMethodArguments A;
719
720         QQmlPropertyCache *c = ddata->propertyCache;
721         Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count());
722
723         while (index < c->methodIndexCacheStart)
724             c = c->parent;
725
726         QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart));
727
728         if (rv->arguments)  
729             return static_cast<A *>(rv->arguments)->arguments;
730
731         const QMetaObject *metaObject = object->metaObject();
732         QMetaMethod m = metaObject->method(index);
733
734         int argc = m.parameterCount();
735         A *args = static_cast<A *>(malloc(sizeof(A) + (argc + 1) * sizeof(int)));
736         args->arguments[0] = argc;
737         QList<QByteArray> argTypeNames; // Only loaded if needed
738
739         for (int ii = 0; ii < argc; ++ii) {
740             int type = m.parameterType(ii);
741             if ((QMetaType::typeFlags(type) & QMetaType::IsEnumeration) == QMetaType::IsEnumeration)
742                 type = QVariant::Int;
743             else if (type == QMetaType::UnknownType) {
744                 if (argTypeNames.isEmpty())
745                     argTypeNames = m.parameterTypes();
746                 type = EnumType(object->metaObject(), argTypeNames.at(ii));
747             }
748             if (type == QMetaType::UnknownType) {
749                 if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii);
750                 free(args);
751                 return 0;
752             }
753             args->arguments[ii + 1] = type;
754         }
755
756         rv->arguments = args;
757         args->next = c->argumentsCache;
758         c->argumentsCache = args;
759         return static_cast<A *>(rv->arguments)->arguments;
760
761     } else {
762         QMetaMethod m = object->metaObject()->method(index);
763         int argc = m.parameterCount();
764         dummy.resize(argc + 1);
765         dummy[0] = argc;
766         QList<QByteArray> argTypeNames; // Only loaded if needed
767
768         for (int ii = 0; ii < argc; ++ii) {
769             int type = m.parameterType(ii);
770             if ((QMetaType::typeFlags(type) & QMetaType::IsEnumeration) == QMetaType::IsEnumeration)
771                 type = QVariant::Int;
772             else if (type == QMetaType::UnknownType) {
773                 if (argTypeNames.isEmpty())
774                     argTypeNames = m.parameterTypes();
775                 type = EnumType(object->metaObject(), argTypeNames.at(ii));
776             }
777             if (type == QMetaType::UnknownType) {
778                 if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii);
779                 return 0;
780             }
781             dummy[ii + 1] = type;
782         }
783
784         return dummy.data();
785     }
786 }
787
788 QQmlPropertyData qQmlPropertyCacheCreate(const QMetaObject *metaObject,
789                                                          const QString &property)
790 {
791     Q_ASSERT(metaObject);
792
793     QQmlPropertyData rv;
794     {
795         const QMetaObject *cmo = metaObject;
796         const QByteArray propertyName = property.toUtf8();
797         while (cmo) {
798             int idx = cmo->indexOfProperty(propertyName);
799             if (idx != -1) {
800                 QMetaProperty p = cmo->property(idx);
801                 if (p.isScriptable()) {
802                     rv.load(p);
803                     return rv;
804                 } else {
805                     while (cmo && cmo->propertyOffset() >= idx)
806                         cmo = cmo->superClass();
807                 }
808             } else {
809                 cmo = 0;
810             }
811         }
812     }
813
814     int methodCount = metaObject->methodCount();
815     int defaultMethods = QObject::staticMetaObject.methodCount();
816     for (int ii = methodCount - 1; ii >= defaultMethods; --ii) {
817         // >=defaultMethods to block the signals and slots of QObject::staticMetaObject
818         // incl. destroyed signals, objectNameChanged signal, deleteLater slot, _q_reregisterTimers slot.
819         QMetaMethod m = metaObject->method(ii);
820         if (m.access() == QMetaMethod::Private)
821             continue;
822         QString methodName = QString::fromUtf8(m.name().constData());
823
824         if (methodName == property) {
825             rv.load(m);
826             return rv;
827         }
828     }
829
830     return rv;
831 }
832
833 inline const QString &qQmlPropertyCacheToString(const QString &string)
834 {
835     return string;
836 }
837
838 inline QString qQmlPropertyCacheToString(const QHashedV8String &string)
839 {
840     return QV8Engine::toStringStatic(string.string());
841 }
842
843 template<typename T>
844 QQmlPropertyData *
845 qQmlPropertyCacheProperty(QQmlEngine *engine, QObject *obj,
846                                   const T &name, QQmlPropertyData &local)
847 {
848     QQmlPropertyCache *cache = 0;
849
850     if (engine) {
851         QQmlData *ddata = QQmlData::get(obj);
852
853         if (ddata && ddata->propertyCache) {
854             cache = ddata->propertyCache;
855         } else if (engine) {
856             QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
857             cache = ep->cache(obj);
858             if (cache) {
859                 ddata = QQmlData::get(obj, true);
860                 cache->addref();
861                 ddata->propertyCache = cache;
862             }
863         }
864     }
865
866     QQmlPropertyData *rv = 0;
867
868     if (cache) {
869         rv = cache->property(name);
870     } else {
871         local = qQmlPropertyCacheCreate(obj->metaObject(),
872                                                 qQmlPropertyCacheToString(name));
873         if (local.isValid())
874             rv = &local;
875     }
876
877     return rv;
878 }
879
880 QQmlPropertyData *
881 QQmlPropertyCache::property(QQmlEngine *engine, QObject *obj, 
882                                     const QHashedV8String &name, QQmlPropertyData &local)
883 {
884     return qQmlPropertyCacheProperty<QHashedV8String>(engine, obj, name, local);
885 }
886
887 QQmlPropertyData *
888 QQmlPropertyCache::property(QQmlEngine *engine, QObject *obj,
889                                     const QString &name, QQmlPropertyData &local)
890 {
891     return qQmlPropertyCacheProperty<QString>(engine, obj, name, local);
892 }
893
894 static inline const QMetaObjectPrivate *priv(const uint* data)
895 { return reinterpret_cast<const QMetaObjectPrivate*>(data); }
896
897 bool QQmlPropertyCache::isDynamicMetaObject(const QMetaObject *mo)
898 {
899     return priv(mo->d.data)->revision >= 3 && priv(mo->d.data)->flags & DynamicMetaObject;
900 }
901
902 QT_END_NAMESPACE