Fixed rounding errors in QtQuick1 to int conversions
[qt:qt.git] / src / declarative / qml / qdeclarativeobjectscriptclass.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtDeclarative 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 "private/qdeclarativeobjectscriptclass_p.h"
43
44 #include "private/qdeclarativeengine_p.h"
45 #include "private/qdeclarativecontext_p.h"
46 #include "private/qdeclarativedata_p.h"
47 #include "private/qdeclarativetypenamescriptclass_p.h"
48 #include "private/qdeclarativelistscriptclass_p.h"
49 #include "private/qdeclarativebinding_p.h"
50 #include "private/qdeclarativeguard_p.h"
51 #include "private/qdeclarativevmemetaobject_p.h"
52
53 #include <QtCore/qtimer.h>
54 #include <QtCore/qvarlengtharray.h>
55 #include <QtScript/qscriptcontextinfo.h>
56
57 Q_DECLARE_METATYPE(QScriptValue)
58
59 #if defined(__GNUC__)
60 # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
61 // The code in this file does not violate strict aliasing, but GCC thinks it does
62 // so turn off the warnings for us to have a clean build
63 #  pragma GCC diagnostic ignored "-Wstrict-aliasing"
64 # endif
65 #endif
66
67 QT_BEGIN_NAMESPACE
68
69 struct ObjectData : public QScriptDeclarativeClass::Object {
70     ObjectData(QObject *o, int t) : object(o), type(t) {
71         if (o) {
72             QDeclarativeData *ddata = QDeclarativeData::get(object, true);
73             if (ddata) ddata->objectDataRefCount++;
74         }
75     }
76
77     virtual ~ObjectData() {
78         if (object && !object->parent()) {
79             QDeclarativeData *ddata = QDeclarativeData::get(object, false);
80             if (ddata && !ddata->indestructible && 0 == --ddata->objectDataRefCount) 
81                 object->deleteLater();
82         }
83     }
84
85     QDeclarativeGuard<QObject> object;
86     int type;
87 };
88
89 /*
90     The QDeclarativeObjectScriptClass handles property access for QObjects
91     via QtScript. It is also used to provide a more useful API in
92     QtScript for QML.
93  */
94 QDeclarativeObjectScriptClass::QDeclarativeObjectScriptClass(QDeclarativeEngine *bindEngine)
95 : QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)),
96   methods(bindEngine), lastData(0), engine(bindEngine)
97 {
98     QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
99
100     m_destroy = scriptEngine->newFunction(destroy);
101     m_destroyId = createPersistentIdentifier(QLatin1String("destroy"));
102     m_toString = scriptEngine->newFunction(tostring);
103     m_toStringId = createPersistentIdentifier(QLatin1String("toString"));
104 }
105
106 QDeclarativeObjectScriptClass::~QDeclarativeObjectScriptClass()
107 {
108 }
109
110 QScriptValue QDeclarativeObjectScriptClass::newQObject(QObject *object, int type)
111 {
112     QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
113
114     if (!object)
115         return scriptEngine->nullValue();
116 //        return newObject(scriptEngine, this, new ObjectData(object, type));
117
118     if (QObjectPrivate::get(object)->wasDeleted)
119        return scriptEngine->undefinedValue();
120
121     QDeclarativeData *ddata = QDeclarativeData::get(object, true);
122
123     if (!ddata) {
124        return scriptEngine->undefinedValue();
125     } else if (!ddata->indestructible && !object->parent()) {
126         return newObject(scriptEngine, this, new ObjectData(object, type));
127     } else if (!ddata->scriptValue) {
128         ddata->scriptValue = new QScriptValue(newObject(scriptEngine, this, new ObjectData(object, type)));
129         return *ddata->scriptValue;
130     } else if (ddata->scriptValue->engine() == QDeclarativeEnginePrivate::getScriptEngine(engine)) {
131         return *ddata->scriptValue;
132     } else {
133         return newObject(scriptEngine, this, new ObjectData(object, type));
134     }
135 }
136
137 QObject *QDeclarativeObjectScriptClass::toQObject(const QScriptValue &value) const
138 {
139     return value.toQObject();
140 }
141
142 int QDeclarativeObjectScriptClass::objectType(const QScriptValue &value) const
143 {
144     if (scriptClass(value) != this)
145         return QVariant::Invalid;
146
147     Object *o = object(value);
148     return ((ObjectData*)(o))->type;
149 }
150
151 QScriptClass::QueryFlags
152 QDeclarativeObjectScriptClass::queryProperty(Object *object, const Identifier &name,
153                                     QScriptClass::QueryFlags flags)
154 {
155     return queryProperty(toQObject(object), name, flags, 0);
156 }
157
158 QScriptClass::QueryFlags
159 QDeclarativeObjectScriptClass::queryProperty(QObject *obj, const Identifier &name,
160                                              QScriptClass::QueryFlags flags, QDeclarativeContextData *evalContext,
161                                              QueryHints hints)
162 {
163     Q_UNUSED(flags);
164     lastData = 0;
165     lastTNData = 0;
166
167     if (name == m_destroyId.identifier ||
168         name == m_toStringId.identifier)
169         return QScriptClass::HandlesReadAccess;
170
171     if (!obj)
172         return 0;
173
174     QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine);
175     lastData = QDeclarativePropertyCache::property(engine, obj, name, local);
176     if ((hints & ImplicitObject) && lastData && lastData->revision != 0) {
177
178         QDeclarativeData *ddata = QDeclarativeData::get(obj);
179         if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(lastData)) 
180             return 0;
181     }
182
183     if (lastData)
184         return QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess;
185
186     if (!(hints & SkipAttachedProperties)) {
187         if (!evalContext && context()) {
188             // Global object, QScriptContext activation object, QDeclarativeContext object
189             QScriptValue scopeNode = scopeChainValue(context(), -3);
190             if (scopeNode.isValid()) {
191                 Q_ASSERT(scriptClass(scopeNode) == enginePrivate->contextClass);
192
193                 evalContext = enginePrivate->contextClass->contextFromValue(scopeNode);
194             }
195         }
196
197         if (evalContext && evalContext->imports) {
198             QDeclarativeTypeNameCache::Data *data = evalContext->imports->data(name);
199             if (data) {
200                 lastTNData = data;
201                 return QScriptClass::HandlesReadAccess;
202             }
203         }
204     }
205
206     if (!(hints & ImplicitObject)) {
207         local.coreIndex = -1;
208         lastData = &local;
209         return QScriptClass::HandlesWriteAccess;
210     }
211
212     return 0;
213 }
214
215 QDeclarativeObjectScriptClass::Value
216 QDeclarativeObjectScriptClass::property(Object *object, const Identifier &name)
217 {
218     return property(toQObject(object), name);
219 }
220
221 QDeclarativeObjectScriptClass::Value
222 QDeclarativeObjectScriptClass::property(QObject *obj, const Identifier &name)
223 {
224     QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
225
226     if (name == m_destroyId.identifier)
227         return Value(scriptEngine, m_destroy);
228     else if (name == m_toStringId.identifier)
229         return Value(scriptEngine, m_toString);
230
231     if (lastData && !lastData->isValid())
232         return Value();
233
234     Q_ASSERT(obj);
235
236     QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine);
237
238     if (lastTNData) {
239
240         if (lastTNData->type)
241             return Value(scriptEngine, enginePriv->typeNameClass->newObject(obj, lastTNData->type));
242         else
243             return Value(scriptEngine, enginePriv->typeNameClass->newObject(obj, lastTNData->typeNamespace));
244
245     } else if (lastData->flags & QDeclarativePropertyCache::Data::IsFunction) {
246         if (lastData->flags & QDeclarativePropertyCache::Data::IsVMEFunction) {
247             return Value(scriptEngine, ((QDeclarativeVMEMetaObject *)(obj->metaObject()))->vmeMethod(lastData->coreIndex));
248         } else {
249             // Uncomment to use QtScript method call logic
250             // QScriptValue sobj = scriptEngine->newQObject(obj);
251             // return Value(scriptEngine, sobj.property(toString(name)));
252             return Value(scriptEngine, methods.newMethod(obj, lastData));
253         }
254     } else {
255         if (enginePriv->captureProperties && !(lastData->flags & QDeclarativePropertyCache::Data::IsConstant)) {
256             if (lastData->coreIndex == 0) {
257                 enginePriv->capturedProperties <<
258                     QDeclarativeEnginePrivate::CapturedProperty(QDeclarativeData::get(obj, true)->objectNameNotifier());
259             } else {
260                 enginePriv->capturedProperties <<
261                     QDeclarativeEnginePrivate::CapturedProperty(obj, lastData->coreIndex, lastData->notifyIndex);
262             }
263         }
264
265         if (QDeclarativeValueTypeFactory::isValueType((uint)lastData->propType)) {
266             QDeclarativeValueType *valueType = enginePriv->valueTypes[lastData->propType];
267             if (valueType)
268                 return Value(scriptEngine, enginePriv->valueTypeClass->newObject(obj, lastData->coreIndex, valueType));
269         }
270
271         if (lastData->flags & QDeclarativePropertyCache::Data::IsQList) {
272             return Value(scriptEngine, enginePriv->listClass->newList(obj, lastData->coreIndex, lastData->propType));
273         } else if (lastData->flags & QDeclarativePropertyCache::Data::IsQObjectDerived) {
274             QObject *rv = 0;
275             void *args[] = { &rv, 0 };
276             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
277             return Value(scriptEngine, newQObject(rv, lastData->propType));
278         } else if (lastData->flags & QDeclarativePropertyCache::Data::IsQScriptValue) {
279             QScriptValue rv = scriptEngine->nullValue();
280             void *args[] = { &rv, 0 };
281             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
282             return Value(scriptEngine, rv);
283         } else if (lastData->propType == QMetaType::QReal) {
284             qreal rv = 0;
285             void *args[] = { &rv, 0 };
286             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
287             return Value(scriptEngine, rv);
288         } else if (lastData->propType == QMetaType::Int || lastData->flags & QDeclarativePropertyCache::Data::IsEnumType) {
289             int rv = 0;
290             void *args[] = { &rv, 0 };
291             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
292             return Value(scriptEngine, rv);
293         } else if (lastData->propType == QMetaType::Bool) {
294             bool rv = false;
295             void *args[] = { &rv, 0 };
296             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
297             return Value(scriptEngine, rv);
298         } else if (lastData->propType == QMetaType::QString) {
299             QString rv;
300             void *args[] = { &rv, 0 };
301             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
302             return Value(scriptEngine, rv);
303         } else if (lastData->propType == QMetaType::UInt) {
304             uint rv = 0;
305             void *args[] = { &rv, 0 };
306             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
307             return Value(scriptEngine, rv);
308         } else if (lastData->propType == QMetaType::Float) {
309             float rv = 0;
310             void *args[] = { &rv, 0 };
311             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
312             return Value(scriptEngine, rv);
313         } else if (lastData->propType == QMetaType::Double) {
314             double rv = 0;
315             void *args[] = { &rv, 0 };
316             QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
317             return Value(scriptEngine, rv);
318         } else {
319             QVariant var = obj->metaObject()->property(lastData->coreIndex).read(obj);
320             return Value(scriptEngine, enginePriv->scriptValueFromVariant(var));
321         }
322     }
323 }
324
325 void QDeclarativeObjectScriptClass::setProperty(Object *object,
326                                        const Identifier &name,
327                                        const QScriptValue &value)
328 {
329     return setProperty(toQObject(object), name, value, context());
330 }
331 namespace {
332 int qRoundDouble(double d)
333 {
334     return d >= double(0.0) ? int(d + double(0.5)) : int(d - int(d-1) + double(0.5)) + int(d-1);
335 }
336 }
337 void QDeclarativeObjectScriptClass::setProperty(QObject *obj,
338                                                 const Identifier &name,
339                                                 const QScriptValue &value,
340                                                 QScriptContext *context,
341                                                 QDeclarativeContextData *evalContext)
342 {
343     Q_UNUSED(name);
344
345     Q_ASSERT(obj);
346     Q_ASSERT(lastData);
347     Q_ASSERT(context);
348
349     if (!lastData->isValid()) {
350         QString error = QLatin1String("Cannot assign to non-existent property \"") +
351                         toString(name) + QLatin1Char('\"');
352         context->throwError(error);
353         return;
354     }
355
356     if (!(lastData->flags & QDeclarativePropertyCache::Data::IsWritable) && 
357         !(lastData->flags & QDeclarativePropertyCache::Data::IsQList)) {
358         QString error = QLatin1String("Cannot assign to read-only property \"") +
359                         toString(name) + QLatin1Char('\"');
360         context->throwError(error);
361         return;
362     }
363
364     QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine);
365
366     if (!evalContext) {
367         // Global object, QScriptContext activation object, QDeclarativeContext object
368         QScriptValue scopeNode = scopeChainValue(context, -3);
369         if (scopeNode.isValid()) {
370             Q_ASSERT(scriptClass(scopeNode) == enginePriv->contextClass);
371
372             evalContext = enginePriv->contextClass->contextFromValue(scopeNode);
373         }
374     }
375
376     QDeclarativeBinding *newBinding = 0;
377     if (value.isFunction() && !value.isRegExp()) {
378         QScriptContextInfo ctxtInfo(context);
379         QDeclarativePropertyCache::ValueTypeData valueTypeData;
380
381         newBinding = new QDeclarativeBinding(value, obj, evalContext);
382         newBinding->setSourceLocation(ctxtInfo.fileName(), ctxtInfo.functionStartLineNumber());
383         newBinding->setTarget(QDeclarativePropertyPrivate::restore(*lastData, valueTypeData, obj, evalContext));
384         if (newBinding->expression().contains(QLatin1String("this")))
385             newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject);
386     }
387
388     QDeclarativeAbstractBinding *delBinding =
389         QDeclarativePropertyPrivate::setBinding(obj, lastData->coreIndex, -1, newBinding);
390     if (delBinding)
391         delBinding->destroy();
392
393     if (value.isNull() && lastData->flags & QDeclarativePropertyCache::Data::IsQObjectDerived) {
394         QObject *o = 0;
395         int status = -1;
396         int flags = 0;
397         void *argv[] = { &o, 0, &status, &flags };
398         QMetaObject::metacall(obj, QMetaObject::WriteProperty, lastData->coreIndex, argv);
399     } else if (value.isUndefined() && lastData->flags & QDeclarativePropertyCache::Data::IsResettable) {
400         void *a[] = { 0 };
401         QMetaObject::metacall(obj, QMetaObject::ResetProperty, lastData->coreIndex, a);
402     } else if (value.isUndefined() && lastData->propType == qMetaTypeId<QVariant>()) {
403         QDeclarativePropertyPrivate::write(obj, *lastData, QVariant(), evalContext);
404     } else if (value.isUndefined()) {
405         QString error = QLatin1String("Cannot assign [undefined] to ") +
406                         QLatin1String(QMetaType::typeName(lastData->propType));
407         context->throwError(error);
408     } else if (value.isFunction() && !value.isRegExp()) {
409         // this is handled by the binding creation above
410     } else {
411         //### expand optimization for other known types
412         if (lastData->propType == QMetaType::Int && value.isNumber()) {
413             int rawValue = qRoundDouble(value.toNumber());
414             int status = -1;
415             int flags = 0;
416             void *a[] = { (void *)&rawValue, 0, &status, &flags };
417             QMetaObject::metacall(obj, QMetaObject::WriteProperty,
418                                   lastData->coreIndex, a);
419             return;
420         } else if (lastData->propType == QMetaType::QReal && value.isNumber()) {
421             qreal rawValue = qreal(value.toNumber());
422             int status = -1;
423             int flags = 0;
424             void *a[] = { (void *)&rawValue, 0, &status, &flags };
425             QMetaObject::metacall(obj, QMetaObject::WriteProperty,
426                                   lastData->coreIndex, a);
427             return;
428         } else if (lastData->propType == QMetaType::QString && value.isString()) {
429             const QString &rawValue = value.toString();
430             int status = -1;
431             int flags = 0;
432             void *a[] = { (void *)&rawValue, 0, &status, &flags };
433             QMetaObject::metacall(obj, QMetaObject::WriteProperty,
434                                   lastData->coreIndex, a);
435             return;
436         }
437
438         QVariant v;
439         if (lastData->flags & QDeclarativePropertyCache::Data::IsQList)
440             v = enginePriv->scriptValueToVariant(value, qMetaTypeId<QList<QObject *> >());
441         else
442             v = enginePriv->scriptValueToVariant(value, lastData->propType);
443
444         if (!QDeclarativePropertyPrivate::write(obj, *lastData, v, evalContext)) {
445             const char *valueType = 0;
446             if (v.userType() == QVariant::Invalid) valueType = "null";
447             else valueType = QMetaType::typeName(v.userType());
448
449             QString error = QLatin1String("Cannot assign ") +
450                             QLatin1String(valueType) +
451                             QLatin1String(" to ") +
452                             QLatin1String(QMetaType::typeName(lastData->propType));
453             context->throwError(error);
454         }
455     }
456 }
457
458 bool QDeclarativeObjectScriptClass::isQObject() const
459 {
460     return true;
461 }
462
463 QObject *QDeclarativeObjectScriptClass::toQObject(Object *object, bool *ok)
464 {
465     if (ok) *ok = true;
466
467     ObjectData *data = (ObjectData*)object;
468     return data->object.data();
469 }
470
471 QScriptValue QDeclarativeObjectScriptClass::tostring(QScriptContext *context, QScriptEngine *)
472 {
473     QObject* obj = context->thisObject().toQObject();
474
475     QString ret;
476     if(obj){
477         QString objectName = obj->objectName();
478
479         ret += QString::fromUtf8(obj->metaObject()->className());
480         ret += QLatin1String("(0x");
481         ret += QString::number((quintptr)obj,16);
482
483         if (!objectName.isEmpty()) {
484             ret += QLatin1String(", \"");
485             ret += objectName;
486             ret += QLatin1Char('\"');
487         }
488
489         ret += QLatin1Char(')');
490     }else{
491         ret += QLatin1String("null");
492     }
493     return QScriptValue(ret);
494 }
495
496 QScriptValue QDeclarativeObjectScriptClass::destroy(QScriptContext *context, QScriptEngine *engine)
497 {
498     QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
499     QScriptValue that = context->thisObject();
500
501     if (scriptClass(that) != p->objectClass)
502         return engine->undefinedValue();
503
504     ObjectData *data = (ObjectData *)p->objectClass->object(that);
505     if (!data->object)
506         return engine->undefinedValue();
507
508     QDeclarativeData *ddata = QDeclarativeData::get(data->object, false);
509     if (!ddata || ddata->indestructible)
510         return engine->currentContext()->throwError(QLatin1String("Invalid attempt to destroy() an indestructible object"));
511
512     QObject *obj = data->object;
513     int delay = 0;
514     if (context->argumentCount() > 0)
515         delay = context->argument(0).toInt32();
516     if (delay > 0)
517         QTimer::singleShot(delay, obj, SLOT(deleteLater()));
518     else
519         obj->deleteLater();
520
521     return engine->undefinedValue();
522 }
523
524 QStringList QDeclarativeObjectScriptClass::propertyNames(Object *object)
525 {
526     QObject *obj = toQObject(object);
527     if (!obj)
528         return QStringList();
529
530     QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine);
531
532     QDeclarativePropertyCache *cache = 0;
533     QDeclarativeData *ddata = QDeclarativeData::get(obj);
534     if (ddata)
535         cache = ddata->propertyCache;
536     if (!cache) {
537         cache = enginePrivate->cache(obj);
538         if (cache) {
539             if (ddata) { cache->addref(); ddata->propertyCache = cache; }
540         } else {
541             // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
542             // XXX QDeclarativeOpenMetaObject has a cache, so this is suboptimal.
543             // XXX This is a workaround for QTBUG-9420.
544             const QMetaObject *mo = obj->metaObject();
545             QStringList r;
546             int pc = mo->propertyCount();
547             int po = mo->propertyOffset();
548             for (int i=po; i<pc; ++i)
549                 r += QString::fromUtf8(mo->property(i).name());
550             return r;
551         }
552     }
553     return cache->propertyNames();
554 }
555
556 bool QDeclarativeObjectScriptClass::compare(Object *o1, Object *o2)
557 {
558     ObjectData *d1 = (ObjectData *)o1;
559     ObjectData *d2 = (ObjectData *)o2;
560
561     return d1 == d2 || d1->object == d2->object;
562 }
563
564 struct MethodData : public QScriptDeclarativeClass::Object {
565     MethodData(QObject *o, const QDeclarativePropertyCache::Data &d) : object(o), data(d) {}
566
567     QDeclarativeGuard<QObject> object;
568     QDeclarativePropertyCache::Data data;
569 };
570
571 QDeclarativeObjectMethodScriptClass::QDeclarativeObjectMethodScriptClass(QDeclarativeEngine *bindEngine)
572 : QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)),
573   engine(bindEngine)
574 {
575     qRegisterMetaType<QList<QObject *> >("QList<QObject *>");
576
577     setSupportsCall(true);
578
579     QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
580
581     m_connect = scriptEngine->newFunction(connect);
582     m_connectId = createPersistentIdentifier(QLatin1String("connect"));
583     m_disconnect = scriptEngine->newFunction(disconnect);
584     m_disconnectId = createPersistentIdentifier(QLatin1String("disconnect"));
585 }
586
587 QDeclarativeObjectMethodScriptClass::~QDeclarativeObjectMethodScriptClass()
588 {
589 }
590
591 QScriptValue QDeclarativeObjectMethodScriptClass::newMethod(QObject *object, const QDeclarativePropertyCache::Data *method)
592 {
593     QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
594
595     return newObject(scriptEngine, this, new MethodData(object, *method));
596 }
597
598 QScriptValue QDeclarativeObjectMethodScriptClass::connect(QScriptContext *context, QScriptEngine *engine)
599 {
600     QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
601
602     QScriptValue that = context->thisObject();
603     if (&p->objectClass->methods != scriptClass(that))
604         return engine->undefinedValue();
605
606     MethodData *data = (MethodData *)object(that);
607
608     if (!data->object || context->argumentCount() == 0)
609         return engine->undefinedValue();
610
611     QByteArray signal("2");
612     signal.append(data->object->metaObject()->method(data->data.coreIndex).signature());
613
614     if (context->argumentCount() == 1) {
615         qScriptConnect(data->object, signal.constData(), QScriptValue(), context->argument(0));
616     } else {
617         qScriptConnect(data->object, signal.constData(), context->argument(0), context->argument(1));
618     }
619
620     return engine->undefinedValue();
621 }
622
623 QScriptValue QDeclarativeObjectMethodScriptClass::disconnect(QScriptContext *context, QScriptEngine *engine)
624 {
625     QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
626
627     QScriptValue that = context->thisObject();
628     if (&p->objectClass->methods != scriptClass(that))
629         return engine->undefinedValue();
630
631     MethodData *data = (MethodData *)object(that);
632
633     if (!data->object || context->argumentCount() == 0)
634         return engine->undefinedValue();
635
636     QByteArray signal("2");
637     signal.append(data->object->metaObject()->method(data->data.coreIndex).signature());
638
639     if (context->argumentCount() == 1) {
640         qScriptDisconnect(data->object, signal.constData(), QScriptValue(), context->argument(0));
641     } else {
642         qScriptDisconnect(data->object, signal.constData(), context->argument(0), context->argument(1));
643     }
644
645     return engine->undefinedValue();
646 }
647
648 QScriptClass::QueryFlags
649 QDeclarativeObjectMethodScriptClass::queryProperty(Object *, const Identifier &name,
650                                                    QScriptClass::QueryFlags flags)
651 {
652     Q_UNUSED(flags);
653     if (name == m_connectId.identifier || name == m_disconnectId.identifier)
654         return QScriptClass::HandlesReadAccess;
655     else
656         return 0;
657
658 }
659
660 QDeclarativeObjectMethodScriptClass::Value
661 QDeclarativeObjectMethodScriptClass::property(Object *, const Identifier &name)
662 {
663     QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
664
665     if (name == m_connectId.identifier)
666         return Value(scriptEngine, m_connect);
667     else if (name == m_disconnectId.identifier)
668         return Value(scriptEngine, m_disconnect);
669     else
670         return Value();
671 }
672
673 namespace {
674 struct MetaCallArgument {
675     inline MetaCallArgument();
676     inline ~MetaCallArgument();
677     inline void *dataPtr();
678
679     inline void initAsType(int type, QDeclarativeEngine *);
680     void fromScriptValue(int type, QDeclarativeEngine *, const QScriptValue &);
681     inline QScriptDeclarativeClass::Value toValue(QDeclarativeEngine *);
682
683 private:
684     MetaCallArgument(const MetaCallArgument &);
685
686     inline void cleanup();
687
688     union {
689         float floatValue;
690         double doubleValue;
691         quint32 intValue;
692         bool boolValue;
693         QObject *qobjectPtr;
694
695         char allocData[sizeof(QVariant)];
696     };
697
698     // Pointers to allocData
699     union {
700         QString *qstringPtr;
701         QVariant *qvariantPtr;
702         QList<QObject *> *qlistPtr;
703         QScriptValue *qscriptValuePtr;
704     };
705
706     int type;
707 };
708 }
709
710 MetaCallArgument::MetaCallArgument()
711 : type(QVariant::Invalid)
712 {
713 }
714
715 MetaCallArgument::~MetaCallArgument()
716 {
717     cleanup();
718 }
719
720 void MetaCallArgument::cleanup()
721 {
722     if (type == QMetaType::QString) {
723         qstringPtr->~QString();
724     } else if (type == -1 || type == QMetaType::QVariant) {
725         qvariantPtr->~QVariant();
726     } else if (type == qMetaTypeId<QScriptValue>()) {
727         qscriptValuePtr->~QScriptValue();
728     } else if (type == qMetaTypeId<QList<QObject *> >()) {
729         qlistPtr->~QList<QObject *>();
730     }
731 }
732
733 void *MetaCallArgument::dataPtr()
734 {
735     if (type == -1)
736         return qvariantPtr->data();
737     else 
738         return (void *)&allocData;
739 }
740
741 void MetaCallArgument::initAsType(int callType, QDeclarativeEngine *e)
742 {
743     if (type != 0) { cleanup(); type = 0; }
744     if (callType == 0) return;
745
746     QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e);
747
748     if (callType == qMetaTypeId<QScriptValue>()) {
749         qscriptValuePtr = new (&allocData) QScriptValue(engine->undefinedValue());
750         type = callType;
751     } else if (callType == QMetaType::Int ||
752                callType == QMetaType::UInt ||
753                callType == QMetaType::Bool ||
754                callType == QMetaType::Double ||
755                callType == QMetaType::Float) {
756         type = callType;
757     } else if (callType == QMetaType::QObjectStar) {
758         qobjectPtr = 0;
759         type = callType;
760     } else if (callType == QMetaType::QString) {
761         qstringPtr = new (&allocData) QString();
762         type = callType;
763     } else if (callType == qMetaTypeId<QVariant>()) {
764         type = callType;
765         qvariantPtr = new (&allocData) QVariant();
766     } else if (callType == qMetaTypeId<QList<QObject *> >()) {
767         type = callType;
768         qlistPtr = new (&allocData) QList<QObject *>();
769     } else {
770         type = -1;
771         qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
772     }
773 }
774
775 void MetaCallArgument::fromScriptValue(int callType, QDeclarativeEngine *engine, const QScriptValue &value)
776 {
777     if (type != 0) { cleanup(); type = 0; }
778
779     if (callType == qMetaTypeId<QScriptValue>()) {
780         qscriptValuePtr = new (&allocData) QScriptValue(value);
781         type = qMetaTypeId<QScriptValue>();
782     } else if (callType == QMetaType::Int) {
783         intValue = quint32(value.toInt32());
784         type = callType;
785     } else if (callType == QMetaType::UInt) {
786         intValue = quint32(value.toUInt32());
787         type = callType;
788     } else if (callType == QMetaType::Bool) {
789         boolValue = value.toBool();
790         type = callType;
791     } else if (callType == QMetaType::Double) {
792         doubleValue = double(value.toNumber());
793         type = callType;
794     } else if (callType == QMetaType::Float) {
795         floatValue = float(value.toNumber());
796         type = callType;
797     } else if (callType == QMetaType::QString) {
798         if (value.isNull() || value.isUndefined())
799             qstringPtr = new (&allocData) QString();
800         else
801             qstringPtr = new (&allocData) QString(value.toString());
802         type = callType;
803     } else if (callType == QMetaType::QObjectStar) {
804         qobjectPtr = value.toQObject();
805         type = callType;
806     } else if (callType == qMetaTypeId<QVariant>()) {
807         QVariant other = QDeclarativeEnginePrivate::get(engine)->scriptValueToVariant(value);
808         qvariantPtr = new (&allocData) QVariant(other);
809         type = callType;
810     } else if (callType == qMetaTypeId<QList<QObject*> >()) {
811         qlistPtr = new (&allocData) QList<QObject *>(); 
812         if (value.isArray()) {
813             int length = value.property(QLatin1String("length")).toInt32();
814             for (int ii = 0; ii < length; ++ii) {
815                 QScriptValue arrayItem = value.property(ii);
816                 QObject *d = arrayItem.toQObject();
817                 qlistPtr->append(d);
818             }
819         } else if (QObject *d = value.toQObject()) {
820             qlistPtr->append(d);
821         }
822         type = callType;
823     } else {
824         qvariantPtr = new (&allocData) QVariant();
825         type = -1;
826
827         QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(engine);
828         QVariant v = priv->scriptValueToVariant(value);
829         if (v.userType() == callType) {
830             *qvariantPtr = v;
831         } else if (v.canConvert((QVariant::Type)callType)) {
832             *qvariantPtr = v;
833             qvariantPtr->convert((QVariant::Type)callType);
834         } else if (const QMetaObject *mo = priv->rawMetaObjectForType(callType)) {
835             QObject *obj = priv->toQObject(v);
836             
837             if (obj) {
838                 const QMetaObject *objMo = obj->metaObject();
839                 while (objMo && objMo != mo) objMo = objMo->superClass();
840                 if (!objMo) obj = 0;
841             }
842
843             *qvariantPtr = QVariant(callType, &obj);
844         } else {
845             *qvariantPtr = QVariant(callType, (void *)0);
846         }
847     }
848 }
849
850 QScriptDeclarativeClass::Value MetaCallArgument::toValue(QDeclarativeEngine *e)
851 {
852     QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e);
853
854     if (type == qMetaTypeId<QScriptValue>()) {
855         return QScriptDeclarativeClass::Value(engine, *qscriptValuePtr);
856     } else if (type == QMetaType::Int) {
857         return QScriptDeclarativeClass::Value(engine, int(intValue));
858     } else if (type == QMetaType::UInt) {
859         return QScriptDeclarativeClass::Value(engine, uint(intValue));
860     } else if (type == QMetaType::Bool) {
861         return QScriptDeclarativeClass::Value(engine, boolValue);
862     } else if (type == QMetaType::Double) {
863         return QScriptDeclarativeClass::Value(engine, doubleValue);
864     } else if (type == QMetaType::Float) {
865         return QScriptDeclarativeClass::Value(engine, floatValue);
866     } else if (type == QMetaType::QString) {
867         return QScriptDeclarativeClass::Value(engine, *qstringPtr);
868     } else if (type == QMetaType::QObjectStar) {
869         if (qobjectPtr)
870             QDeclarativeData::get(qobjectPtr, true)->setImplicitDestructible();
871         QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(e);
872         return QScriptDeclarativeClass::Value(engine, priv->objectClass->newQObject(qobjectPtr));
873     } else if (type == qMetaTypeId<QList<QObject *> >()) {
874         QList<QObject *> &list = *qlistPtr;
875         QScriptValue rv = engine->newArray(list.count());
876         QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(e);
877         for (int ii = 0; ii < list.count(); ++ii) {
878             QObject *object = list.at(ii);
879             QDeclarativeData::get(object, true)->setImplicitDestructible();
880             rv.setProperty(ii, priv->objectClass->newQObject(object));
881         }
882         return QScriptDeclarativeClass::Value(engine, rv);
883     } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
884         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(e);
885         QScriptValue rv = ep->scriptValueFromVariant(*qvariantPtr);
886         if (rv.isQObject()) {
887             QObject *object = rv.toQObject();
888             if (object)
889                 QDeclarativeData::get(object, true)->setImplicitDestructible();
890         }
891         return QScriptDeclarativeClass::Value(engine, rv);
892     } else {
893         return QScriptDeclarativeClass::Value();
894     }
895 }
896
897 int QDeclarativeObjectMethodScriptClass::enumType(const QMetaObject *meta, const QString &strname)
898 {
899     QByteArray str = strname.toUtf8();
900     QByteArray scope;
901     QByteArray name;
902     int scopeIdx = str.lastIndexOf("::");
903     if (scopeIdx != -1) {
904         scope = str.left(scopeIdx);
905         name = str.mid(scopeIdx + 2);
906     } else { 
907         name = str;
908     }
909     for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
910         QMetaEnum m = meta->enumerator(i);
911         if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
912             return QVariant::Int;
913     }
914     return QVariant::Invalid;
915 }
916
917 QDeclarativeObjectMethodScriptClass::Value QDeclarativeObjectMethodScriptClass::call(Object *o, QScriptContext *ctxt)
918 {
919     MethodData *method = static_cast<MethodData *>(o);
920
921     if (method->data.relatedIndex == -1)
922         return callPrecise(method->object, method->data, ctxt);
923     else
924         return callOverloaded(method, ctxt);
925 }
926
927 QDeclarativeObjectMethodScriptClass::Value 
928 QDeclarativeObjectMethodScriptClass::callPrecise(QObject *object, const QDeclarativePropertyCache::Data &data, 
929                                                  QScriptContext *ctxt)
930 {
931     if (data.flags & QDeclarativePropertyCache::Data::HasArguments) {
932
933         QMetaMethod m = object->metaObject()->method(data.coreIndex);
934         QList<QByteArray> argTypeNames = m.parameterTypes();
935         QVarLengthArray<int, 9> argTypes(argTypeNames.count());
936
937         // ### Cache
938         for (int ii = 0; ii < argTypeNames.count(); ++ii) {
939             argTypes[ii] = QMetaType::type(argTypeNames.at(ii));
940             if (argTypes[ii] == QVariant::Invalid) 
941                 argTypes[ii] = enumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii)));
942             if (argTypes[ii] == QVariant::Invalid) 
943                 return Value(ctxt, ctxt->throwError(QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii)))));
944         }
945
946         if (argTypes.count() > ctxt->argumentCount())
947             return Value(ctxt, ctxt->throwError(QLatin1String("Insufficient arguments")));
948
949         return callMethod(object, data.coreIndex, data.propType, argTypes.count(), argTypes.data(), ctxt);
950
951     } else {
952
953         return callMethod(object, data.coreIndex, data.propType, 0, 0, ctxt);
954
955     }
956 }
957
958 QDeclarativeObjectMethodScriptClass::Value 
959 QDeclarativeObjectMethodScriptClass::callMethod(QObject *object, int index, 
960                                                 int returnType, int argCount, int *argTypes, 
961                                                 QScriptContext *ctxt)
962 {
963     if (argCount > 0) {
964
965         QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
966         args[0].initAsType(returnType, engine);
967
968         for (int ii = 0; ii < argCount; ++ii)
969             args[ii + 1].fromScriptValue(argTypes[ii], engine, ctxt->argument(ii));
970
971         QVarLengthArray<void *, 9> argData(args.count());
972         for (int ii = 0; ii < args.count(); ++ii)
973             argData[ii] = args[ii].dataPtr();
974
975         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
976
977         return args[0].toValue(engine);
978
979     } else if (returnType != 0) {
980         
981         MetaCallArgument arg;
982         arg.initAsType(returnType, engine);
983
984         void *args[] = { arg.dataPtr() };
985
986         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
987
988         return arg.toValue(engine);
989
990     } else {
991
992         void *args[] = { 0 };
993         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
994         return Value();
995
996     }
997 }
998
999 /*!
1000 Resolve the overloaded method to call.  The algorithm works conceptually like this:
1001     1.  Resolve the set of overloads it is *possible* to call.
1002         Impossible overloads include those that have too many parameters or have parameters 
1003         of unknown type.  
1004     2.  Filter the set of overloads to only contain those with the closest number of 
1005         parameters.
1006         For example, if we are called with 3 parameters and there are 2 overloads that
1007         take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1008     3.  Find the best remaining overload based on its match score.  
1009         If two or more overloads have the same match score, call the last one.  The match
1010         score is constructed by adding the matchScore() result for each of the parameters.
1011 */
1012 QDeclarativeObjectMethodScriptClass::Value
1013 QDeclarativeObjectMethodScriptClass::callOverloaded(MethodData *method, QScriptContext *ctxt)
1014 {
1015     int argumentCount = ctxt->argumentCount();
1016
1017     QDeclarativePropertyCache::Data *best = 0;
1018     int bestParameterScore = INT_MAX;
1019     int bestMatchScore = INT_MAX;
1020
1021     QDeclarativePropertyCache::Data dummy;
1022     QDeclarativePropertyCache::Data *attempt = &method->data;
1023
1024     do {
1025         QList<QByteArray> methodArgTypeNames;
1026
1027         if (attempt->flags & QDeclarativePropertyCache::Data::HasArguments)
1028             methodArgTypeNames = method->object->metaObject()->method(attempt->coreIndex).parameterTypes();
1029
1030         int methodArgumentCount = methodArgTypeNames.count();
1031
1032         if (methodArgumentCount > argumentCount)
1033             continue; // We don't have sufficient arguments to call this method
1034
1035         int methodParameterScore = argumentCount - methodArgumentCount;
1036         if (methodParameterScore > bestParameterScore)
1037             continue; // We already have a better option
1038
1039         int methodMatchScore = 0;
1040         QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount);
1041
1042         bool unknownArgument = false;
1043         for (int ii = 0; ii < methodArgumentCount; ++ii) {
1044             methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii));
1045             if (methodArgTypes[ii] == QVariant::Invalid) 
1046                 methodArgTypes[ii] = enumType(method->object->metaObject(), 
1047                                               QString::fromLatin1(methodArgTypeNames.at(ii)));
1048             if (methodArgTypes[ii] == QVariant::Invalid) {
1049                 unknownArgument = true;
1050                 break;
1051             }
1052             methodMatchScore += matchScore(ctxt->argument(ii), methodArgTypes[ii], methodArgTypeNames.at(ii));
1053         }
1054         if (unknownArgument)
1055             continue; // We don't understand all the parameters
1056
1057         if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1058             best = attempt;
1059             bestParameterScore = methodParameterScore;
1060             bestMatchScore = methodMatchScore;
1061         }
1062
1063         if (bestParameterScore == 0 && bestMatchScore == 0)
1064             break; // We can't get better than that
1065
1066     } while((attempt = relatedMethod(method->object, attempt, dummy)) != 0);
1067
1068     if (best) {
1069         return callPrecise(method->object, *best, ctxt);
1070     } else {
1071         QString error = QLatin1String("Unable to determine callable overload.  Candidates are:");
1072         QDeclarativePropertyCache::Data *candidate = &method->data;
1073         while (candidate) {
1074             error += QLatin1String("\n    ") + QString::fromUtf8(method->object->metaObject()->method(candidate->coreIndex).signature());
1075             candidate = relatedMethod(method->object, candidate, dummy);
1076         }
1077         return Value(ctxt, ctxt->throwError(error));
1078     }
1079 }
1080
1081 /*!
1082     Returns the match score for converting \a actual to be of type \a conversionType.  A 
1083     zero score means "perfect match" whereas a higher score is worse.
1084
1085     The conversion table is copied out of the QtScript callQtMethod() function.
1086 */
1087 int QDeclarativeObjectMethodScriptClass::matchScore(const QScriptValue &actual, int conversionType, 
1088                                                     const QByteArray &conversionTypeName)
1089 {
1090     if (actual.isNumber()) {
1091         switch (conversionType) {
1092         case QMetaType::Double:
1093             return 0;
1094         case QMetaType::Float:
1095             return 1;
1096         case QMetaType::LongLong:
1097         case QMetaType::ULongLong:
1098             return 2;
1099         case QMetaType::Long:
1100         case QMetaType::ULong:
1101             return 3;
1102         case QMetaType::Int:
1103         case QMetaType::UInt:
1104             return 4;
1105         case QMetaType::Short:
1106         case QMetaType::UShort:
1107             return 5;
1108             break;
1109         case QMetaType::Char:
1110         case QMetaType::UChar:
1111             return 6;
1112         default:
1113             return 10;
1114         }
1115     } else if (actual.isString()) {
1116         switch (conversionType) {
1117         case QMetaType::QString:
1118             return 0;
1119         default:
1120             return 10;
1121         }
1122     } else if (actual.isBoolean()) {
1123         switch (conversionType) {
1124         case QMetaType::Bool:
1125             return 0;
1126         default:
1127             return 10;
1128         }
1129     } else if (actual.isDate()) {
1130         switch (conversionType) {
1131         case QMetaType::QDateTime:
1132             return 0;
1133         case QMetaType::QDate:
1134             return 1;
1135         case QMetaType::QTime:
1136             return 2;
1137         default:
1138             return 10;
1139         }
1140     } else if (actual.isRegExp()) {
1141         switch (conversionType) {
1142         case QMetaType::QRegExp:
1143             return 0;
1144         default:
1145             return 10;
1146         }
1147     } else if (actual.isVariant()) {
1148         if (conversionType == qMetaTypeId<QVariant>())
1149             return 0;
1150         else if (actual.toVariant().userType() == conversionType)
1151             return 0;
1152         else
1153             return 10;
1154     } else if (actual.isArray()) {
1155         switch (conversionType) {
1156         case QMetaType::QStringList:
1157         case QMetaType::QVariantList:
1158             return 5;
1159         default:
1160             return 10;
1161         }
1162     } else if (actual.isQObject()) {
1163         switch (conversionType) {
1164         case QMetaType::QObjectStar:
1165             return 0;
1166         default:
1167             return 10;
1168         }
1169     } else if (actual.isNull()) {
1170         switch (conversionType) {
1171         case QMetaType::VoidStar:
1172         case QMetaType::QObjectStar:
1173             return 0;
1174         default:
1175             if (!conversionTypeName.endsWith('*'))
1176                 return 10;
1177             else
1178                 return 0;
1179         }
1180     } else {
1181         return 10;
1182     }
1183 }
1184
1185 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1186 {
1187     struct Private
1188     {
1189         int revision;
1190         int className;
1191         int classInfoCount, classInfoData;
1192         int methodCount, methodData;
1193     };
1194
1195     return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1196 }
1197
1198 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1199 {
1200     QByteArray sig = m.signature();
1201     int paren = sig.indexOf('(');
1202     if (paren == -1)
1203         return sig;
1204     else
1205         return sig.left(paren);
1206 }
1207
1208 /*!
1209 Returns the next related method, if one, or 0.
1210 */
1211 QDeclarativePropertyCache::Data *
1212 QDeclarativeObjectMethodScriptClass::relatedMethod(QObject *object, QDeclarativePropertyCache::Data *current, 
1213                                                    QDeclarativePropertyCache::Data &dummy)
1214 {
1215     QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1216     if (current->relatedIndex == -1)
1217         return 0;
1218
1219     if (cache) {
1220         return cache->method(current->relatedIndex);
1221     } else {
1222         const QMetaObject *mo = object->metaObject();
1223         int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1224
1225         while (methodOffset > current->relatedIndex) {
1226             mo = mo->superClass();
1227             methodOffset -= QMetaObject_methods(mo);
1228         }
1229
1230         QMetaMethod method = mo->method(current->relatedIndex);
1231         dummy.load(method);
1232         
1233         // Look for overloaded methods
1234         QByteArray methodName = QMetaMethod_name(method);
1235         for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) {
1236             if (methodName == QMetaMethod_name(mo->method(ii))) {
1237                 dummy.relatedIndex = ii;
1238                 return &dummy;
1239             }
1240         }
1241
1242         return &dummy;
1243     }
1244 }
1245
1246 QT_END_NAMESPACE
1247