Initial import from the monolithic Qt.
[profile/ivi/qtdeclarative.git] / src / declarative / qml / qdeclarativeobjectscriptclass.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
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
332 void QDeclarativeObjectScriptClass::setProperty(QObject *obj,
333                                                 const Identifier &name,
334                                                 const QScriptValue &value,
335                                                 QScriptContext *context,
336                                                 QDeclarativeContextData *evalContext)
337 {
338     Q_UNUSED(name);
339
340     Q_ASSERT(obj);
341     Q_ASSERT(lastData);
342     Q_ASSERT(context);
343
344     if (!lastData->isValid()) {
345         QString error = QLatin1String("Cannot assign to non-existent property \"") +
346                         toString(name) + QLatin1Char('\"');
347         context->throwError(error);
348         return;
349     }
350
351     if (!(lastData->flags & QDeclarativePropertyCache::Data::IsWritable) && 
352         !(lastData->flags & QDeclarativePropertyCache::Data::IsQList)) {
353         QString error = QLatin1String("Cannot assign to read-only property \"") +
354                         toString(name) + QLatin1Char('\"');
355         context->throwError(error);
356         return;
357     }
358
359     QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine);
360
361     if (!evalContext) {
362         // Global object, QScriptContext activation object, QDeclarativeContext object
363         QScriptValue scopeNode = scopeChainValue(context, -3);
364         if (scopeNode.isValid()) {
365             Q_ASSERT(scriptClass(scopeNode) == enginePriv->contextClass);
366
367             evalContext = enginePriv->contextClass->contextFromValue(scopeNode);
368         }
369     }
370
371     QDeclarativeBinding *newBinding = 0;
372     if (value.isFunction() && !value.isRegExp()) {
373         QScriptContextInfo ctxtInfo(context);
374         QDeclarativePropertyCache::ValueTypeData valueTypeData;
375
376         newBinding = new QDeclarativeBinding(value, obj, evalContext);
377         newBinding->setSourceLocation(ctxtInfo.fileName(), ctxtInfo.functionStartLineNumber());
378         newBinding->setTarget(QDeclarativePropertyPrivate::restore(*lastData, valueTypeData, obj, evalContext));
379         if (newBinding->expression().contains(QLatin1String("this")))
380             newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject);
381     }
382
383     QDeclarativeAbstractBinding *delBinding =
384         QDeclarativePropertyPrivate::setBinding(obj, lastData->coreIndex, -1, newBinding);
385     if (delBinding)
386         delBinding->destroy();
387
388     if (value.isNull() && lastData->flags & QDeclarativePropertyCache::Data::IsQObjectDerived) {
389         QObject *o = 0;
390         int status = -1;
391         int flags = 0;
392         void *argv[] = { &o, 0, &status, &flags };
393         QMetaObject::metacall(obj, QMetaObject::WriteProperty, lastData->coreIndex, argv);
394     } else if (value.isUndefined() && lastData->flags & QDeclarativePropertyCache::Data::IsResettable) {
395         void *a[] = { 0 };
396         QMetaObject::metacall(obj, QMetaObject::ResetProperty, lastData->coreIndex, a);
397     } else if (value.isUndefined() && lastData->propType == qMetaTypeId<QVariant>()) {
398         QDeclarativePropertyPrivate::write(obj, *lastData, QVariant(), evalContext);
399     } else if (value.isUndefined()) {
400         QString error = QLatin1String("Cannot assign [undefined] to ") +
401                         QLatin1String(QMetaType::typeName(lastData->propType));
402         context->throwError(error);
403     } else if (value.isFunction() && !value.isRegExp()) {
404         // this is handled by the binding creation above
405     } else {
406         QVariant v;
407         if (lastData->flags & QDeclarativePropertyCache::Data::IsQList)
408             v = enginePriv->scriptValueToVariant(value, qMetaTypeId<QList<QObject *> >());
409         else
410             v = enginePriv->scriptValueToVariant(value, lastData->propType);
411
412         if (!QDeclarativePropertyPrivate::write(obj, *lastData, v, evalContext)) {
413             const char *valueType = 0;
414             if (v.userType() == QVariant::Invalid) valueType = "null";
415             else valueType = QMetaType::typeName(v.userType());
416
417             QString error = QLatin1String("Cannot assign ") +
418                             QLatin1String(valueType) +
419                             QLatin1String(" to ") +
420                             QLatin1String(QMetaType::typeName(lastData->propType));
421             context->throwError(error);
422         }
423     }
424 }
425
426 bool QDeclarativeObjectScriptClass::isQObject() const
427 {
428     return true;
429 }
430
431 QObject *QDeclarativeObjectScriptClass::toQObject(Object *object, bool *ok)
432 {
433     if (ok) *ok = true;
434
435     ObjectData *data = (ObjectData*)object;
436     return data->object.data();
437 }
438
439 QScriptValue QDeclarativeObjectScriptClass::tostring(QScriptContext *context, QScriptEngine *)
440 {
441     QObject* obj = context->thisObject().toQObject();
442
443     QString ret;
444     if(obj){
445         QString objectName = obj->objectName();
446
447         ret += QString::fromUtf8(obj->metaObject()->className());
448         ret += QLatin1String("(0x");
449         ret += QString::number((quintptr)obj,16);
450
451         if (!objectName.isEmpty()) {
452             ret += QLatin1String(", \"");
453             ret += objectName;
454             ret += QLatin1Char('\"');
455         }
456
457         ret += QLatin1Char(')');
458     }else{
459         ret += QLatin1String("null");
460     }
461     return QScriptValue(ret);
462 }
463
464 QScriptValue QDeclarativeObjectScriptClass::destroy(QScriptContext *context, QScriptEngine *engine)
465 {
466     QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
467     QScriptValue that = context->thisObject();
468
469     if (scriptClass(that) != p->objectClass)
470         return engine->undefinedValue();
471
472     ObjectData *data = (ObjectData *)p->objectClass->object(that);
473     if (!data->object)
474         return engine->undefinedValue();
475
476     QDeclarativeData *ddata = QDeclarativeData::get(data->object, false);
477     if (!ddata || ddata->indestructible)
478         return engine->currentContext()->throwError(QLatin1String("Invalid attempt to destroy() an indestructible object"));
479
480     QObject *obj = data->object;
481     int delay = 0;
482     if (context->argumentCount() > 0)
483         delay = context->argument(0).toInt32();
484     if (delay > 0)
485         QTimer::singleShot(delay, obj, SLOT(deleteLater()));
486     else
487         obj->deleteLater();
488
489     return engine->undefinedValue();
490 }
491
492 QStringList QDeclarativeObjectScriptClass::propertyNames(Object *object)
493 {
494     QObject *obj = toQObject(object);
495     if (!obj)
496         return QStringList();
497
498     QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine);
499
500     QDeclarativePropertyCache *cache = 0;
501     QDeclarativeData *ddata = QDeclarativeData::get(obj);
502     if (ddata)
503         cache = ddata->propertyCache;
504     if (!cache) {
505         cache = enginePrivate->cache(obj);
506         if (cache) {
507             if (ddata) { cache->addref(); ddata->propertyCache = cache; }
508         } else {
509             // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
510             // XXX QDeclarativeOpenMetaObject has a cache, so this is suboptimal.
511             // XXX This is a workaround for QTBUG-9420.
512             const QMetaObject *mo = obj->metaObject();
513             QStringList r;
514             int pc = mo->propertyCount();
515             int po = mo->propertyOffset();
516             for (int i=po; i<pc; ++i)
517                 r += QString::fromUtf8(mo->property(i).name());
518             return r;
519         }
520     }
521     return cache->propertyNames();
522 }
523
524 bool QDeclarativeObjectScriptClass::compare(Object *o1, Object *o2)
525 {
526     ObjectData *d1 = (ObjectData *)o1;
527     ObjectData *d2 = (ObjectData *)o2;
528
529     return d1 == d2 || d1->object == d2->object;
530 }
531
532 struct MethodData : public QScriptDeclarativeClass::Object {
533     MethodData(QObject *o, const QDeclarativePropertyCache::Data &d) : object(o), data(d) {}
534
535     QDeclarativeGuard<QObject> object;
536     QDeclarativePropertyCache::Data data;
537 };
538
539 QDeclarativeObjectMethodScriptClass::QDeclarativeObjectMethodScriptClass(QDeclarativeEngine *bindEngine)
540 : QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)),
541   engine(bindEngine)
542 {
543     qRegisterMetaType<QList<QObject *> >("QList<QObject *>");
544
545     setSupportsCall(true);
546
547     QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
548
549     m_connect = scriptEngine->newFunction(connect);
550     m_connectId = createPersistentIdentifier(QLatin1String("connect"));
551     m_disconnect = scriptEngine->newFunction(disconnect);
552     m_disconnectId = createPersistentIdentifier(QLatin1String("disconnect"));
553 }
554
555 QDeclarativeObjectMethodScriptClass::~QDeclarativeObjectMethodScriptClass()
556 {
557 }
558
559 QScriptValue QDeclarativeObjectMethodScriptClass::newMethod(QObject *object, const QDeclarativePropertyCache::Data *method)
560 {
561     QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
562
563     return newObject(scriptEngine, this, new MethodData(object, *method));
564 }
565
566 QScriptValue QDeclarativeObjectMethodScriptClass::connect(QScriptContext *context, QScriptEngine *engine)
567 {
568     QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
569
570     QScriptValue that = context->thisObject();
571     if (&p->objectClass->methods != scriptClass(that))
572         return engine->undefinedValue();
573
574     MethodData *data = (MethodData *)object(that);
575
576     if (!data->object || context->argumentCount() == 0)
577         return engine->undefinedValue();
578
579     QByteArray signal("2");
580     signal.append(data->object->metaObject()->method(data->data.coreIndex).signature());
581
582     if (context->argumentCount() == 1) {
583         qScriptConnect(data->object, signal.constData(), QScriptValue(), context->argument(0));
584     } else {
585         qScriptConnect(data->object, signal.constData(), context->argument(0), context->argument(1));
586     }
587
588     return engine->undefinedValue();
589 }
590
591 QScriptValue QDeclarativeObjectMethodScriptClass::disconnect(QScriptContext *context, QScriptEngine *engine)
592 {
593     QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
594
595     QScriptValue that = context->thisObject();
596     if (&p->objectClass->methods != scriptClass(that))
597         return engine->undefinedValue();
598
599     MethodData *data = (MethodData *)object(that);
600
601     if (!data->object || context->argumentCount() == 0)
602         return engine->undefinedValue();
603
604     QByteArray signal("2");
605     signal.append(data->object->metaObject()->method(data->data.coreIndex).signature());
606
607     if (context->argumentCount() == 1) {
608         qScriptDisconnect(data->object, signal.constData(), QScriptValue(), context->argument(0));
609     } else {
610         qScriptDisconnect(data->object, signal.constData(), context->argument(0), context->argument(1));
611     }
612
613     return engine->undefinedValue();
614 }
615
616 QScriptClass::QueryFlags
617 QDeclarativeObjectMethodScriptClass::queryProperty(Object *, const Identifier &name,
618                                                    QScriptClass::QueryFlags flags)
619 {
620     Q_UNUSED(flags);
621     if (name == m_connectId.identifier || name == m_disconnectId.identifier)
622         return QScriptClass::HandlesReadAccess;
623     else
624         return 0;
625
626 }
627
628 QDeclarativeObjectMethodScriptClass::Value
629 QDeclarativeObjectMethodScriptClass::property(Object *, const Identifier &name)
630 {
631     QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
632
633     if (name == m_connectId.identifier)
634         return Value(scriptEngine, m_connect);
635     else if (name == m_disconnectId.identifier)
636         return Value(scriptEngine, m_disconnect);
637     else
638         return Value();
639 }
640
641 namespace {
642 struct MetaCallArgument {
643     inline MetaCallArgument();
644     inline ~MetaCallArgument();
645     inline void *dataPtr();
646
647     inline void initAsType(int type, QDeclarativeEngine *);
648     void fromScriptValue(int type, QDeclarativeEngine *, const QScriptValue &);
649     inline QScriptDeclarativeClass::Value toValue(QDeclarativeEngine *);
650
651 private:
652     MetaCallArgument(const MetaCallArgument &);
653
654     inline void cleanup();
655
656     char data[4 * sizeof(void *)];
657     int type;
658     bool isObjectType;
659 };
660 }
661
662 MetaCallArgument::MetaCallArgument()
663 : type(QVariant::Invalid), isObjectType(false)
664 {
665 }
666
667 MetaCallArgument::~MetaCallArgument()
668 {
669     cleanup();
670 }
671
672 void MetaCallArgument::cleanup()
673 {
674     if (type == QMetaType::QString) {
675         ((QString *)&data)->~QString();
676     } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
677         ((QVariant *)&data)->~QVariant();
678     } else if (type == qMetaTypeId<QScriptValue>()) {
679         ((QScriptValue *)&data)->~QScriptValue();
680     } else if (type == qMetaTypeId<QList<QObject *> >()) {
681         ((QList<QObject *> *)&data)->~QList<QObject *>();
682     }
683 }
684
685 void *MetaCallArgument::dataPtr()
686 {
687     if (type == -1)
688         return ((QVariant *)data)->data();
689     else
690         return (void *)&data;
691 }
692
693 void MetaCallArgument::initAsType(int callType, QDeclarativeEngine *e)
694 {
695     if (type != 0) { cleanup(); type = 0; }
696     if (callType == 0) return;
697
698     QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e);
699
700     if (callType == qMetaTypeId<QScriptValue>()) {
701         new (&data) QScriptValue(engine->undefinedValue());
702         type = callType;
703     } else if (callType == QMetaType::Int ||
704                callType == QMetaType::UInt ||
705                callType == QMetaType::Bool ||
706                callType == QMetaType::Double ||
707                callType == QMetaType::Float) {
708         type = callType;
709     } else if (callType == QMetaType::QObjectStar) {
710         *((QObject **)&data) = 0;
711         type = callType;
712     } else if (callType == QMetaType::QString) {
713         new (&data) QString();
714         type = callType;
715     } else if (callType == qMetaTypeId<QVariant>()) {
716         type = callType;
717         new (&data) QVariant();
718     } else if (callType == qMetaTypeId<QList<QObject *> >()) {
719         type = callType;
720         new (&data) QList<QObject *>();
721     } else {
722         type = -1;
723         new (&data) QVariant(callType, (void *)0);
724     }
725 }
726
727 void MetaCallArgument::fromScriptValue(int callType, QDeclarativeEngine *engine, const QScriptValue &value)
728 {
729     if (type != 0) { cleanup(); type = 0; }
730
731     if (callType == qMetaTypeId<QScriptValue>()) {
732         new (&data) QScriptValue(value);
733         type = qMetaTypeId<QScriptValue>();
734     } else if (callType == QMetaType::Int) {
735         *((int *)&data) = int(value.toInt32());
736         type = callType;
737     } else if (callType == QMetaType::UInt) {
738         *((uint *)&data) = uint(value.toUInt32());
739         type = callType;
740     } else if (callType == QMetaType::Bool) {
741         *((bool *)&data) = value.toBool();
742         type = callType;
743     } else if (callType == QMetaType::Double) {
744         *((double *)&data) = double(value.toNumber());
745         type = callType;
746     } else if (callType == QMetaType::Float) {
747         *((float *)&data) = float(value.toNumber());
748         type = callType;
749     } else if (callType == QMetaType::QString) {
750         if (value.isNull() || value.isUndefined())
751             new (&data) QString();
752         else
753             new (&data) QString(value.toString());
754         type = callType;
755     } else if (callType == QMetaType::QObjectStar) {
756         *((QObject **)&data) = value.toQObject();
757         type = callType;
758     } else if (callType == qMetaTypeId<QVariant>()) {
759         new (&data) QVariant(QDeclarativeEnginePrivate::get(engine)->scriptValueToVariant(value));
760         type = callType;
761     } else if (callType == qMetaTypeId<QList<QObject*> >()) {
762         QList<QObject *> *list = new (&data) QList<QObject *>(); 
763         if (value.isArray()) {
764             int length = value.property(QLatin1String("length")).toInt32();
765             for (int ii = 0; ii < length; ++ii) {
766                 QScriptValue arrayItem = value.property(ii);
767                 QObject *d = arrayItem.toQObject();
768                 list->append(d);
769             }
770         } else if (QObject *d = value.toQObject()) {
771             list->append(d);
772         }
773         type = callType;
774     } else {
775         new (&data) QVariant();
776         type = -1;
777
778         QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(engine);
779         QVariant v = priv->scriptValueToVariant(value);
780         if (v.userType() == callType) {
781             *((QVariant *)&data) = v;
782         } else if (v.canConvert((QVariant::Type)callType)) {
783             *((QVariant *)&data) = v;
784             ((QVariant *)&data)->convert((QVariant::Type)callType);
785         } else if (const QMetaObject *mo = priv->rawMetaObjectForType(callType)) {
786             QObject *obj = priv->toQObject(v);
787             
788             if (obj) {
789                 const QMetaObject *objMo = obj->metaObject();
790                 while (objMo && objMo != mo) objMo = objMo->superClass();
791                 if (!objMo) obj = 0;
792             }
793
794             *((QVariant *)&data) = QVariant(callType, &obj);
795         } else {
796             *((QVariant *)&data) = QVariant(callType, (void *)0);
797         }
798     }
799 }
800
801 QScriptDeclarativeClass::Value MetaCallArgument::toValue(QDeclarativeEngine *e)
802 {
803     QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e);
804
805     if (type == qMetaTypeId<QScriptValue>()) {
806         return QScriptDeclarativeClass::Value(engine, *((QScriptValue *)&data));
807     } else if (type == QMetaType::Int) {
808         return QScriptDeclarativeClass::Value(engine, *((int *)&data));
809     } else if (type == QMetaType::UInt) {
810         return QScriptDeclarativeClass::Value(engine, *((uint *)&data));
811     } else if (type == QMetaType::Bool) {
812         return QScriptDeclarativeClass::Value(engine, *((bool *)&data));
813     } else if (type == QMetaType::Double) {
814         return QScriptDeclarativeClass::Value(engine, *((double *)&data));
815     } else if (type == QMetaType::Float) {
816         return QScriptDeclarativeClass::Value(engine, *((float *)&data));
817     } else if (type == QMetaType::QString) {
818         return QScriptDeclarativeClass::Value(engine, *((QString *)&data));
819     } else if (type == QMetaType::QObjectStar) {
820         QObject *object = *((QObject **)&data);
821         if (object)
822             QDeclarativeData::get(object, true)->setImplicitDestructible();
823         QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(e);
824         return QScriptDeclarativeClass::Value(engine, priv->objectClass->newQObject(object));
825     } else if (type == qMetaTypeId<QList<QObject *> >()) {
826         QList<QObject *> &list = *(QList<QObject *>*)&data;
827         QScriptValue rv = engine->newArray(list.count());
828         QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(e);
829         for (int ii = 0; ii < list.count(); ++ii) {
830             QObject *object = list.at(ii);
831             QDeclarativeData::get(object, true)->setImplicitDestructible();
832             rv.setProperty(ii, priv->objectClass->newQObject(object));
833         }
834         return QScriptDeclarativeClass::Value(engine, rv);
835     } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
836         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(e);
837         QScriptValue rv = ep->scriptValueFromVariant(*((QVariant *)&data));
838         if (rv.isQObject()) {
839             QObject *object = rv.toQObject();
840             if (object)
841                 QDeclarativeData::get(object, true)->setImplicitDestructible();
842         }
843         return QScriptDeclarativeClass::Value(engine, rv);
844     } else {
845         return QScriptDeclarativeClass::Value();
846     }
847 }
848
849 int QDeclarativeObjectMethodScriptClass::enumType(const QMetaObject *meta, const QString &strname)
850 {
851     QByteArray str = strname.toUtf8();
852     QByteArray scope;
853     QByteArray name;
854     int scopeIdx = str.lastIndexOf("::");
855     if (scopeIdx != -1) {
856         scope = str.left(scopeIdx);
857         name = str.mid(scopeIdx + 2);
858     } else { 
859         name = str;
860     }
861     for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
862         QMetaEnum m = meta->enumerator(i);
863         if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
864             return QVariant::Int;
865     }
866     return QVariant::Invalid;
867 }
868
869 QDeclarativeObjectMethodScriptClass::Value QDeclarativeObjectMethodScriptClass::call(Object *o, QScriptContext *ctxt)
870 {
871     MethodData *method = static_cast<MethodData *>(o);
872
873     if (method->data.relatedIndex == -1)
874         return callPrecise(method->object, method->data, ctxt);
875     else
876         return callOverloaded(method, ctxt);
877 }
878
879 QDeclarativeObjectMethodScriptClass::Value 
880 QDeclarativeObjectMethodScriptClass::callPrecise(QObject *object, const QDeclarativePropertyCache::Data &data, 
881                                                  QScriptContext *ctxt)
882 {
883     if (data.flags & QDeclarativePropertyCache::Data::HasArguments) {
884
885         QMetaMethod m = object->metaObject()->method(data.coreIndex);
886         QList<QByteArray> argTypeNames = m.parameterTypes();
887         QVarLengthArray<int, 9> argTypes(argTypeNames.count());
888
889         // ### Cache
890         for (int ii = 0; ii < argTypeNames.count(); ++ii) {
891             argTypes[ii] = QMetaType::type(argTypeNames.at(ii));
892             if (argTypes[ii] == QVariant::Invalid) 
893                 argTypes[ii] = enumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii)));
894             if (argTypes[ii] == QVariant::Invalid) 
895                 return Value(ctxt, ctxt->throwError(QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii)))));
896         }
897
898         if (argTypes.count() > ctxt->argumentCount())
899             return Value(ctxt, ctxt->throwError(QLatin1String("Insufficient arguments")));
900
901         return callMethod(object, data.coreIndex, data.propType, argTypes.count(), argTypes.data(), ctxt);
902
903     } else {
904
905         return callMethod(object, data.coreIndex, data.propType, 0, 0, ctxt);
906
907     }
908 }
909
910 QDeclarativeObjectMethodScriptClass::Value 
911 QDeclarativeObjectMethodScriptClass::callMethod(QObject *object, int index, 
912                                                 int returnType, int argCount, int *argTypes, 
913                                                 QScriptContext *ctxt)
914 {
915     if (argCount > 0) {
916
917         QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
918         args[0].initAsType(returnType, engine);
919
920         for (int ii = 0; ii < argCount; ++ii)
921             args[ii + 1].fromScriptValue(argTypes[ii], engine, ctxt->argument(ii));
922
923         QVarLengthArray<void *, 9> argData(args.count());
924         for (int ii = 0; ii < args.count(); ++ii)
925             argData[ii] = args[ii].dataPtr();
926
927         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
928
929         return args[0].toValue(engine);
930
931     } else if (returnType != 0) {
932         
933         MetaCallArgument arg;
934         arg.initAsType(returnType, engine);
935
936         void *args[] = { arg.dataPtr() };
937
938         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
939
940         return arg.toValue(engine);
941
942     } else {
943
944         void *args[] = { 0 };
945         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
946         return Value();
947
948     }
949 }
950
951 /*!
952 Resolve the overloaded method to call.  The algorithm works conceptually like this:
953     1.  Resolve the set of overloads it is *possible* to call.
954         Impossible overloads include those that have too many parameters or have parameters 
955         of unknown type.  
956     2.  Filter the set of overloads to only contain those with the closest number of 
957         parameters.
958         For example, if we are called with 3 parameters and there are 2 overloads that
959         take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
960     3.  Find the best remaining overload based on its match score.  
961         If two or more overloads have the same match score, call the last one.  The match
962         score is constructed by adding the matchScore() result for each of the parameters.
963 */
964 QDeclarativeObjectMethodScriptClass::Value
965 QDeclarativeObjectMethodScriptClass::callOverloaded(MethodData *method, QScriptContext *ctxt)
966 {
967     int argumentCount = ctxt->argumentCount();
968
969     QDeclarativePropertyCache::Data *best = 0;
970     int bestParameterScore = INT_MAX;
971     int bestMatchScore = INT_MAX;
972
973     QDeclarativePropertyCache::Data dummy;
974     QDeclarativePropertyCache::Data *attempt = &method->data;
975
976     do {
977         QList<QByteArray> methodArgTypeNames;
978
979         if (attempt->flags & QDeclarativePropertyCache::Data::HasArguments)
980             methodArgTypeNames = method->object->metaObject()->method(attempt->coreIndex).parameterTypes();
981
982         int methodArgumentCount = methodArgTypeNames.count();
983
984         if (methodArgumentCount > argumentCount)
985             continue; // We don't have sufficient arguments to call this method
986
987         int methodParameterScore = argumentCount - methodArgumentCount;
988         if (methodParameterScore > bestParameterScore)
989             continue; // We already have a better option
990
991         int methodMatchScore = 0;
992         QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount);
993
994         bool unknownArgument = false;
995         for (int ii = 0; ii < methodArgumentCount; ++ii) {
996             methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii));
997             if (methodArgTypes[ii] == QVariant::Invalid) 
998                 methodArgTypes[ii] = enumType(method->object->metaObject(), 
999                                               QString::fromLatin1(methodArgTypeNames.at(ii)));
1000             if (methodArgTypes[ii] == QVariant::Invalid) {
1001                 unknownArgument = true;
1002                 break;
1003             }
1004             methodMatchScore += matchScore(ctxt->argument(ii), methodArgTypes[ii], methodArgTypeNames.at(ii));
1005         }
1006         if (unknownArgument)
1007             continue; // We don't understand all the parameters
1008
1009         if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1010             best = attempt;
1011             bestParameterScore = methodParameterScore;
1012             bestMatchScore = methodMatchScore;
1013         }
1014
1015         if (bestParameterScore == 0 && bestMatchScore == 0)
1016             break; // We can't get better than that
1017
1018     } while((attempt = relatedMethod(method->object, attempt, dummy)) != 0);
1019
1020     if (best) {
1021         return callPrecise(method->object, *best, ctxt);
1022     } else {
1023         QString error = QLatin1String("Unable to determine callable overload.  Candidates are:");
1024         QDeclarativePropertyCache::Data *candidate = &method->data;
1025         while (candidate) {
1026             error += QLatin1String("\n    ") + QString::fromUtf8(method->object->metaObject()->method(candidate->coreIndex).signature());
1027             candidate = relatedMethod(method->object, candidate, dummy);
1028         }
1029         return Value(ctxt, ctxt->throwError(error));
1030     }
1031 }
1032
1033 /*!
1034     Returns the match score for converting \a actual to be of type \a conversionType.  A 
1035     zero score means "perfect match" whereas a higher score is worse.
1036
1037     The conversion table is copied out of the QtScript callQtMethod() function.
1038 */
1039 int QDeclarativeObjectMethodScriptClass::matchScore(const QScriptValue &actual, int conversionType, 
1040                                                     const QByteArray &conversionTypeName)
1041 {
1042     if (actual.isNumber()) {
1043         switch (conversionType) {
1044         case QMetaType::Double:
1045             return 0;
1046         case QMetaType::Float:
1047             return 1;
1048         case QMetaType::LongLong:
1049         case QMetaType::ULongLong:
1050             return 2;
1051         case QMetaType::Long:
1052         case QMetaType::ULong:
1053             return 3;
1054         case QMetaType::Int:
1055         case QMetaType::UInt:
1056             return 4;
1057         case QMetaType::Short:
1058         case QMetaType::UShort:
1059             return 5;
1060             break;
1061         case QMetaType::Char:
1062         case QMetaType::UChar:
1063             return 6;
1064         default:
1065             return 10;
1066         }
1067     } else if (actual.isString()) {
1068         switch (conversionType) {
1069         case QMetaType::QString:
1070             return 0;
1071         default:
1072             return 10;
1073         }
1074     } else if (actual.isBoolean()) {
1075         switch (conversionType) {
1076         case QMetaType::Bool:
1077             return 0;
1078         default:
1079             return 10;
1080         }
1081     } else if (actual.isDate()) {
1082         switch (conversionType) {
1083         case QMetaType::QDateTime:
1084             return 0;
1085         case QMetaType::QDate:
1086             return 1;
1087         case QMetaType::QTime:
1088             return 2;
1089         default:
1090             return 10;
1091         }
1092     } else if (actual.isRegExp()) {
1093         switch (conversionType) {
1094         case QMetaType::QRegExp:
1095             return 0;
1096         default:
1097             return 10;
1098         }
1099     } else if (actual.isVariant()) {
1100         if (conversionType == qMetaTypeId<QVariant>())
1101             return 0;
1102         else if (actual.toVariant().userType() == conversionType)
1103             return 0;
1104         else
1105             return 10;
1106     } else if (actual.isArray()) {
1107         switch (conversionType) {
1108         case QMetaType::QStringList:
1109         case QMetaType::QVariantList:
1110             return 5;
1111         default:
1112             return 10;
1113         }
1114     } else if (actual.isQObject()) {
1115         switch (conversionType) {
1116         case QMetaType::QObjectStar:
1117             return 0;
1118         default:
1119             return 10;
1120         }
1121     } else if (actual.isNull()) {
1122         switch (conversionType) {
1123         case QMetaType::VoidStar:
1124         case QMetaType::QObjectStar:
1125             return 0;
1126         default:
1127             if (!conversionTypeName.endsWith('*'))
1128                 return 10;
1129             else
1130                 return 0;
1131         }
1132     } else {
1133         return 10;
1134     }
1135 }
1136
1137 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1138 {
1139     struct Private
1140     {
1141         int revision;
1142         int className;
1143         int classInfoCount, classInfoData;
1144         int methodCount, methodData;
1145     };
1146
1147     return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1148 }
1149
1150 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1151 {
1152     QByteArray sig = m.signature();
1153     int paren = sig.indexOf('(');
1154     if (paren == -1)
1155         return sig;
1156     else
1157         return sig.left(paren);
1158 }
1159
1160 /*!
1161 Returns the next related method, if one, or 0.
1162 */
1163 QDeclarativePropertyCache::Data *
1164 QDeclarativeObjectMethodScriptClass::relatedMethod(QObject *object, QDeclarativePropertyCache::Data *current, 
1165                                                    QDeclarativePropertyCache::Data &dummy)
1166 {
1167     QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1168     if (current->relatedIndex == -1)
1169         return 0;
1170
1171     if (cache) {
1172         return cache->method(current->relatedIndex);
1173     } else {
1174         const QMetaObject *mo = object->metaObject();
1175         int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1176
1177         while (methodOffset > current->relatedIndex) {
1178             mo = mo->superClass();
1179             methodOffset -= QMetaObject_methods(mo);
1180         }
1181
1182         QMetaMethod method = mo->method(current->relatedIndex);
1183         dummy.load(method);
1184         
1185         // Look for overloaded methods
1186         QByteArray methodName = QMetaMethod_name(method);
1187         for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) {
1188             if (methodName == QMetaMethod_name(mo->method(ii))) {
1189                 dummy.relatedIndex = ii;
1190                 return &dummy;
1191             }
1192         }
1193
1194         return &dummy;
1195     }
1196 }
1197
1198 QT_END_NAMESPACE
1199