Improve documentation.
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmlpropertycache.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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         QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names);
402         ::memcpy(args->arguments, types, (argumentCount + 1) * sizeof(int));
403         args->argumentsValid = true;
404         data.arguments = args;
405     }
406
407     QQmlPropertyData *old = findNamedProperty(name);
408     if (old)
409         data.markAsOverrideOf(old);
410
411     int methodIndex = methodIndexCache.count();
412     methodIndexCache.append(data);
413
414     int signalHandlerIndex = signalHandlerIndexCache.count();
415     signalHandlerIndexCache.append(handler);
416
417     QString handlerName = QLatin1String("on") + name;
418     handlerName[2] = handlerName[2].toUpper();
419
420     setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0));
421     setNamedProperty(handlerName, signalHandlerIndex + signalOffset(), signalHandlerIndexCache.data() + signalHandlerIndex, (old != 0));
422 }
423
424 void QQmlPropertyCache::appendSignal(const QHashedCStringRef &name, quint32 flags, int coreIndex,
425                                      const int *types, const QList<QByteArray> &names)
426 {
427     QQmlPropertyData data;
428     data.propType = QVariant::Invalid;
429     data.coreIndex = coreIndex;
430     data.flags = flags;
431     data.arguments = 0;
432
433     QQmlPropertyData handler = data;
434     handler.flags |= QQmlPropertyData::IsSignalHandler;
435
436     if (types) {
437         int argumentCount = *types;
438         QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names);
439         ::memcpy(args->arguments, types, (argumentCount + 1) * sizeof(int));
440         args->argumentsValid = true;
441         data.arguments = args;
442     }
443
444     QQmlPropertyData *old = findNamedProperty(name);
445     if (old)
446         data.markAsOverrideOf(old);
447
448     int methodIndex = methodIndexCache.count();
449     methodIndexCache.append(data);
450
451     int signalHandlerIndex = signalHandlerIndexCache.count();
452     signalHandlerIndexCache.append(handler);
453
454     QString handlerName = QLatin1String("on") + name.toUtf16();
455     handlerName[2] = handlerName[2].toUpper();
456
457     setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0));
458     setNamedProperty(handlerName, signalHandlerIndex + signalOffset(), signalHandlerIndexCache.data() + signalHandlerIndex, (old != 0));
459 }
460
461 void QQmlPropertyCache::appendMethod(const QString &name, quint32 flags, int coreIndex,
462                                      const QList<QByteArray> &names)
463 {
464     int argumentCount = names.count();
465
466     QQmlPropertyData data;
467     data.propType = QMetaType::QVariant;
468     data.coreIndex = coreIndex;
469
470     QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names);
471     for (int ii = 0; ii < argumentCount; ++ii)
472         args->arguments[ii + 1] = QMetaType::QVariant;
473     args->argumentsValid = true;
474     data.arguments = args;
475
476     data.flags = flags;
477
478     QQmlPropertyData *old = findNamedProperty(name);
479     if (old)
480         data.markAsOverrideOf(old);
481
482     int methodIndex = methodIndexCache.count();
483     methodIndexCache.append(data);
484
485     setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0));
486 }
487
488 void QQmlPropertyCache::appendMethod(const QHashedCStringRef &name, quint32 flags, int coreIndex,
489                                      const QList<QByteArray> &names)
490 {
491     int argumentCount = names.count();
492
493     QQmlPropertyData data;
494     data.propType = QMetaType::QVariant;
495     data.coreIndex = coreIndex;
496
497     QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names);
498     for (int ii = 0; ii < argumentCount; ++ii)
499         args->arguments[ii + 1] = QMetaType::QVariant;
500     args->argumentsValid = true;
501     data.arguments = args;
502
503     data.flags = flags;
504
505     QQmlPropertyData *old = findNamedProperty(name);
506     if (old)
507         data.markAsOverrideOf(old);
508
509     int methodIndex = methodIndexCache.count();
510     methodIndexCache.append(data);
511
512     setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0));
513 }
514
515 // Returns this property cache's metaObject.  May be null if it hasn't been created yet.
516 const QMetaObject *QQmlPropertyCache::metaObject() const
517 {
518     return _metaObject;
519 }
520
521 // Returns this property cache's metaObject, creating it if necessary.
522 const QMetaObject *QQmlPropertyCache::createMetaObject()
523 {
524     if (!_metaObject) {
525         _ownMetaObject = true;
526
527         QMetaObjectBuilder builder;
528         toMetaObjectBuilder(builder);
529         builder.setSuperClass(_parent->createMetaObject());
530         _metaObject = builder.toMetaObject();
531     }
532
533     return _metaObject;
534 }
535
536 // Returns the name of the default property for this cache
537 QString QQmlPropertyCache::defaultPropertyName() const
538 {
539     return _defaultPropertyName;
540 }
541
542 QQmlPropertyData *QQmlPropertyCache::defaultProperty() const
543 {
544     return property(defaultPropertyName(), 0, 0);
545 }
546
547 QQmlPropertyCache *QQmlPropertyCache::parent() const
548 {
549     return _parent;
550 }
551
552 void QQmlPropertyCache::setParent(QQmlPropertyCache *newParent)
553 {
554     _parent = newParent;
555 }
556
557 // Returns the first C++ type's QMetaObject - that is, the first QMetaObject not created by
558 // QML
559 const QMetaObject *QQmlPropertyCache::firstCppMetaObject() const
560 {
561     while (_parent && (_metaObject == 0 || _ownMetaObject))
562         return _parent->firstCppMetaObject();
563     return _metaObject;
564 }
565
566 QQmlPropertyCache *
567 QQmlPropertyCache::copyAndAppend(QQmlEngine *engine, const QMetaObject *metaObject,
568                                          QQmlPropertyData::Flag propertyFlags,
569                                          QQmlPropertyData::Flag methodFlags,
570                                          QQmlPropertyData::Flag signalFlags)
571 {
572     return copyAndAppend(engine, metaObject, -1, propertyFlags, methodFlags, signalFlags);
573 }
574
575 QQmlPropertyCache *
576 QQmlPropertyCache::copyAndAppend(QQmlEngine *engine, const QMetaObject *metaObject,
577                                          int revision,
578                                          QQmlPropertyData::Flag propertyFlags,
579                                          QQmlPropertyData::Flag methodFlags,
580                                          QQmlPropertyData::Flag signalFlags)
581 {
582     Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4);
583
584     // Reserve enough space in the name hash for all the methods (including signals), all the
585     // signal handlers and all the properties.  This assumes no name clashes, but this is the
586     // common case.
587     QQmlPropertyCache *rv = copy(QMetaObjectPrivate::get(metaObject)->methodCount +
588                                          QMetaObjectPrivate::get(metaObject)->signalCount +
589                                          QMetaObjectPrivate::get(metaObject)->propertyCount);
590
591     rv->append(engine, metaObject, revision, propertyFlags, methodFlags, signalFlags);
592
593     return rv;
594 }
595
596 void QQmlPropertyCache::append(QQmlEngine *engine, const QMetaObject *metaObject,
597                                        int revision,
598                                        QQmlPropertyData::Flag propertyFlags,
599                                        QQmlPropertyData::Flag methodFlags,
600                                        QQmlPropertyData::Flag signalFlags)
601 {
602     Q_UNUSED(revision);
603     Q_ASSERT(constructor.IsEmpty()); // We should not be appending to an in-use property cache
604
605     _metaObject = metaObject;
606
607     bool dynamicMetaObject = isDynamicMetaObject(metaObject);
608
609     allowedRevisionCache.append(0);
610
611     int methodCount = metaObject->methodCount();
612     Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4);
613     int signalCount = metaObjectSignalCount(metaObject);
614     int classInfoCount = QMetaObjectPrivate::get(metaObject)->classInfoCount;
615
616     QQmlAccessorProperties::Properties accessorProperties;
617
618     if (classInfoCount) {
619         int classInfoOffset = metaObject->classInfoOffset();
620         bool hasFastProperty = false;
621         for (int ii = 0; ii < classInfoCount; ++ii) {
622             int idx = ii + classInfoOffset;
623
624             if (0 == qstrcmp(metaObject->classInfo(idx).name(), "qt_HasQmlAccessors")) {
625                 hasFastProperty = true;
626             } else if (0 == qstrcmp(metaObject->classInfo(idx).name(), "DefaultProperty")) {
627                 _defaultPropertyName = QString::fromUtf8(metaObject->classInfo(idx).value());
628             }
629         }
630
631         if (hasFastProperty) {
632             accessorProperties = QQmlAccessorProperties::properties(metaObject);
633             if (accessorProperties.count == 0)
634                 qFatal("QQmlPropertyCache: %s has FastProperty class info, but has not "
635                        "installed property accessors", metaObject->className());
636         } else {
637 #ifndef QT_NO_DEBUG
638             accessorProperties = QQmlAccessorProperties::properties(metaObject);
639             if (accessorProperties.count != 0)
640                 qFatal("QQmlPropertyCache: %s has fast property accessors, but is missing "
641                        "FastProperty class info", metaObject->className());
642 #endif
643         }
644     }
645
646     //Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
647     static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
648     static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()");
649     static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()");
650
651     int methodOffset = metaObject->methodOffset();
652     int signalOffset = signalCount - QMetaObjectPrivate::get(metaObject)->signalCount;
653
654     // update() should have reserved enough space in the vector that this doesn't cause a realloc
655     // and invalidate the stringCache.
656     methodIndexCache.resize(methodCount - methodIndexCacheStart);
657     signalHandlerIndexCache.resize(signalCount - signalHandlerIndexCacheStart);
658     int signalHandlerIndex = signalOffset;
659     for (int ii = methodOffset; ii < methodCount; ++ii) {
660         if (ii == destroyedIdx1 || ii == destroyedIdx2 || ii == deleteLaterIdx)
661             continue;
662         QMetaMethod m = metaObject->method(ii);
663         if (m.access() == QMetaMethod::Private)
664             continue;
665
666         // Extract method name
667         // It's safe to keep the raw name pointer
668         Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 7);
669         const char *rawName = m.name().constData();
670         const char *cptr = rawName;
671         char utf8 = 0;
672         while (*cptr) {
673             utf8 |= *cptr & 0x80;
674             ++cptr;
675         }
676
677         QQmlPropertyData *data = &methodIndexCache[ii - methodIndexCacheStart];
678         QQmlPropertyData *sigdata = 0;
679
680         data->lazyLoad(m);
681
682         if (data->isSignal())
683             data->flags |= signalFlags;
684         else
685             data->flags |= methodFlags;
686
687         if (!dynamicMetaObject)
688             data->flags |= QQmlPropertyData::IsDirect;
689
690         Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX);
691         data->metaObjectOffset = allowedRevisionCache.count() - 1;
692
693         if (data->isSignal()) {
694             sigdata = &signalHandlerIndexCache[signalHandlerIndex - signalHandlerIndexCacheStart];
695             *sigdata = *data;
696             sigdata->flags |= QQmlPropertyData::IsSignalHandler;
697         }
698
699         QQmlPropertyData *old = 0;
700
701         if (utf8) {
702             QHashedString methodName(QString::fromUtf8(rawName, cptr - rawName));
703             if (StringCache::mapped_type *it = stringCache.value(methodName))
704                 old = it->second;
705             setNamedProperty(methodName, ii, data, (old != 0));
706
707             if (data->isSignal()) {
708                 QHashedString on(QStringLiteral("on") % methodName.at(0).toUpper() % methodName.midRef(1));
709                 setNamedProperty(on, ii, sigdata, (old != 0));
710                 ++signalHandlerIndex;
711             }
712         } else {
713             QHashedCStringRef methodName(rawName, cptr - rawName);
714             if (StringCache::mapped_type *it = stringCache.value(methodName))
715                 old = it->second;
716             setNamedProperty(methodName, ii, data, (old != 0));
717
718             if (data->isSignal()) {
719                 int length = methodName.length();
720
721                 QVarLengthArray<char, 128> str(length+3);
722                 str[0] = 'o';
723                 str[1] = 'n';
724                 str[2] = toupper(rawName[0]);
725                 if (length > 1)
726                     memcpy(&str[3], &rawName[1], length - 1);
727                 str[length + 2] = '\0';
728
729                 QHashedString on(QString::fromLatin1(str.data()));
730                 setNamedProperty(on, ii, data, (old != 0));
731                 ++signalHandlerIndex;
732             }
733         }
734
735         if (old) {
736             // We only overload methods in the same class, exactly like C++
737             if (old->isFunction() && old->coreIndex >= methodOffset)
738                 data->flags |= QQmlPropertyData::IsOverload;
739
740             data->markAsOverrideOf(old);
741         }
742     }
743
744     int propCount = metaObject->propertyCount();
745     int propOffset = metaObject->propertyOffset();
746
747     // update() should have reserved enough space in the vector that this doesn't cause a realloc
748     // and invalidate the stringCache.
749     propertyIndexCache.resize(propCount - propertyIndexCacheStart);
750     for (int ii = propOffset; ii < propCount; ++ii) {
751         QMetaProperty p = metaObject->property(ii);
752         if (!p.isScriptable())
753             continue;
754
755         const char *str = p.name();
756         char utf8 = 0;
757         const char *cptr = str;
758         while (*cptr != 0) {
759             utf8 |= *cptr & 0x80;
760             ++cptr;
761         }
762
763         QQmlPropertyData *data = &propertyIndexCache[ii - propertyIndexCacheStart];
764
765         data->lazyLoad(p, engine);
766         data->flags |= propertyFlags;
767
768         if (!dynamicMetaObject)
769             data->flags |= QQmlPropertyData::IsDirect;
770
771         Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX);
772         data->metaObjectOffset = allowedRevisionCache.count() - 1;
773
774         QQmlPropertyData *old = 0;
775
776         if (utf8) {
777             QHashedString propName(QString::fromUtf8(str, cptr - str));
778             if (StringCache::mapped_type *it = stringCache.value(propName))
779                 old = it->second;
780             setNamedProperty(propName, ii, data, (old != 0));
781         } else {
782             QHashedCStringRef propName(str, cptr - str);
783             if (StringCache::mapped_type *it = stringCache.value(propName))
784                 old = it->second;
785             setNamedProperty(propName, ii, data, (old != 0));
786         }
787
788         QQmlAccessorProperties::Property *accessorProperty = accessorProperties.property(str);
789
790         // Fast properties may not be overrides or revisioned
791         Q_ASSERT(accessorProperty == 0 || (old == 0 && data->revision == 0));
792
793         if (accessorProperty) {
794             data->flags |= QQmlPropertyData::HasAccessors;
795             data->accessors = accessorProperty->accessors;
796             data->accessorData = accessorProperty->data;
797         } else if (old) {
798             data->markAsOverrideOf(old);
799         }
800     }
801 }
802
803 QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const
804 {
805     if (p && p->notFullyResolved())
806         resolve(p);
807
808     return p;
809 }
810
811 void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
812 {
813     Q_ASSERT(data->notFullyResolved());
814
815     data->propType = QMetaType::type(data->propTypeName);
816
817     if (!data->isFunction())
818         data->flags |= flagsForPropertyType(data->propType, engine);
819
820     data->flags &= ~QQmlPropertyData::NotFullyResolved;
821 }
822
823 void QQmlPropertyCache::updateRecur(QQmlEngine *engine, const QMetaObject *metaObject)
824 {
825     if (!metaObject)
826         return;
827
828     updateRecur(engine, metaObject->superClass());
829
830     append(engine, metaObject, -1);
831 }
832
833 void QQmlPropertyCache::update(QQmlEngine *engine, const QMetaObject *metaObject)
834 {
835     Q_ASSERT(engine);
836     Q_ASSERT(metaObject);
837     Q_ASSERT(stringCache.isEmpty());
838
839     // Preallocate enough space in the index caches for all the properties/methods/signals that
840     // are not cached in a parent cache so that the caches never need to be reallocated as this
841     // would invalidate pointers stored in the stringCache.
842     int pc = metaObject->propertyCount();
843     int mc = metaObject->methodCount();
844     int sc = metaObjectSignalCount(metaObject);
845     propertyIndexCache.reserve(pc - propertyIndexCacheStart);
846     methodIndexCache.reserve(mc - methodIndexCacheStart);
847     signalHandlerIndexCache.reserve(sc - signalHandlerIndexCacheStart);
848
849     // Reserve enough space in the stringCache for all properties/methods/signals including those
850     // cached in a parent cache.
851     stringCache.reserve(pc + mc + sc);
852
853     updateRecur(engine,metaObject);
854 }
855
856 /*! \internal
857     invalidates and updates the PropertyCache if the QMetaObject has changed.
858     This function is used in the tooling to update dynamic properties.
859 */
860 void QQmlPropertyCache::invalidate(QQmlEngine *engine, const QMetaObject *metaObject)
861 {
862     stringCache.clear();
863     propertyIndexCache.clear();
864     methodIndexCache.clear();
865     signalHandlerIndexCache.clear();
866
867     _hasPropertyOverrides = false;
868     argumentsCache = 0;
869
870     int pc = metaObject->propertyCount();
871     int mc = metaObject->methodCount();
872     int sc = metaObjectSignalCount(metaObject);
873     int reserve = pc + mc + sc;
874
875     if (parent()) {
876         propertyIndexCacheStart = parent()->propertyIndexCache.count() + parent()->propertyIndexCacheStart;
877         methodIndexCacheStart = parent()->methodIndexCache.count() + parent()->methodIndexCacheStart;
878         signalHandlerIndexCacheStart = parent()->signalHandlerIndexCache.count() + parent()->signalHandlerIndexCacheStart;
879         stringCache.linkAndReserve(parent()->stringCache, reserve);
880         append(engine, metaObject, -1);
881     } else {
882         propertyIndexCacheStart = 0;
883         methodIndexCacheStart = 0;
884         signalHandlerIndexCacheStart = 0;
885         update(engine, metaObject);
886     }
887 }
888
889 /*! \internal
890     \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
891     This is different from QMetaMethod::methodIndex().
892 */
893 QQmlPropertyData *
894 QQmlPropertyCache::signal(int index, QQmlPropertyCache **c) const
895 {
896     if (index < 0 || index >= (signalHandlerIndexCacheStart + signalHandlerIndexCache.count()))
897         return 0;
898
899     if (index < signalHandlerIndexCacheStart)
900         return _parent->signal(index, c);
901
902     QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - signalHandlerIndexCacheStart));
903     if (rv->notFullyResolved()) resolve(rv);
904     Q_ASSERT(rv->isSignal() || rv->coreIndex == -1);
905     if (c) *c = const_cast<QQmlPropertyCache *>(this);
906     return rv;
907 }
908
909 int QQmlPropertyCache::methodIndexToSignalIndex(int index) const
910 {
911     if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count()))
912         return index;
913
914     if (index < methodIndexCacheStart)
915         return _parent->methodIndexToSignalIndex(index);
916
917     return index - methodIndexCacheStart + signalHandlerIndexCacheStart;
918 }
919
920 QQmlPropertyData *
921 QQmlPropertyCache::property(int index) const
922 {
923     if (index < 0 || index >= (propertyIndexCacheStart + propertyIndexCache.count()))
924         return 0;
925
926     if (index < propertyIndexCacheStart)
927         return _parent->property(index);
928
929     QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&propertyIndexCache.at(index - propertyIndexCacheStart));
930     return ensureResolved(rv);
931 }
932
933 QQmlPropertyData *
934 QQmlPropertyCache::method(int index) const
935 {
936     if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count()))
937         return 0;
938
939     if (index < methodIndexCacheStart)
940         return _parent->method(index);
941
942     QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - methodIndexCacheStart));
943     return ensureResolved(rv);
944 }
945
946 QQmlPropertyData *QQmlPropertyCache::findProperty(StringCache::ConstIterator it, QObject *object, QQmlContextData *context) const
947 {
948     QQmlData *data = (object ? QQmlData::get(object) : 0);
949     const QQmlVMEMetaObject *vmemo = (data && data->hasVMEMetaObject ? static_cast<const QQmlVMEMetaObject *>(object->metaObject()) : 0);
950     return findProperty(it, vmemo, context);
951 }
952
953 namespace {
954
955 inline bool contextHasNoExtensions(QQmlContextData *context)
956 {
957     // This context has no extension if its parent is the engine's rootContext,
958     // which has children but no imports
959     return (!context->parent || !context->parent->imports);
960 }
961
962 inline int maximumIndexForProperty(QQmlPropertyData *prop, const QQmlVMEMetaObject *vmemo)
963 {
964     return (prop->isFunction() ? vmemo->methodCount()
965                                : prop->isSignalHandler() ? vmemo->signalCount()
966                                                          : vmemo->propertyCount());
967 }
968
969 }
970
971 QQmlPropertyData *QQmlPropertyCache::findProperty(StringCache::ConstIterator it, const QQmlVMEMetaObject *vmemo, QQmlContextData *context) const
972 {
973     StringCache::ConstIterator end = stringCache.end();
974
975     if (it != end) {
976         QQmlPropertyData *result = it.value().second;
977
978         // If there exists a typed property (not a function or signal handler), of the
979         // right name available to the specified context, we need to return that
980         // property rather than any subsequent override
981
982         if (vmemo && context && !contextHasNoExtensions(context)) {
983             // Find the meta-object that corresponds to the supplied context
984             do {
985                 if (vmemo->ctxt == context)
986                     break;
987
988                 vmemo = vmemo->parentVMEMetaObject();
989             } while (vmemo);
990         }
991
992         if (vmemo) {
993             // Ensure that the property we resolve to is accessible from this meta-object
994             do {
995                 const StringCache::mapped_type &property(it.value());
996
997                 if (property.first < maximumIndexForProperty(property.second, vmemo)) {
998                     // This property is available in the specified context
999                     if (property.second->isFunction() || property.second->isSignalHandler()) {
1000                         // Prefer the earlier resolution
1001                     } else {
1002                         // Prefer the typed property to any previous property found
1003                         result = property.second;
1004                     }
1005                     break;
1006                 }
1007
1008                 // See if there is a better candidate
1009                 it = stringCache.findNext(it);
1010             } while (it != end);
1011         }
1012
1013         return ensureResolved(result);
1014     }
1015
1016     return 0;
1017 }
1018
1019 QString QQmlPropertyData::name(QObject *object)
1020 {
1021     if (!object)
1022         return QString();
1023
1024     return name(object->metaObject());
1025 }
1026
1027 QString QQmlPropertyData::name(const QMetaObject *metaObject)
1028 {
1029     if (!metaObject || coreIndex == -1)
1030         return QString();
1031
1032     if (flags & IsFunction) {
1033         QMetaMethod m = metaObject->method(coreIndex);
1034
1035         return QString::fromUtf8(m.name().constData());
1036     } else {
1037         QMetaProperty p = metaObject->property(coreIndex);
1038         return QString::fromUtf8(p.name());
1039     }
1040 }
1041
1042 void QQmlPropertyData::markAsOverrideOf(QQmlPropertyData *predecessor)
1043 {
1044     overrideIndexIsProperty = !predecessor->isFunction();
1045     overrideIndex = predecessor->coreIndex;
1046
1047     predecessor->flags |= QQmlPropertyData::IsOverridden;
1048 }
1049
1050 QStringList QQmlPropertyCache::propertyNames() const
1051 {
1052     QStringList keys;
1053     for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter)
1054         keys.append(iter.key());
1055     return keys;
1056 }
1057
1058 struct StaticQtMetaObject : public QObject
1059 {
1060     static const QMetaObject *get()
1061         { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; }
1062 };
1063
1064 static int EnumType(const QMetaObject *metaobj, const QByteArray &str, int type)
1065 {
1066     QByteArray scope;
1067     QByteArray name;
1068     int scopeIdx = str.lastIndexOf("::");
1069     if (scopeIdx != -1) {
1070         scope = str.left(scopeIdx);
1071         name = str.mid(scopeIdx + 2);
1072     } else {
1073         name = str;
1074     }
1075     const QMetaObject *meta;
1076     if (scope == "Qt")
1077         meta = StaticQtMetaObject::get();
1078     else
1079         meta = metaobj;
1080     for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
1081         QMetaEnum m = meta->enumerator(i);
1082         if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
1083             return QVariant::Int;
1084     }
1085     return type;
1086 }
1087
1088 QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int argc, const QList<QByteArray> &names)
1089 {
1090     typedef QQmlPropertyCacheMethodArguments A;
1091     A *args = static_cast<A *>(malloc(sizeof(A) + (argc + 1) * sizeof(int)));
1092     args->arguments[0] = argc;
1093     args->argumentsValid = false;
1094     args->signalParameterStringForJS = 0;
1095     args->signalParameterCountForJS = 0;
1096     args->parameterError = false;
1097     args->names = argc ? new QList<QByteArray>(names) : 0;
1098     args->next = argumentsCache;
1099     argumentsCache = args;
1100     return args;
1101 }
1102
1103 /*! \internal
1104     \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
1105     This is different from QMetaMethod::methodIndex().
1106 */
1107 QString QQmlPropertyCache::signalParameterStringForJS(int index, int *count, QString *errorString)
1108 {
1109     QQmlPropertyCache *c = 0;
1110     QQmlPropertyData *signalData = signal(index, &c);
1111     if (!signalData)
1112         return QString();
1113
1114     typedef QQmlPropertyCacheMethodArguments A;
1115
1116     if (signalData->arguments) {
1117         A *arguments = static_cast<A *>(signalData->arguments);
1118         if (arguments->signalParameterStringForJS) {
1119             if (count)
1120                 *count = arguments->signalParameterCountForJS;
1121             if (arguments->parameterError) {
1122                 if (errorString)
1123                     *errorString = *arguments->signalParameterStringForJS;
1124                 return QString();
1125             }
1126             return *arguments->signalParameterStringForJS;
1127         }
1128     }
1129
1130     QList<QByteArray> parameterNameList = signalParameterNames(index);
1131
1132     if (!signalData->arguments) {
1133         A *args = c->createArgumentsObject(parameterNameList.count(), parameterNameList);
1134         signalData->arguments = args;
1135     }
1136
1137     QQmlRewrite::RewriteSignalHandler rewriter;
1138     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1139     const QString &parameters = rewriter.createParameterString(parameterNameList,
1140                                                                ep->v8engine()->illegalNames());
1141
1142     bool error = rewriter.hasParameterError();
1143     A *arguments = static_cast<A *>(signalData->arguments);
1144     arguments->signalParameterStringForJS = new QString(error ? rewriter.parameterError() : parameters);
1145     arguments->signalParameterCountForJS = rewriter.parameterCountForJS();
1146     if (count)
1147         *count = arguments->signalParameterCountForJS;
1148     if (error) {
1149         arguments->parameterError = true;
1150         if (errorString)
1151             *errorString = *arguments->signalParameterStringForJS;
1152         return QString();
1153     }
1154     return *arguments->signalParameterStringForJS;
1155 }
1156
1157 // Returns an array of the arguments for method \a index.  The first entry in the array
1158 // is the number of arguments.
1159 int *QQmlPropertyCache::methodParameterTypes(QObject *object, int index,
1160                                                      QVarLengthArray<int, 9> &dummy,
1161                                                      QByteArray *unknownTypeError)
1162 {
1163     Q_ASSERT(object && index >= 0);
1164
1165     QQmlData *ddata = QQmlData::get(object, false);
1166
1167     if (ddata && ddata->propertyCache) {
1168         typedef QQmlPropertyCacheMethodArguments A;
1169
1170         QQmlPropertyCache *c = ddata->propertyCache;
1171         Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count());
1172
1173         while (index < c->methodIndexCacheStart)
1174             c = c->_parent;
1175
1176         QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart));
1177
1178         if (rv->arguments && static_cast<A *>(rv->arguments)->argumentsValid)
1179             return static_cast<A *>(rv->arguments)->arguments;
1180
1181         const QMetaObject *metaObject = c->createMetaObject();
1182         Q_ASSERT(metaObject);
1183         QMetaMethod m = metaObject->method(index);
1184
1185         int argc = m.parameterCount();
1186         if (!rv->arguments) {
1187             A *args = c->createArgumentsObject(argc);
1188             rv->arguments = args;
1189         }
1190         A *args = static_cast<A *>(rv->arguments);
1191
1192         QList<QByteArray> argTypeNames; // Only loaded if needed
1193
1194         for (int ii = 0; ii < argc; ++ii) {
1195             int type = m.parameterType(ii);
1196             QMetaType::TypeFlags flags = QMetaType::typeFlags(type);
1197             if (flags & QMetaType::IsEnumeration)
1198                 type = QVariant::Int;
1199             else if (type == QMetaType::UnknownType ||
1200                      (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) &&
1201                       type != qMetaTypeId<QJSValue>())) {
1202                 //the UserType clause is to catch registered QFlags
1203                 if (argTypeNames.isEmpty())
1204                     argTypeNames = m.parameterTypes();
1205                 type = EnumType(object->metaObject(), argTypeNames.at(ii), type);
1206             }
1207             if (type == QMetaType::UnknownType) {
1208                 if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii);
1209                 return 0;
1210             }
1211             args->arguments[ii + 1] = type;
1212         }
1213         args->argumentsValid = true;
1214         return static_cast<A *>(rv->arguments)->arguments;
1215
1216     } else {
1217         QMetaMethod m = object->metaObject()->method(index);
1218         int argc = m.parameterCount();
1219         dummy.resize(argc + 1);
1220         dummy[0] = argc;
1221         QList<QByteArray> argTypeNames; // Only loaded if needed
1222
1223         for (int ii = 0; ii < argc; ++ii) {
1224             int type = m.parameterType(ii);
1225             QMetaType::TypeFlags flags = QMetaType::typeFlags(type);
1226             if (flags & QMetaType::IsEnumeration)
1227                 type = QVariant::Int;
1228             else if (type == QMetaType::UnknownType ||
1229                      (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) &&
1230                       type != qMetaTypeId<QJSValue>())) {
1231                 //the UserType clause is to catch registered QFlags)
1232                 if (argTypeNames.isEmpty())
1233                     argTypeNames = m.parameterTypes();
1234                 type = EnumType(object->metaObject(), argTypeNames.at(ii), type);
1235             }
1236             if (type == QMetaType::UnknownType) {
1237                 if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii);
1238                 return 0;
1239             }
1240             dummy[ii + 1] = type;
1241         }
1242
1243         return dummy.data();
1244     }
1245 }
1246
1247 // Returns the return type of the method.
1248 int QQmlPropertyCache::methodReturnType(QObject *object, const QQmlPropertyData &data,
1249                                         QByteArray *unknownTypeError)
1250 {
1251     Q_ASSERT(object && data.coreIndex >= 0);
1252
1253     int type = data.propType;
1254
1255     const char *propTypeName = 0;
1256
1257     if (type == QMetaType::UnknownType) {
1258         // Find the return type name from the method info
1259         QMetaMethod m;
1260
1261         QQmlData *ddata = QQmlData::get(object, false);
1262         if (ddata && ddata->propertyCache) {
1263             QQmlPropertyCache *c = ddata->propertyCache;
1264             Q_ASSERT(data.coreIndex < c->methodIndexCacheStart + c->methodIndexCache.count());
1265
1266             while (data.coreIndex < c->methodIndexCacheStart)
1267                 c = c->_parent;
1268
1269             const QMetaObject *metaObject = c->createMetaObject();
1270             Q_ASSERT(metaObject);
1271             m = metaObject->method(data.coreIndex);
1272         } else {
1273             m = object->metaObject()->method(data.coreIndex);
1274         }
1275
1276         type = m.returnType();
1277         propTypeName = m.typeName();
1278     }
1279
1280     QMetaType::TypeFlags flags = QMetaType::typeFlags(type);
1281     if (flags & QMetaType::IsEnumeration) {
1282         type = QVariant::Int;
1283     } else if (type == QMetaType::UnknownType ||
1284                (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) &&
1285                type != qMetaTypeId<QJSValue>())) {
1286         //the UserType clause is to catch registered QFlags
1287         type = EnumType(object->metaObject(), propTypeName, type);
1288     }
1289
1290     if (type == QMetaType::UnknownType) {
1291         if (unknownTypeError) *unknownTypeError = propTypeName;
1292     }
1293
1294     return type;
1295 }
1296
1297 int QQmlPropertyCache::originalClone(int index)
1298 {
1299     while (signal(index)->isCloned())
1300         --index;
1301     return index;
1302 }
1303
1304 int QQmlPropertyCache::originalClone(QObject *object, int index)
1305 {
1306     QQmlData *data = QQmlData::get(object, false);
1307     if (data && data->propertyCache) {
1308         QQmlPropertyCache *cache = data->propertyCache;
1309         while (cache->signal(index)->isCloned())
1310             --index;
1311     } else {
1312         while (QMetaObjectPrivate::signal(object->metaObject(), index).attributes() & QMetaMethod::Cloned)
1313             --index;
1314     }
1315     return index;
1316 }
1317
1318 QQmlPropertyData qQmlPropertyCacheCreate(const QMetaObject *metaObject, const QString &property)
1319 {
1320     Q_ASSERT(metaObject);
1321
1322     QQmlPropertyData rv;
1323     {
1324         const QMetaObject *cmo = metaObject;
1325         const QByteArray propertyName = property.toUtf8();
1326         while (cmo) {
1327             int idx = cmo->indexOfProperty(propertyName);
1328             if (idx != -1) {
1329                 QMetaProperty p = cmo->property(idx);
1330                 if (p.isScriptable()) {
1331                     rv.load(p);
1332                     return rv;
1333                 } else {
1334                     while (cmo && cmo->propertyOffset() >= idx)
1335                         cmo = cmo->superClass();
1336                 }
1337             } else {
1338                 cmo = 0;
1339             }
1340         }
1341     }
1342
1343     //Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
1344     static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
1345     static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()");
1346     static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()");
1347
1348     int methodCount = metaObject->methodCount();
1349     for (int ii = methodCount - 1; ii >= 0; --ii) {
1350         if (ii == destroyedIdx1 || ii == destroyedIdx2 || ii == deleteLaterIdx)
1351             continue;
1352         QMetaMethod m = metaObject->method(ii);
1353         if (m.access() == QMetaMethod::Private)
1354             continue;
1355         QString methodName = QString::fromUtf8(m.name().constData());
1356
1357         if (methodName == property) {
1358             rv.load(m);
1359             return rv;
1360         }
1361     }
1362
1363     return rv;
1364 }
1365
1366 inline const QString &qQmlPropertyCacheToString(const QString &string)
1367 {
1368     return string;
1369 }
1370
1371 inline QString qQmlPropertyCacheToString(const QHashedV8String &string)
1372 {
1373     return QV8Engine::toStringStatic(string.string());
1374 }
1375
1376 template<typename T>
1377 QQmlPropertyData *
1378 qQmlPropertyCacheProperty(QQmlEngine *engine, QObject *obj, const T &name,
1379                           QQmlContextData *context, QQmlPropertyData &local)
1380 {
1381     QQmlPropertyCache *cache = 0;
1382
1383     QQmlData *ddata = QQmlData::get(obj, false);
1384
1385     if (ddata && ddata->propertyCache) {
1386         cache = ddata->propertyCache;
1387     } else if (engine) {
1388         QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
1389         cache = ep->cache(obj);
1390         if (cache) {
1391             ddata = QQmlData::get(obj, true);
1392             cache->addref();
1393             ddata->propertyCache = cache;
1394         }
1395     }
1396
1397     QQmlPropertyData *rv = 0;
1398
1399     if (cache) {
1400         rv = cache->property(name, obj, context);
1401     } else {
1402         local = qQmlPropertyCacheCreate(obj->metaObject(), qQmlPropertyCacheToString(name));
1403         if (local.isValid())
1404             rv = &local;
1405     }
1406
1407     return rv;
1408 }
1409
1410 QQmlPropertyData *
1411 QQmlPropertyCache::property(QQmlEngine *engine, QObject *obj, const QHashedV8String &name,
1412                             QQmlContextData *context, QQmlPropertyData &local)
1413 {
1414     return qQmlPropertyCacheProperty<QHashedV8String>(engine, obj, name, context, local);
1415 }
1416
1417 QQmlPropertyData *
1418 QQmlPropertyCache::property(QQmlEngine *engine, QObject *obj,
1419                                     const QString &name, QQmlContextData *context, QQmlPropertyData &local)
1420 {
1421     return qQmlPropertyCacheProperty<QString>(engine, obj, name, context, local);
1422 }
1423
1424 static inline const QMetaObjectPrivate *priv(const uint* data)
1425 { return reinterpret_cast<const QMetaObjectPrivate*>(data); }
1426
1427 bool QQmlPropertyCache::isDynamicMetaObject(const QMetaObject *mo)
1428 {
1429     return priv(mo->d.data)->revision >= 3 && priv(mo->d.data)->flags & DynamicMetaObject;
1430 }
1431
1432 const char *QQmlPropertyCache::className() const
1433 {
1434     if (!_ownMetaObject && _metaObject)
1435         return _metaObject->className();
1436     else
1437         return _dynamicClassName.constData();
1438 }
1439
1440 void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder)
1441 {
1442     struct Sort { static bool lt(const QPair<QString, QQmlPropertyData *> &lhs,
1443                                  const QPair<QString, QQmlPropertyData *> &rhs) {
1444         return lhs.second->coreIndex < rhs.second->coreIndex;
1445     } };
1446
1447     struct Insert { static void in(QQmlPropertyCache *This,
1448                                    QList<QPair<QString, QQmlPropertyData *> > &properties,
1449                                    QList<QPair<QString, QQmlPropertyData *> > &methods,
1450                                    StringCache::ConstIterator iter, QQmlPropertyData *data) {
1451         if (data->isSignalHandler())
1452             return;
1453
1454         if (data->isFunction()) {
1455             if (data->coreIndex < This->methodIndexCacheStart)
1456                 return;
1457
1458             QPair<QString, QQmlPropertyData *> entry = qMakePair((QString)iter.key(), data);
1459             // Overrides can cause the entry to already exist
1460             if (!methods.contains(entry)) methods.append(entry);
1461
1462             data = This->overrideData(data);
1463             if (data && !data->isFunction()) Insert::in(This, properties, methods, iter, data);
1464         } else {
1465             if (data->coreIndex < This->propertyIndexCacheStart)
1466                 return;
1467
1468             QPair<QString, QQmlPropertyData *> entry = qMakePair((QString)iter.key(), data);
1469             // Overrides can cause the entry to already exist
1470             if (!properties.contains(entry)) properties.append(entry);
1471
1472             data = This->overrideData(data);
1473             if (data) Insert::in(This, properties, methods, iter, data);
1474         }
1475
1476     } };
1477
1478     builder.setClassName(_dynamicClassName);
1479
1480     QList<QPair<QString, QQmlPropertyData *> > properties;
1481     QList<QPair<QString, QQmlPropertyData *> > methods;
1482
1483     for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter)
1484         Insert::in(this, properties, methods, iter, iter.value().second);
1485
1486     Q_ASSERT(properties.count() == propertyIndexCache.count());
1487     Q_ASSERT(methods.count() == methodIndexCache.count());
1488
1489     qSort(properties.begin(), properties.end(), Sort::lt);
1490     qSort(methods.begin(), methods.end(), Sort::lt);
1491
1492     for (int ii = 0; ii < properties.count(); ++ii) {
1493         QQmlPropertyData *data = properties.at(ii).second;
1494
1495         int notifierId = -1;
1496         if (data->notifyIndex != -1)
1497             notifierId = data->notifyIndex - signalHandlerIndexCacheStart;
1498
1499         QMetaPropertyBuilder property = builder.addProperty(properties.at(ii).first.toUtf8(),
1500                                                             QMetaType::typeName(data->propType),
1501                                                             notifierId);
1502
1503         property.setReadable(true);
1504         property.setWritable(data->isWritable());
1505         property.setResettable(data->isResettable());
1506     }
1507
1508     for (int ii = 0; ii < methods.count(); ++ii) {
1509         QQmlPropertyData *data = methods.at(ii).second;
1510
1511         QByteArray returnType;
1512         if (data->propType != 0)
1513             returnType = QMetaType::typeName(data->propType);
1514
1515         QByteArray signature = methods.at(ii).first.toUtf8() + "(";
1516
1517         QQmlPropertyCacheMethodArguments *arguments = 0;
1518         if (data->hasArguments()) {
1519             arguments = (QQmlPropertyCacheMethodArguments *)data->arguments;
1520             Q_ASSERT(arguments->argumentsValid);
1521             for (int ii = 0; ii < arguments->arguments[0]; ++ii) {
1522                 if (ii != 0) signature.append(",");
1523                 signature.append(QMetaType::typeName(arguments->arguments[1 + ii]));
1524             }
1525         }
1526
1527         signature.append(")");
1528
1529         QMetaMethodBuilder method;
1530         if (data->isSignal()) {
1531             method = builder.addSignal(signature);
1532         } else {
1533             method = builder.addSlot(signature);
1534         }
1535         method.setAccess(QMetaMethod::Protected);
1536
1537         if (arguments && arguments->names)
1538             method.setParameterNames(*arguments->names);
1539
1540         if (!returnType.isEmpty())
1541             method.setReturnType(returnType);
1542     }
1543
1544     if (!_defaultPropertyName.isEmpty()) {
1545         QQmlPropertyData *dp = property(_defaultPropertyName, 0, 0);
1546         if (dp && dp->coreIndex >= propertyIndexCacheStart) {
1547             Q_ASSERT(!dp->isFunction());
1548             builder.addClassInfo("DefaultProperty", _defaultPropertyName.toUtf8());
1549         }
1550     }
1551 }
1552
1553 /*! \internal
1554     \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
1555     This is different from QMetaMethod::methodIndex().
1556 */
1557 QList<QByteArray> QQmlPropertyCache::signalParameterNames(int index) const
1558 {
1559     QQmlPropertyData *signalData = signal(index);
1560     if (signalData && signalData->hasArguments()) {
1561         QQmlPropertyCacheMethodArguments *args = (QQmlPropertyCacheMethodArguments *)signalData->arguments;
1562         if (args && args->names)
1563             return *args->names;
1564         const QMetaMethod &method = QMetaObjectPrivate::signal(firstCppMetaObject(), index);
1565         return method.parameterNames();
1566     }
1567     return QList<QByteArray>();
1568 }
1569
1570 // Returns true if \a from is assignable to a property of type \a to
1571 bool QQmlMetaObject::canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to)
1572 {
1573     Q_ASSERT(!from.isNull() && !to.isNull());
1574
1575     struct I { static bool equal(const QMetaObject *lhs, const QMetaObject *rhs) {
1576         return lhs == rhs || (lhs && rhs && lhs->d.stringdata == rhs->d.stringdata);
1577     } };
1578
1579     const QMetaObject *tom = to._m.isT1()?to._m.asT1()->metaObject():to._m.asT2();
1580     if (tom == &QObject::staticMetaObject) return true;
1581
1582     if (from._m.isT1() && to._m.isT1()) { // QQmlPropertyCache -> QQmlPropertyCache
1583         QQmlPropertyCache *fromp = from._m.asT1();
1584         QQmlPropertyCache *top = to._m.asT1();
1585
1586         while (fromp) {
1587             if (fromp == top) return true;
1588             fromp = fromp->parent();
1589         }
1590     } else if (from._m.isT1() && to._m.isT2()) { // QQmlPropertyCache -> QMetaObject
1591         QQmlPropertyCache *fromp = from._m.asT1();
1592
1593         while (fromp) {
1594             const QMetaObject *fromm = fromp->metaObject();
1595             if (fromm && I::equal(fromm, tom)) return true;
1596             fromp = fromp->parent();
1597         }
1598     } else if (from._m.isT2() && to._m.isT1()) { // QMetaObject -> QQmlPropertyCache
1599         const QMetaObject *fromm = from._m.asT2();
1600
1601         if (!tom) return false;
1602
1603         while (fromm) {
1604             if (I::equal(fromm, tom)) return true;
1605             fromm = fromm->superClass();
1606         }
1607     } else { // QMetaObject -> QMetaObject
1608         const QMetaObject *fromm = from._m.asT2();
1609
1610         while (fromm) {
1611             if (I::equal(fromm, tom)) return true;
1612             fromm = fromm->superClass();
1613         }
1614     }
1615
1616     return false;
1617 }
1618
1619 QQmlPropertyCache *QQmlMetaObject::propertyCache(QQmlEnginePrivate *e) const
1620 {
1621     if (_m.isNull()) return 0;
1622     if (_m.isT1()) return _m.asT1();
1623     else return e->cache(_m.asT2());
1624 }
1625
1626 QT_END_NAMESPACE