Fix leak introduced in 0853343c33e394f35c31c161b019b2aed17f9256.
[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 <private/qqmlengine_p.h>
45 #include <private/qqmlbinding_p.h>
46 #include <private/qqmlvmemetaobject_p.h>
47 #include <private/qv8engine_p.h>
48
49 #include <private/qmetaobject_p.h>
50 #include <private/qqmlaccessors_p.h>
51 #include <private/qmetaobjectbuilder_p.h>
52 #include <private/qqmlrewrite_p.h>
53
54 #include <QtCore/qdebug.h>
55
56 #include <ctype.h> // for toupper
57 #include <limits.h>
58
59 #ifdef Q_CC_MSVC
60 // nonstandard extension used : zero-sized array in struct/union.
61 #  pragma warning( disable : 4200 )
62 #endif
63
64 Q_DECLARE_METATYPE(QJSValue)
65 Q_DECLARE_METATYPE(QQmlV8Handle);
66
67 QT_BEGIN_NAMESPACE
68
69 #define Q_INT16_MAX 32767
70
71 class QQmlPropertyCacheMethodArguments
72 {
73 public:
74     QQmlPropertyCacheMethodArguments *next;
75
76     //for signal handler rewrites
77     QString *signalParameterStringForJS;
78     int signalParameterCountForJS:30;
79     int parameterError:1;
80     int argumentsValid:1;
81
82     QList<QByteArray> *names;
83     int arguments[0];
84 };
85
86 // Flags that do *NOT* depend on the property's QMetaProperty::userType() and thus are quick
87 // to load
88 static QQmlPropertyData::Flags fastFlagsForProperty(const QMetaProperty &p)
89 {
90     QQmlPropertyData::Flags flags;
91
92     if (p.isConstant())
93         flags |= QQmlPropertyData::IsConstant;
94     if (p.isWritable())
95         flags |= QQmlPropertyData::IsWritable;
96     if (p.isResettable())
97         flags |= QQmlPropertyData::IsResettable;
98     if (p.isFinal())
99         flags |= QQmlPropertyData::IsFinal;
100     if (p.isEnumType())
101         flags |= QQmlPropertyData::IsEnumType;
102
103     return flags;
104 }
105
106 // Flags that do depend on the property's QMetaProperty::userType() and thus are slow to
107 // load
108 static QQmlPropertyData::Flags flagsForPropertyType(int propType, QQmlEngine *engine)
109 {
110     Q_ASSERT(propType != -1);
111
112     QQmlPropertyData::Flags flags;
113
114     if (propType == QMetaType::QObjectStar) {
115         flags |= QQmlPropertyData::IsQObjectDerived;
116     } else if (propType == QMetaType::QVariant) {
117         flags |= QQmlPropertyData::IsQVariant;
118     } else if (propType < (int)QVariant::UserType) {
119     } else if (propType == qMetaTypeId<QQmlBinding *>()) {
120         flags |= QQmlPropertyData::IsQmlBinding;
121     } else if (propType == qMetaTypeId<QJSValue>()) {
122         flags |= QQmlPropertyData::IsQJSValue;
123     } else if (propType == qMetaTypeId<QQmlV8Handle>()) {
124         flags |= QQmlPropertyData::IsV8Handle;
125     } else {
126         QQmlMetaType::TypeCategory cat =
127             engine ? QQmlEnginePrivate::get(engine)->typeCategory(propType)
128                    : QQmlMetaType::typeCategory(propType);
129
130         if (cat == QQmlMetaType::Object)
131             flags |= QQmlPropertyData::IsQObjectDerived;
132         else if (cat == QQmlMetaType::List)
133             flags |= QQmlPropertyData::IsQList;
134     }
135
136     return flags;
137 }
138
139 static int metaObjectSignalCount(const QMetaObject *metaObject)
140 {
141     int signalCount = 0;
142     for (const QMetaObject *obj = metaObject; obj; obj = obj->superClass())
143         signalCount += QMetaObjectPrivate::get(obj)->signalCount;
144     return signalCount;
145 }
146
147 QQmlPropertyData::Flags
148 QQmlPropertyData::flagsForProperty(const QMetaProperty &p, QQmlEngine *engine)
149 {
150     return fastFlagsForProperty(p) | flagsForPropertyType(p.userType(), engine);
151 }
152
153 void QQmlPropertyData::lazyLoad(const QMetaProperty &p, QQmlEngine *engine)
154 {
155     Q_UNUSED(engine);
156
157     coreIndex = p.propertyIndex();
158     notifyIndex = QMetaObjectPrivate::signalIndex(p.notifySignal());
159     Q_ASSERT(p.revision() <= Q_INT16_MAX);
160     revision = p.revision();
161
162     flags = fastFlagsForProperty(p);
163
164     int type = p.type();
165     if (type == QMetaType::QObjectStar) {
166         propType = type;
167         flags |= QQmlPropertyData::IsQObjectDerived;
168     } else if (type == QMetaType::QVariant) {
169         propType = type;
170         flags |= QQmlPropertyData::IsQVariant;
171     } else if (type == QVariant::UserType || type == -1) {
172         propTypeName = p.typeName();
173         flags |= QQmlPropertyData::NotFullyResolved;
174     } else {
175         propType = type;
176     }
177 }
178
179 void QQmlPropertyData::load(const QMetaProperty &p, QQmlEngine *engine)
180 {
181     propType = p.userType();
182     coreIndex = p.propertyIndex();
183     notifyIndex = QMetaObjectPrivate::signalIndex(p.notifySignal());
184     flags = fastFlagsForProperty(p) | flagsForPropertyType(propType, engine);
185     Q_ASSERT(p.revision() <= Q_INT16_MAX);
186     revision = p.revision();
187 }
188
189 void QQmlPropertyData::load(const QMetaMethod &m)
190 {
191     coreIndex = m.methodIndex();
192     arguments = 0;
193     flags |= IsFunction;
194     if (m.methodType() == QMetaMethod::Signal)
195         flags |= IsSignal;
196     propType = m.returnType();
197
198     if (m.parameterCount()) {
199         flags |= HasArguments;
200         if ((m.parameterCount() == 1) && (m.parameterTypes().first() == "QQmlV8Function*")) {
201             flags |= IsV8Function;
202         }
203     }
204
205     if (m.attributes() & QMetaMethod::Cloned)
206         flags |= IsCloned;
207
208     Q_ASSERT(m.revision() <= Q_INT16_MAX);
209     revision = m.revision();
210 }
211
212 void QQmlPropertyData::lazyLoad(const QMetaMethod &m)
213 {
214     coreIndex = m.methodIndex();
215     arguments = 0;
216     flags |= IsFunction;
217     if (m.methodType() == QMetaMethod::Signal)
218         flags |= IsSignal;
219     propType = QMetaType::Void;
220
221     const char *returnType = m.typeName();
222     if (!returnType)
223         returnType = "\0";
224     if ((*returnType != 'v') || (qstrcmp(returnType+1, "oid") != 0)) {
225         propTypeName = returnType;
226         flags |= NotFullyResolved;
227     }
228
229     if (m.parameterCount()) {
230         flags |= HasArguments;
231         if ((m.parameterCount() == 1) && (m.parameterTypes().first() == "QQmlV8Function*")) {
232             flags |= IsV8Function;
233         }
234     }
235
236     if (m.attributes() & QMetaMethod::Cloned)
237         flags |= IsCloned;
238
239     Q_ASSERT(m.revision() <= Q_INT16_MAX);
240     revision = m.revision();
241 }
242
243 /*!
244 Creates a new empty QQmlPropertyCache.
245 */
246 QQmlPropertyCache::QQmlPropertyCache(QQmlEngine *e)
247 : engine(e), _parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0),
248   signalHandlerIndexCacheStart(0), _hasPropertyOverrides(false), _ownMetaObject(false),
249   _metaObject(0), argumentsCache(0)
250 {
251     Q_ASSERT(engine);
252 }
253
254 /*!
255 Creates a new QQmlPropertyCache of \a metaObject.
256 */
257 QQmlPropertyCache::QQmlPropertyCache(QQmlEngine *e, const QMetaObject *metaObject)
258 : engine(e), _parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0),
259   signalHandlerIndexCacheStart(0), _hasPropertyOverrides(false), _ownMetaObject(false),
260   _metaObject(0), argumentsCache(0)
261 {
262     Q_ASSERT(engine);
263     Q_ASSERT(metaObject);
264
265     update(engine, metaObject);
266 }
267
268 QQmlPropertyCache::~QQmlPropertyCache()
269 {
270     clear();
271
272     QQmlPropertyCacheMethodArguments *args = argumentsCache;
273     while (args) {
274         QQmlPropertyCacheMethodArguments *next = args->next;
275         if (args->signalParameterStringForJS) delete args->signalParameterStringForJS;
276         if (args->names) delete args->names;
277         free(args);
278         args = next;
279     }
280
281     // We must clear this prior to releasing the parent incase it is a
282     // linked hash
283     stringCache.clear();
284     if (_parent) _parent->release();
285
286     if (_ownMetaObject) free((void *)_metaObject);
287     _metaObject = 0;
288     _parent = 0;
289     engine = 0;
290 }
291
292 void QQmlPropertyCache::destroy()
293 {
294     Q_ASSERT(engine || constructor.IsEmpty());
295     if (constructor.IsEmpty())
296         delete this;
297     else
298         QQmlEnginePrivate::deleteInEngineThread(engine, this);
299 }
300
301 // This is inherited from QQmlCleanup, so it should only clear the things
302 // that are tied to the specific QQmlEngine.
303 void QQmlPropertyCache::clear()
304 {
305     qPersistentDispose(constructor);
306     engine = 0;
307 }
308
309 QQmlPropertyCache *QQmlPropertyCache::copy(int reserve)
310 {
311     QQmlPropertyCache *cache = new QQmlPropertyCache(engine);
312     cache->_parent = this;
313     cache->_parent->addref();
314     cache->propertyIndexCacheStart = propertyIndexCache.count() + propertyIndexCacheStart;
315     cache->methodIndexCacheStart = methodIndexCache.count() + methodIndexCacheStart;
316     cache->signalHandlerIndexCacheStart = signalHandlerIndexCache.count() + signalHandlerIndexCacheStart;
317     cache->stringCache.linkAndReserve(stringCache, reserve);
318     cache->allowedRevisionCache = allowedRevisionCache;
319     cache->_metaObject = _metaObject;
320     cache->_defaultPropertyName = _defaultPropertyName;
321
322     // We specifically do *NOT* copy the constructor
323
324     return cache;
325 }
326
327 QQmlPropertyCache *QQmlPropertyCache::copy()
328 {
329     return copy(0);
330 }
331
332 QQmlPropertyCache *QQmlPropertyCache::copyAndReserve(QQmlEngine *, int propertyCount, int methodCount,
333                                                      int signalCount)
334 {
335     QQmlPropertyCache *rv = copy(propertyCount + methodCount + signalCount);
336     rv->propertyIndexCache.reserve(propertyCount);
337     rv->methodIndexCache.reserve(methodCount);
338     rv->signalHandlerIndexCache.reserve(signalCount);
339     rv->_metaObject = 0;
340
341     return rv;
342 }
343
344 /*! \internal
345
346     \a notifyIndex MUST be in the signal index range (see QObjectPrivate::signalIndex()).
347     This is different from QMetaMethod::methodIndex().
348 */
349 void QQmlPropertyCache::appendProperty(const QString &name,
350                                        quint32 flags, int coreIndex, int propType, int notifyIndex)
351 {
352     QQmlPropertyData data;
353     data.propType = propType;
354     data.coreIndex = coreIndex;
355     data.notifyIndex = notifyIndex;
356     data.flags = flags;
357
358     QQmlPropertyData *old = findNamedProperty(name);
359     if (old)
360         data.markAsOverrideOf(old);
361
362     int index = propertyIndexCache.count();
363     propertyIndexCache.append(data);
364
365     setNamedProperty(name, index + propertyOffset(), propertyIndexCache.data() + index, (old != 0));
366 }
367
368 void QQmlPropertyCache::appendProperty(const QHashedCStringRef &name,
369                                        quint32 flags, int coreIndex, int propType, int notifyIndex)
370 {
371     QQmlPropertyData data;
372     data.propType = propType;
373     data.coreIndex = coreIndex;
374     data.notifyIndex = notifyIndex;
375     data.flags = flags;
376
377     QQmlPropertyData *old = findNamedProperty(name);
378     if (old)
379         data.markAsOverrideOf(old);
380
381     int index = propertyIndexCache.count();
382     propertyIndexCache.append(data);
383
384     setNamedProperty(name, index + propertyOffset(), propertyIndexCache.data() + index, (old != 0));
385 }
386
387 void QQmlPropertyCache::appendSignal(const QString &name, quint32 flags, int coreIndex,
388                                      const int *types, const QList<QByteArray> &names)
389 {
390     QQmlPropertyData data;
391     data.propType = QVariant::Invalid;
392     data.coreIndex = coreIndex;
393     data.flags = flags;
394     data.arguments = 0;
395
396     QQmlPropertyData handler = data;
397     handler.flags |= QQmlPropertyData::IsSignalHandler;
398
399     if (types) {
400         int argumentCount = *types;
401         typedef QQmlPropertyCacheMethodArguments A;
402         A *args = static_cast<A *>(malloc(sizeof(A) + (argumentCount + 1) * sizeof(int)));
403         ::memcpy(args->arguments, types, (argumentCount + 1) * sizeof(int));
404         args->argumentsValid = true;
405         args->signalParameterStringForJS = 0;
406         args->signalParameterCountForJS = 0;
407         args->parameterError = false;
408         args->names = new QList<QByteArray>(names);
409         args->next = argumentsCache;
410         argumentsCache = args;
411         data.arguments = args;
412     }
413
414     QQmlPropertyData *old = findNamedProperty(name);
415     if (old)
416         data.markAsOverrideOf(old);
417
418     int methodIndex = methodIndexCache.count();
419     methodIndexCache.append(data);
420
421     int signalHandlerIndex = signalHandlerIndexCache.count();
422     signalHandlerIndexCache.append(handler);
423
424     QString handlerName = QLatin1String("on") + name;
425     handlerName[2] = handlerName[2].toUpper();
426
427     setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0));
428     setNamedProperty(handlerName, signalHandlerIndex + signalOffset(), signalHandlerIndexCache.data() + signalHandlerIndex, (old != 0));
429 }
430
431 void QQmlPropertyCache::appendSignal(const QHashedCStringRef &name, quint32 flags, int coreIndex,
432                                      const int *types, const QList<QByteArray> &names)
433 {
434     QQmlPropertyData data;
435     data.propType = QVariant::Invalid;
436     data.coreIndex = coreIndex;
437     data.flags = flags;
438     data.arguments = 0;
439
440     QQmlPropertyData handler = data;
441     handler.flags |= QQmlPropertyData::IsSignalHandler;
442
443     if (types) {
444         int argumentCount = *types;
445         typedef QQmlPropertyCacheMethodArguments A;
446         A *args = static_cast<A *>(malloc(sizeof(A) + (argumentCount + 1) * sizeof(int)));
447         ::memcpy(args->arguments, types, (argumentCount + 1) * sizeof(int));
448         args->argumentsValid = true;
449         args->signalParameterStringForJS = 0;
450         args->signalParameterCountForJS = 0;
451         args->parameterError = false;
452         args->names = new QList<QByteArray>(names);
453         args->next = argumentsCache;
454         argumentsCache = args;
455         data.arguments = args;
456     }
457
458     QQmlPropertyData *old = findNamedProperty(name);
459     if (old)
460         data.markAsOverrideOf(old);
461
462     int methodIndex = methodIndexCache.count();
463     methodIndexCache.append(data);
464
465     int signalHandlerIndex = signalHandlerIndexCache.count();
466     signalHandlerIndexCache.append(handler);
467
468     QString handlerName = QLatin1String("on") + name.toUtf16();
469     handlerName[2] = handlerName[2].toUpper();
470
471     setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0));
472     setNamedProperty(handlerName, signalHandlerIndex + signalOffset(), signalHandlerIndexCache.data() + signalHandlerIndex, (old != 0));
473 }
474
475 void QQmlPropertyCache::appendMethod(const QString &name, quint32 flags, int coreIndex,
476                                      const QList<QByteArray> &names)
477 {
478     int argumentCount = names.count();
479
480     QQmlPropertyData data;
481     data.propType = QMetaType::QVariant;
482     data.coreIndex = coreIndex;
483
484     typedef QQmlPropertyCacheMethodArguments A;
485     A *args = static_cast<A *>(malloc(sizeof(A) + (argumentCount + 1) * sizeof(int)));
486     args->arguments[0] = argumentCount;
487     for (int ii = 0; ii < argumentCount; ++ii)
488         args->arguments[ii + 1] = QMetaType::QVariant;
489     args->argumentsValid = true;
490     args->signalParameterStringForJS = 0;
491     args->signalParameterCountForJS = 0;
492     args->parameterError = false;
493     args->names = 0;
494     if (argumentCount)
495         args->names = new QList<QByteArray>(names);
496     args->next = argumentsCache;
497     argumentsCache = args;
498     data.arguments = args;
499
500     data.flags = flags;
501
502     QQmlPropertyData *old = findNamedProperty(name);
503     if (old)
504         data.markAsOverrideOf(old);
505
506     int methodIndex = methodIndexCache.count();
507     methodIndexCache.append(data);
508
509     setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0));
510 }
511
512 void QQmlPropertyCache::appendMethod(const QHashedCStringRef &name, quint32 flags, int coreIndex,
513                                      const QList<QByteArray> &names)
514 {
515     int argumentCount = names.count();
516
517     QQmlPropertyData data;
518     data.propType = QMetaType::QVariant;
519     data.coreIndex = coreIndex;
520
521     typedef QQmlPropertyCacheMethodArguments A;
522     A *args = static_cast<A *>(malloc(sizeof(A) + (argumentCount + 1) * sizeof(int)));
523     args->arguments[0] = argumentCount;
524     for (int ii = 0; ii < argumentCount; ++ii)
525         args->arguments[ii + 1] = QMetaType::QVariant;
526     args->argumentsValid = true;
527     args->signalParameterStringForJS = 0;
528     args->signalParameterCountForJS = 0;
529     args->parameterError = false;
530     args->names = 0;
531     if (argumentCount)
532         args->names = new QList<QByteArray>(names);
533     args->next = argumentsCache;
534     argumentsCache = args;
535     data.arguments = args;
536
537     data.flags = flags;
538
539     QQmlPropertyData *old = findNamedProperty(name);
540     if (old)
541         data.markAsOverrideOf(old);
542
543     int methodIndex = methodIndexCache.count();
544     methodIndexCache.append(data);
545
546     setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0));
547 }
548
549 // Returns this property cache's metaObject.  May be null if it hasn't been created yet.
550 const QMetaObject *QQmlPropertyCache::metaObject() const
551 {
552     return _metaObject;
553 }
554
555 // Returns this property cache's metaObject, creating it if necessary.
556 const QMetaObject *QQmlPropertyCache::createMetaObject()
557 {
558     if (!_metaObject) {
559         _ownMetaObject = true;
560
561         QMetaObjectBuilder builder;
562         toMetaObjectBuilder(builder);
563         builder.setSuperClass(_parent->createMetaObject());
564         _metaObject = builder.toMetaObject();
565     }
566
567     return _metaObject;
568 }
569
570 // Returns the name of the default property for this cache
571 QString QQmlPropertyCache::defaultPropertyName() const
572 {
573     return _defaultPropertyName;
574 }
575
576 QQmlPropertyData *QQmlPropertyCache::defaultProperty() const
577 {
578     return property(defaultPropertyName(), 0, 0);
579 }
580
581 QQmlPropertyCache *QQmlPropertyCache::parent() const
582 {
583     return _parent;
584 }
585
586 // Returns the first C++ type's QMetaObject - that is, the first QMetaObject not created by
587 // QML
588 const QMetaObject *QQmlPropertyCache::firstCppMetaObject() const
589 {
590     while (_parent && (_metaObject == 0 || _ownMetaObject))
591         return _parent->firstCppMetaObject();
592     return _metaObject;
593 }
594
595 QQmlPropertyCache *
596 QQmlPropertyCache::copyAndAppend(QQmlEngine *engine, const QMetaObject *metaObject,
597                                          QQmlPropertyData::Flag propertyFlags,
598                                          QQmlPropertyData::Flag methodFlags,
599                                          QQmlPropertyData::Flag signalFlags)
600 {
601     return copyAndAppend(engine, metaObject, -1, propertyFlags, methodFlags, signalFlags);
602 }
603
604 QQmlPropertyCache *
605 QQmlPropertyCache::copyAndAppend(QQmlEngine *engine, const QMetaObject *metaObject,
606                                          int revision,
607                                          QQmlPropertyData::Flag propertyFlags,
608                                          QQmlPropertyData::Flag methodFlags,
609                                          QQmlPropertyData::Flag signalFlags)
610 {
611     Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4);
612
613     // Reserve enough space in the name hash for all the methods (including signals), all the
614     // signal handlers and all the properties.  This assumes no name clashes, but this is the
615     // common case.
616     QQmlPropertyCache *rv = copy(QMetaObjectPrivate::get(metaObject)->methodCount +
617                                          QMetaObjectPrivate::get(metaObject)->signalCount +
618                                          QMetaObjectPrivate::get(metaObject)->propertyCount);
619
620     rv->append(engine, metaObject, revision, propertyFlags, methodFlags, signalFlags);
621
622     return rv;
623 }
624
625 void QQmlPropertyCache::append(QQmlEngine *engine, const QMetaObject *metaObject,
626                                        int revision,
627                                        QQmlPropertyData::Flag propertyFlags,
628                                        QQmlPropertyData::Flag methodFlags,
629                                        QQmlPropertyData::Flag signalFlags)
630 {
631     Q_UNUSED(revision);
632     Q_ASSERT(constructor.IsEmpty()); // We should not be appending to an in-use property cache
633
634     _metaObject = metaObject;
635
636     bool dynamicMetaObject = isDynamicMetaObject(metaObject);
637
638     allowedRevisionCache.append(0);
639
640     int methodCount = metaObject->methodCount();
641     Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4);
642     int signalCount = metaObjectSignalCount(metaObject);
643     int classInfoCount = QMetaObjectPrivate::get(metaObject)->classInfoCount;
644
645     QQmlAccessorProperties::Properties accessorProperties;
646
647     if (classInfoCount) {
648         int classInfoOffset = metaObject->classInfoOffset();
649         bool hasFastProperty = false;
650         for (int ii = 0; ii < classInfoCount; ++ii) {
651             int idx = ii + classInfoOffset;
652
653             if (0 == qstrcmp(metaObject->classInfo(idx).name(), "qt_HasQmlAccessors")) {
654                 hasFastProperty = true;
655             } else if (0 == qstrcmp(metaObject->classInfo(idx).name(), "DefaultProperty")) {
656                 _defaultPropertyName = QString::fromUtf8(metaObject->classInfo(idx).value());
657             }
658         }
659
660         if (hasFastProperty) {
661             accessorProperties = QQmlAccessorProperties::properties(metaObject);
662             if (accessorProperties.count == 0)
663                 qFatal("QQmlPropertyCache: %s has FastProperty class info, but has not "
664                        "installed property accessors", metaObject->className());
665         } else {
666 #ifndef QT_NO_DEBUG
667             accessorProperties = QQmlAccessorProperties::properties(metaObject);
668             if (accessorProperties.count != 0)
669                 qFatal("QQmlPropertyCache: %s has fast property accessors, but is missing "
670                        "FastProperty class info", metaObject->className());
671 #endif
672         }
673     }
674
675     //Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
676     static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
677     static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()");
678     static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()");
679
680     int methodOffset = metaObject->methodOffset();
681     int signalOffset = signalCount - QMetaObjectPrivate::get(metaObject)->signalCount;
682
683     // update() should have reserved enough space in the vector that this doesn't cause a realloc
684     // and invalidate the stringCache.
685     methodIndexCache.resize(methodCount - methodIndexCacheStart);
686     signalHandlerIndexCache.resize(signalCount - signalHandlerIndexCacheStart);
687     int signalHandlerIndex = signalOffset;
688     for (int ii = methodOffset; ii < methodCount; ++ii) {
689         if (ii == destroyedIdx1 || ii == destroyedIdx2 || ii == deleteLaterIdx)
690             continue;
691         QMetaMethod m = metaObject->method(ii);
692         if (m.access() == QMetaMethod::Private)
693             continue;
694
695         // Extract method name
696         // It's safe to keep the raw name pointer
697         Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 7);
698         const char *rawName = m.name().constData();
699         const char *cptr = rawName;
700         char utf8 = 0;
701         while (*cptr) {
702             utf8 |= *cptr & 0x80;
703             ++cptr;
704         }
705
706         QQmlPropertyData *data = &methodIndexCache[ii - methodIndexCacheStart];
707         QQmlPropertyData *sigdata = 0;
708
709         data->lazyLoad(m);
710
711         if (data->isSignal())
712             data->flags |= signalFlags;
713         else
714             data->flags |= methodFlags;
715
716         if (!dynamicMetaObject)
717             data->flags |= QQmlPropertyData::IsDirect;
718
719         Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX);
720         data->metaObjectOffset = allowedRevisionCache.count() - 1;
721
722         if (data->isSignal()) {
723             sigdata = &signalHandlerIndexCache[signalHandlerIndex - signalHandlerIndexCacheStart];
724             *sigdata = *data;
725             sigdata->flags |= QQmlPropertyData::IsSignalHandler;
726         }
727
728         QQmlPropertyData *old = 0;
729
730         if (utf8) {
731             QHashedString methodName(QString::fromUtf8(rawName, cptr - rawName));
732             if (StringCache::mapped_type *it = stringCache.value(methodName))
733                 old = it->second;
734             setNamedProperty(methodName, ii, data, (old != 0));
735
736             if (data->isSignal()) {
737                 QHashedString on(QStringLiteral("on") % methodName.at(0).toUpper() % methodName.midRef(1));
738                 setNamedProperty(on, ii, sigdata, (old != 0));
739                 ++signalHandlerIndex;
740             }
741         } else {
742             QHashedCStringRef methodName(rawName, cptr - rawName);
743             if (StringCache::mapped_type *it = stringCache.value(methodName))
744                 old = it->second;
745             setNamedProperty(methodName, ii, data, (old != 0));
746
747             if (data->isSignal()) {
748                 int length = methodName.length();
749
750                 QVarLengthArray<char, 128> str(length+3);
751                 str[0] = 'o';
752                 str[1] = 'n';
753                 str[2] = toupper(rawName[0]);
754                 if (length > 1)
755                     memcpy(&str[3], &rawName[1], length - 1);
756                 str[length + 2] = '\0';
757
758                 QHashedString on(QString::fromLatin1(str.data()));
759                 setNamedProperty(on, ii, data, (old != 0));
760                 ++signalHandlerIndex;
761             }
762         }
763
764         if (old) {
765             // We only overload methods in the same class, exactly like C++
766             if (old->isFunction() && old->coreIndex >= methodOffset)
767                 data->flags |= QQmlPropertyData::IsOverload;
768
769             data->markAsOverrideOf(old);
770         }
771     }
772
773     int propCount = metaObject->propertyCount();
774     int propOffset = metaObject->propertyOffset();
775
776     // update() should have reserved enough space in the vector that this doesn't cause a realloc
777     // and invalidate the stringCache.
778     propertyIndexCache.resize(propCount - propertyIndexCacheStart);
779     for (int ii = propOffset; ii < propCount; ++ii) {
780         QMetaProperty p = metaObject->property(ii);
781         if (!p.isScriptable())
782             continue;
783
784         const char *str = p.name();
785         char utf8 = 0;
786         const char *cptr = str;
787         while (*cptr != 0) {
788             utf8 |= *cptr & 0x80;
789             ++cptr;
790         }
791
792         QQmlPropertyData *data = &propertyIndexCache[ii - propertyIndexCacheStart];
793
794         data->lazyLoad(p, engine);
795         data->flags |= propertyFlags;
796
797         if (!dynamicMetaObject)
798             data->flags |= QQmlPropertyData::IsDirect;
799
800         Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX);
801         data->metaObjectOffset = allowedRevisionCache.count() - 1;
802
803         QQmlPropertyData *old = 0;
804
805         if (utf8) {
806             QHashedString propName(QString::fromUtf8(str, cptr - str));
807             if (StringCache::mapped_type *it = stringCache.value(propName))
808                 old = it->second;
809             setNamedProperty(propName, ii, data, (old != 0));
810         } else {
811             QHashedCStringRef propName(str, cptr - str);
812             if (StringCache::mapped_type *it = stringCache.value(propName))
813                 old = it->second;
814             setNamedProperty(propName, ii, data, (old != 0));
815         }
816
817         QQmlAccessorProperties::Property *accessorProperty = accessorProperties.property(str);
818
819         // Fast properties may not be overrides or revisioned
820         Q_ASSERT(accessorProperty == 0 || (old == 0 && data->revision == 0));
821
822         if (accessorProperty) {
823             data->flags |= QQmlPropertyData::HasAccessors;
824             data->accessors = accessorProperty->accessors;
825             data->accessorData = accessorProperty->data;
826         } else if (old) {
827             data->markAsOverrideOf(old);
828         }
829     }
830 }
831
832 QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const
833 {
834     if (p && p->notFullyResolved())
835         resolve(p);
836
837     return p;
838 }
839
840 void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
841 {
842     Q_ASSERT(data->notFullyResolved());
843
844     data->propType = QMetaType::type(data->propTypeName);
845
846     if (!data->isFunction())
847         data->flags |= flagsForPropertyType(data->propType, engine);
848
849     data->flags &= ~QQmlPropertyData::NotFullyResolved;
850 }
851
852 void QQmlPropertyCache::updateRecur(QQmlEngine *engine, const QMetaObject *metaObject)
853 {
854     if (!metaObject)
855         return;
856
857     updateRecur(engine, metaObject->superClass());
858
859     append(engine, metaObject, -1);
860 }
861
862 void QQmlPropertyCache::update(QQmlEngine *engine, const QMetaObject *metaObject)
863 {
864     Q_ASSERT(engine);
865     Q_ASSERT(metaObject);
866     Q_ASSERT(stringCache.isEmpty());
867
868     // Preallocate enough space in the index caches for all the properties/methods/signals that
869     // are not cached in a parent cache so that the caches never need to be reallocated as this
870     // would invalidate pointers stored in the stringCache.
871     int pc = metaObject->propertyCount();
872     int mc = metaObject->methodCount();
873     int sc = metaObjectSignalCount(metaObject);
874     propertyIndexCache.reserve(pc - propertyIndexCacheStart);
875     methodIndexCache.reserve(mc - methodIndexCacheStart);
876     signalHandlerIndexCache.reserve(sc - signalHandlerIndexCacheStart);
877
878     // Reserve enough space in the stringCache for all properties/methods/signals including those
879     // cached in a parent cache.
880     stringCache.reserve(pc + mc + sc);
881
882     updateRecur(engine,metaObject);
883 }
884
885 /*! \internal
886     \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
887     This is different from QMetaMethod::methodIndex().
888 */
889 QQmlPropertyData *
890 QQmlPropertyCache::signal(int index, QQmlPropertyCache **c) const
891 {
892     if (index < 0 || index >= (signalHandlerIndexCacheStart + signalHandlerIndexCache.count()))
893         return 0;
894
895     if (index < signalHandlerIndexCacheStart)
896         return _parent->signal(index, c);
897
898     QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - signalHandlerIndexCacheStart));
899     if (rv->notFullyResolved()) resolve(rv);
900     Q_ASSERT(rv->isSignal() || rv->coreIndex == -1);
901     if (c) *c = const_cast<QQmlPropertyCache *>(this);
902     return rv;
903 }
904
905 int QQmlPropertyCache::methodIndexToSignalIndex(int index) const
906 {
907     if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count()))
908         return index;
909
910     if (index < methodIndexCacheStart)
911         return _parent->methodIndexToSignalIndex(index);
912
913     return index - methodIndexCacheStart + signalHandlerIndexCacheStart;
914 }
915
916 QQmlPropertyData *
917 QQmlPropertyCache::property(int index) const
918 {
919     if (index < 0 || index >= (propertyIndexCacheStart + propertyIndexCache.count()))
920         return 0;
921
922     if (index < propertyIndexCacheStart)
923         return _parent->property(index);
924
925     QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&propertyIndexCache.at(index - propertyIndexCacheStart));
926     return ensureResolved(rv);
927 }
928
929 QQmlPropertyData *
930 QQmlPropertyCache::method(int index) const
931 {
932     if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count()))
933         return 0;
934
935     if (index < methodIndexCacheStart)
936         return _parent->method(index);
937
938     QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - methodIndexCacheStart));
939     return ensureResolved(rv);
940 }
941
942 QQmlPropertyData *QQmlPropertyCache::findProperty(StringCache::ConstIterator it, QObject *object, QQmlContextData *context) const
943 {
944     QQmlData *data = (object ? QQmlData::get(object) : 0);
945     const QQmlVMEMetaObject *vmemo = (data && data->hasVMEMetaObject ? static_cast<const QQmlVMEMetaObject *>(object->metaObject()) : 0);
946     return findProperty(it, vmemo, context);
947 }
948
949 namespace {
950
951 inline bool contextHasNoExtensions(QQmlContextData *context)
952 {
953     // This context has no extension if its parent is the engine's rootContext,
954     // which has children but no imports
955     return (!context->parent || !context->parent->imports);
956 }
957
958 inline int maximumIndexForProperty(QQmlPropertyData *prop, const QQmlVMEMetaObject *vmemo)
959 {
960     return (prop->isFunction() ? vmemo->methodCount()
961                                : prop->isSignalHandler() ? vmemo->signalCount()
962                                                          : vmemo->propertyCount());
963 }
964
965 }
966
967 QQmlPropertyData *QQmlPropertyCache::findProperty(StringCache::ConstIterator it, const QQmlVMEMetaObject *vmemo, QQmlContextData *context) const
968 {
969     StringCache::ConstIterator end = stringCache.end();
970
971     if (it != end) {
972         if (vmemo && context && !contextHasNoExtensions(context)) {
973             // Find the highest property offset known to the supplied context
974             do {
975                 if (vmemo->ctxt == context)
976                     break;
977
978                 vmemo = vmemo->parentVMEMetaObject();
979             } while (vmemo);
980         }
981
982         do {
983             // Is this property available to this context?
984             const StringCache::mapped_type &property(it.value());
985             if (!vmemo || (property.first < maximumIndexForProperty(property.second, vmemo)))
986                 return ensureResolved(property.second);
987
988             it = stringCache.findNext(it);
989         } while (it != end);
990     }
991
992     return 0;
993 }
994
995 QString QQmlPropertyData::name(QObject *object)
996 {
997     if (!object)
998         return QString();
999
1000     return name(object->metaObject());
1001 }
1002
1003 QString QQmlPropertyData::name(const QMetaObject *metaObject)
1004 {
1005     if (!metaObject || coreIndex == -1)
1006         return QString();
1007
1008     if (flags & IsFunction) {
1009         QMetaMethod m = metaObject->method(coreIndex);
1010
1011         return QString::fromUtf8(m.name().constData());
1012     } else {
1013         QMetaProperty p = metaObject->property(coreIndex);
1014         return QString::fromUtf8(p.name());
1015     }
1016 }
1017
1018 void QQmlPropertyData::markAsOverrideOf(QQmlPropertyData *predecessor)
1019 {
1020     overrideIndexIsProperty = !predecessor->isFunction();
1021     overrideIndex = predecessor->coreIndex;
1022
1023     predecessor->flags |= QQmlPropertyData::IsOverridden;
1024 }
1025
1026 QStringList QQmlPropertyCache::propertyNames() const
1027 {
1028     QStringList keys;
1029     for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter)
1030         keys.append(iter.key());
1031     return keys;
1032 }
1033
1034 struct StaticQtMetaObject : public QObject
1035 {
1036     static const QMetaObject *get()
1037         { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; }
1038 };
1039
1040 static int EnumType(const QMetaObject *metaobj, const QByteArray &str, int type)
1041 {
1042     QByteArray scope;
1043     QByteArray name;
1044     int scopeIdx = str.lastIndexOf("::");
1045     if (scopeIdx != -1) {
1046         scope = str.left(scopeIdx);
1047         name = str.mid(scopeIdx + 2);
1048     } else {
1049         name = str;
1050     }
1051     const QMetaObject *meta;
1052     if (scope == "Qt")
1053         meta = StaticQtMetaObject::get();
1054     else
1055         meta = metaobj;
1056     for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
1057         QMetaEnum m = meta->enumerator(i);
1058         if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
1059             return QVariant::Int;
1060     }
1061     return type;
1062 }
1063
1064 /*! \internal
1065     \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
1066     This is different from QMetaMethod::methodIndex().
1067 */
1068 QString QQmlPropertyCache::signalParameterStringForJS(int index, int *count, QString *errorString)
1069 {
1070     QQmlPropertyCache *c = 0;
1071     QQmlPropertyData *signalData = signal(index, &c);
1072     if (!signalData)
1073         return QString();
1074
1075     typedef QQmlPropertyCacheMethodArguments A;
1076
1077     if (signalData->arguments) {
1078         A *arguments = static_cast<A *>(signalData->arguments);
1079         if (arguments->signalParameterStringForJS) {
1080             if (count)
1081                 *count = arguments->signalParameterCountForJS;
1082             if (arguments->parameterError) {
1083                 if (errorString)
1084                     *errorString = *arguments->signalParameterStringForJS;
1085                 return QString();
1086             }
1087             return *arguments->signalParameterStringForJS;
1088         }
1089     }
1090
1091     QList<QByteArray> parameterNameList = signalParameterNames(index);
1092
1093     if (!signalData->arguments) {
1094         int argc = parameterNameList.count();
1095         A *args = static_cast<A *>(malloc(sizeof(A) + (argc + 1) * sizeof(int)));
1096         args->arguments[0] = argc;
1097         args->argumentsValid = false;
1098         args->signalParameterStringForJS = 0;
1099         args->signalParameterCountForJS = 0;
1100         args->parameterError = false;
1101         args->names = new QList<QByteArray>(parameterNameList);
1102         signalData->arguments = args;
1103         args->next = c->argumentsCache;
1104         c->argumentsCache = args;
1105     }
1106
1107     QQmlRewrite::RewriteSignalHandler rewriter;
1108     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1109     const QString &parameters = rewriter.createParameterString(parameterNameList,
1110                                                                ep->v8engine()->illegalNames());
1111
1112     bool error = rewriter.hasParameterError();
1113     A *arguments = static_cast<A *>(signalData->arguments);
1114     arguments->signalParameterStringForJS = new QString(error ? rewriter.parameterError() : parameters);
1115     arguments->signalParameterCountForJS = rewriter.parameterCountForJS();
1116     if (count)
1117         *count = arguments->signalParameterCountForJS;
1118     if (error) {
1119         arguments->parameterError = true;
1120         if (errorString)
1121             *errorString = *arguments->signalParameterStringForJS;
1122         return QString();
1123     }
1124     return *arguments->signalParameterStringForJS;
1125 }
1126
1127 // Returns an array of the arguments for method \a index.  The first entry in the array
1128 // is the number of arguments.
1129 int *QQmlPropertyCache::methodParameterTypes(QObject *object, int index,
1130                                                      QVarLengthArray<int, 9> &dummy,
1131                                                      QByteArray *unknownTypeError)
1132 {
1133     Q_ASSERT(object && index >= 0);
1134
1135     QQmlData *ddata = QQmlData::get(object, false);
1136
1137     if (ddata && ddata->propertyCache) {
1138         typedef QQmlPropertyCacheMethodArguments A;
1139
1140         QQmlPropertyCache *c = ddata->propertyCache;
1141         Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count());
1142
1143         while (index < c->methodIndexCacheStart)
1144             c = c->_parent;
1145
1146         QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart));
1147
1148         if (rv->arguments && static_cast<A *>(rv->arguments)->argumentsValid)
1149             return static_cast<A *>(rv->arguments)->arguments;
1150
1151         const QMetaObject *metaObject = c->createMetaObject();
1152         Q_ASSERT(metaObject);
1153         QMetaMethod m = metaObject->method(index);
1154
1155         int argc = m.parameterCount();
1156         if (!rv->arguments) {
1157             A *args = static_cast<A *>(malloc(sizeof(A) + (argc + 1) * sizeof(int)));
1158             args->arguments[0] = argc;
1159             args->argumentsValid = false;
1160             args->signalParameterStringForJS = 0;
1161             args->signalParameterCountForJS = 0;
1162             args->parameterError = false;
1163             args->names = 0;
1164             rv->arguments = args;
1165             args->next = c->argumentsCache;
1166             c->argumentsCache = args;
1167         }
1168         A *args = static_cast<A *>(rv->arguments);
1169
1170         QList<QByteArray> argTypeNames; // Only loaded if needed
1171
1172         for (int ii = 0; ii < argc; ++ii) {
1173             int type = m.parameterType(ii);
1174             QMetaType::TypeFlags flags = QMetaType::typeFlags(type);
1175             if (flags & QMetaType::IsEnumeration)
1176                 type = QVariant::Int;
1177             else if (type == QMetaType::UnknownType ||
1178                      (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) &&
1179                       type != qMetaTypeId<QJSValue>())) {
1180                 //the UserType clause is to catch registered QFlags
1181                 if (argTypeNames.isEmpty())
1182                     argTypeNames = m.parameterTypes();
1183                 type = EnumType(object->metaObject(), argTypeNames.at(ii), type);
1184             }
1185             if (type == QMetaType::UnknownType) {
1186                 if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii);
1187                 return 0;
1188             }
1189             args->arguments[ii + 1] = type;
1190         }
1191         args->argumentsValid = true;
1192         return static_cast<A *>(rv->arguments)->arguments;
1193
1194     } else {
1195         QMetaMethod m = object->metaObject()->method(index);
1196         int argc = m.parameterCount();
1197         dummy.resize(argc + 1);
1198         dummy[0] = argc;
1199         QList<QByteArray> argTypeNames; // Only loaded if needed
1200
1201         for (int ii = 0; ii < argc; ++ii) {
1202             int type = m.parameterType(ii);
1203             QMetaType::TypeFlags flags = QMetaType::typeFlags(type);
1204             if (flags & QMetaType::IsEnumeration)
1205                 type = QVariant::Int;
1206             else if (type == QMetaType::UnknownType ||
1207                      (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) &&
1208                       type != qMetaTypeId<QJSValue>())) {
1209                 //the UserType clause is to catch registered QFlags)
1210                 if (argTypeNames.isEmpty())
1211                     argTypeNames = m.parameterTypes();
1212                 type = EnumType(object->metaObject(), argTypeNames.at(ii), type);
1213             }
1214             if (type == QMetaType::UnknownType) {
1215                 if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii);
1216                 return 0;
1217             }
1218             dummy[ii + 1] = type;
1219         }
1220
1221         return dummy.data();
1222     }
1223 }
1224
1225 // Returns the return type of the method.
1226 int QQmlPropertyCache::methodReturnType(QObject *object, const QQmlPropertyData &data,
1227                                         QByteArray *unknownTypeError)
1228 {
1229     Q_ASSERT(object && data.coreIndex >= 0);
1230
1231     int type = data.propType;
1232
1233     const char *propTypeName = 0;
1234
1235     if (type == QMetaType::UnknownType) {
1236         // Find the return type name from the method info
1237         QMetaMethod m;
1238
1239         QQmlData *ddata = QQmlData::get(object, false);
1240         if (ddata && ddata->propertyCache) {
1241             QQmlPropertyCache *c = ddata->propertyCache;
1242             Q_ASSERT(data.coreIndex < c->methodIndexCacheStart + c->methodIndexCache.count());
1243
1244             while (data.coreIndex < c->methodIndexCacheStart)
1245                 c = c->_parent;
1246
1247             const QMetaObject *metaObject = c->createMetaObject();
1248             Q_ASSERT(metaObject);
1249             m = metaObject->method(data.coreIndex);
1250         } else {
1251             m = object->metaObject()->method(data.coreIndex);
1252         }
1253
1254         type = m.returnType();
1255         propTypeName = m.typeName();
1256     }
1257
1258     QMetaType::TypeFlags flags = QMetaType::typeFlags(type);
1259     if (flags & QMetaType::IsEnumeration) {
1260         type = QVariant::Int;
1261     } else if (type == QMetaType::UnknownType ||
1262                (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) &&
1263                type != qMetaTypeId<QJSValue>())) {
1264         //the UserType clause is to catch registered QFlags
1265         type = EnumType(object->metaObject(), propTypeName, type);
1266     }
1267
1268     if (type == QMetaType::UnknownType) {
1269         if (unknownTypeError) *unknownTypeError = propTypeName;
1270     }
1271
1272     return type;
1273 }
1274
1275 int QQmlPropertyCache::originalClone(int index)
1276 {
1277     while (signal(index)->isCloned())
1278         --index;
1279     return index;
1280 }
1281
1282 int QQmlPropertyCache::originalClone(QObject *object, int index)
1283 {
1284     QQmlData *data = QQmlData::get(object, false);
1285     if (data && data->propertyCache) {
1286         QQmlPropertyCache *cache = data->propertyCache;
1287         while (cache->signal(index)->isCloned())
1288             --index;
1289     } else {
1290         while (QMetaObjectPrivate::signal(object->metaObject(), index).attributes() & QMetaMethod::Cloned)
1291             --index;
1292     }
1293     return index;
1294 }
1295
1296 QQmlPropertyData qQmlPropertyCacheCreate(const QMetaObject *metaObject, const QString &property)
1297 {
1298     Q_ASSERT(metaObject);
1299
1300     QQmlPropertyData rv;
1301     {
1302         const QMetaObject *cmo = metaObject;
1303         const QByteArray propertyName = property.toUtf8();
1304         while (cmo) {
1305             int idx = cmo->indexOfProperty(propertyName);
1306             if (idx != -1) {
1307                 QMetaProperty p = cmo->property(idx);
1308                 if (p.isScriptable()) {
1309                     rv.load(p);
1310                     return rv;
1311                 } else {
1312                     while (cmo && cmo->propertyOffset() >= idx)
1313                         cmo = cmo->superClass();
1314                 }
1315             } else {
1316                 cmo = 0;
1317             }
1318         }
1319     }
1320
1321     //Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
1322     static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
1323     static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()");
1324     static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()");
1325
1326     int methodCount = metaObject->methodCount();
1327     for (int ii = methodCount - 1; ii >= 0; --ii) {
1328         if (ii == destroyedIdx1 || ii == destroyedIdx2 || ii == deleteLaterIdx)
1329             continue;
1330         QMetaMethod m = metaObject->method(ii);
1331         if (m.access() == QMetaMethod::Private)
1332             continue;
1333         QString methodName = QString::fromUtf8(m.name().constData());
1334
1335         if (methodName == property) {
1336             rv.load(m);
1337             return rv;
1338         }
1339     }
1340
1341     return rv;
1342 }
1343
1344 inline const QString &qQmlPropertyCacheToString(const QString &string)
1345 {
1346     return string;
1347 }
1348
1349 inline QString qQmlPropertyCacheToString(const QHashedV8String &string)
1350 {
1351     return QV8Engine::toStringStatic(string.string());
1352 }
1353
1354 template<typename T>
1355 QQmlPropertyData *
1356 qQmlPropertyCacheProperty(QQmlEngine *engine, QObject *obj, const T &name,
1357                           QQmlContextData *context, QQmlPropertyData &local)
1358 {
1359     QQmlPropertyCache *cache = 0;
1360
1361     QQmlData *ddata = QQmlData::get(obj, false);
1362
1363     if (ddata && ddata->propertyCache) {
1364         cache = ddata->propertyCache;
1365     } else if (engine) {
1366         QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1367         cache = ep->cache(obj);
1368         if (cache) {
1369             ddata = QQmlData::get(obj, true);
1370             cache->addref();
1371             ddata->propertyCache = cache;
1372         }
1373     }
1374
1375     QQmlPropertyData *rv = 0;
1376
1377     if (cache) {
1378         rv = cache->property(name, obj, context);
1379     } else {
1380         local = qQmlPropertyCacheCreate(obj->metaObject(), qQmlPropertyCacheToString(name));
1381         if (local.isValid())
1382             rv = &local;
1383     }
1384
1385     return rv;
1386 }
1387
1388 QQmlPropertyData *
1389 QQmlPropertyCache::property(QQmlEngine *engine, QObject *obj, const QHashedV8String &name,
1390                             QQmlContextData *context, QQmlPropertyData &local)
1391 {
1392     return qQmlPropertyCacheProperty<QHashedV8String>(engine, obj, name, context, local);
1393 }
1394
1395 QQmlPropertyData *
1396 QQmlPropertyCache::property(QQmlEngine *engine, QObject *obj,
1397                                     const QString &name, QQmlContextData *context, QQmlPropertyData &local)
1398 {
1399     return qQmlPropertyCacheProperty<QString>(engine, obj, name, context, local);
1400 }
1401
1402 static inline const QMetaObjectPrivate *priv(const uint* data)
1403 { return reinterpret_cast<const QMetaObjectPrivate*>(data); }
1404
1405 bool QQmlPropertyCache::isDynamicMetaObject(const QMetaObject *mo)
1406 {
1407     return priv(mo->d.data)->revision >= 3 && priv(mo->d.data)->flags & DynamicMetaObject;
1408 }
1409
1410 const char *QQmlPropertyCache::className() const
1411 {
1412     if (!_ownMetaObject && _metaObject)
1413         return _metaObject->className();
1414     else
1415         return _dynamicClassName.constData();
1416 }
1417
1418 void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder)
1419 {
1420     struct Sort { static bool lt(const QPair<QString, QQmlPropertyData *> &lhs,
1421                                  const QPair<QString, QQmlPropertyData *> &rhs) {
1422         return lhs.second->coreIndex < rhs.second->coreIndex;
1423     } };
1424
1425     struct Insert { static void in(QQmlPropertyCache *This,
1426                                    QList<QPair<QString, QQmlPropertyData *> > &properties,
1427                                    QList<QPair<QString, QQmlPropertyData *> > &methods,
1428                                    StringCache::ConstIterator iter, QQmlPropertyData *data) {
1429         if (data->isSignalHandler())
1430             return;
1431
1432         if (data->isFunction()) {
1433             if (data->coreIndex < This->methodIndexCacheStart)
1434                 return;
1435
1436             QPair<QString, QQmlPropertyData *> entry = qMakePair((QString)iter.key(), data);
1437             // Overrides can cause the entry to already exist
1438             if (!methods.contains(entry)) methods.append(entry);
1439
1440             data = This->overrideData(data);
1441             if (data && !data->isFunction()) Insert::in(This, properties, methods, iter, data);
1442         } else {
1443             if (data->coreIndex < This->propertyIndexCacheStart)
1444                 return;
1445
1446             QPair<QString, QQmlPropertyData *> entry = qMakePair((QString)iter.key(), data);
1447             // Overrides can cause the entry to already exist
1448             if (!properties.contains(entry)) properties.append(entry);
1449
1450             data = This->overrideData(data);
1451             if (data) Insert::in(This, properties, methods, iter, data);
1452         }
1453
1454     } };
1455
1456     builder.setClassName(_dynamicClassName);
1457
1458     QList<QPair<QString, QQmlPropertyData *> > properties;
1459     QList<QPair<QString, QQmlPropertyData *> > methods;
1460
1461     for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter)
1462         Insert::in(this, properties, methods, iter, iter.value().second);
1463
1464     Q_ASSERT(properties.count() == propertyIndexCache.count());
1465     Q_ASSERT(methods.count() == methodIndexCache.count());
1466
1467     qSort(properties.begin(), properties.end(), Sort::lt);
1468     qSort(methods.begin(), methods.end(), Sort::lt);
1469
1470     for (int ii = 0; ii < properties.count(); ++ii) {
1471         QQmlPropertyData *data = properties.at(ii).second;
1472
1473         int notifierId = -1;
1474         if (data->notifyIndex != -1)
1475             notifierId = data->notifyIndex - signalHandlerIndexCacheStart;
1476
1477         QMetaPropertyBuilder property = builder.addProperty(properties.at(ii).first.toUtf8(),
1478                                                             QMetaType::typeName(data->propType),
1479                                                             notifierId);
1480
1481         property.setReadable(true);
1482         property.setWritable(data->isWritable());
1483         property.setResettable(data->isResettable());
1484     }
1485
1486     for (int ii = 0; ii < methods.count(); ++ii) {
1487         QQmlPropertyData *data = methods.at(ii).second;
1488
1489         QByteArray returnType;
1490         if (data->propType != 0)
1491             returnType = QMetaType::typeName(data->propType);
1492
1493         QByteArray signature = methods.at(ii).first.toUtf8() + "(";
1494
1495         QQmlPropertyCacheMethodArguments *arguments = 0;
1496         if (data->hasArguments()) {
1497             arguments = (QQmlPropertyCacheMethodArguments *)data->arguments;
1498             Q_ASSERT(arguments->argumentsValid);
1499             for (int ii = 0; ii < arguments->arguments[0]; ++ii) {
1500                 if (ii != 0) signature.append(",");
1501                 signature.append(QMetaType::typeName(arguments->arguments[1 + ii]));
1502             }
1503         }
1504
1505         signature.append(")");
1506
1507         QMetaMethodBuilder method;
1508         if (data->isSignal()) {
1509             method = builder.addSignal(signature);
1510         } else {
1511             method = builder.addSlot(signature);
1512         }
1513         method.setAccess(QMetaMethod::Protected);
1514
1515         if (arguments && arguments->names)
1516             method.setParameterNames(*arguments->names);
1517
1518         if (!returnType.isEmpty())
1519             method.setReturnType(returnType);
1520     }
1521
1522     if (!_defaultPropertyName.isEmpty()) {
1523         QQmlPropertyData *dp = property(_defaultPropertyName, 0, 0);
1524         if (dp && dp->coreIndex >= propertyIndexCacheStart) {
1525             Q_ASSERT(!dp->isFunction());
1526             builder.addClassInfo("DefaultProperty", _defaultPropertyName.toUtf8());
1527         }
1528     }
1529 }
1530
1531 /*! \internal
1532     \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
1533     This is different from QMetaMethod::methodIndex().
1534 */
1535 QList<QByteArray> QQmlPropertyCache::signalParameterNames(int index) const
1536 {
1537     QQmlPropertyData *signalData = signal(index);
1538     if (signalData && signalData->hasArguments()) {
1539         QQmlPropertyCacheMethodArguments *args = (QQmlPropertyCacheMethodArguments *)signalData->arguments;
1540         if (args && args->names)
1541             return *args->names;
1542         const QMetaMethod &method = QMetaObjectPrivate::signal(firstCppMetaObject(), index);
1543         return method.parameterNames();
1544     }
1545     return QList<QByteArray>();
1546 }
1547
1548 // Returns true if \a from is assignable to a property of type \a to
1549 bool QQmlMetaObject::canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to)
1550 {
1551     Q_ASSERT(!from.isNull() && !to.isNull());
1552
1553     struct I { static bool equal(const QMetaObject *lhs, const QMetaObject *rhs) {
1554         return lhs == rhs || (lhs && rhs && lhs->d.stringdata == rhs->d.stringdata);
1555     } };
1556
1557     const QMetaObject *tom = to._m.isT1()?to._m.asT1()->metaObject():to._m.asT2();
1558     if (tom == &QObject::staticMetaObject) return true;
1559
1560     if (from._m.isT1() && to._m.isT1()) { // QQmlPropertyCache -> QQmlPropertyCache
1561         QQmlPropertyCache *fromp = from._m.asT1();
1562         QQmlPropertyCache *top = to._m.asT1();
1563
1564         while (fromp) {
1565             if (fromp == top) return true;
1566             fromp = fromp->parent();
1567         }
1568     } else if (from._m.isT1() && to._m.isT2()) { // QQmlPropertyCache -> QMetaObject
1569         QQmlPropertyCache *fromp = from._m.asT1();
1570
1571         while (fromp) {
1572             const QMetaObject *fromm = fromp->metaObject();
1573             if (fromm && I::equal(fromm, tom)) return true;
1574             fromp = fromp->parent();
1575         }
1576     } else if (from._m.isT2() && to._m.isT1()) { // QMetaObject -> QQmlPropertyCache
1577         const QMetaObject *fromm = from._m.asT2();
1578
1579         if (!tom) return false;
1580
1581         while (fromm) {
1582             if (I::equal(fromm, tom)) return true;
1583             fromm = fromm->superClass();
1584         }
1585     } else { // QMetaObject -> QMetaObject
1586         const QMetaObject *fromm = from._m.asT2();
1587
1588         while (fromm) {
1589             if (I::equal(fromm, tom)) return true;
1590             fromm = fromm->superClass();
1591         }
1592     }
1593
1594     return false;
1595 }
1596
1597 QQmlPropertyCache *QQmlMetaObject::propertyCache(QQmlEnginePrivate *e) const
1598 {
1599     if (_m.isNull()) return 0;
1600     if (_m.isT1()) return _m.asT1();
1601     else return e->cache(_m.asT2());
1602 }
1603
1604 QT_END_NAMESPACE