1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
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
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "private/qdeclarativeobjectscriptclass_p.h"
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"
53 #include <QtCore/qtimer.h>
54 #include <QtCore/qvarlengtharray.h>
55 #include <QtScript/qscriptcontextinfo.h>
57 Q_DECLARE_METATYPE(QScriptValue)
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"
69 struct ObjectData : public QScriptDeclarativeClass::Object {
70 ObjectData(QObject *o, int t) : object(o), type(t) {
72 QDeclarativeData *ddata = QDeclarativeData::get(object, true);
73 if (ddata) ddata->objectDataRefCount++;
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();
85 QDeclarativeGuard<QObject> object;
90 The QDeclarativeObjectScriptClass handles property access for QObjects
91 via QtScript. It is also used to provide a more useful API in
94 QDeclarativeObjectScriptClass::QDeclarativeObjectScriptClass(QDeclarativeEngine *bindEngine)
95 : QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)),
96 methods(bindEngine), lastData(0), engine(bindEngine)
98 QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
100 m_destroy = scriptEngine->newFunction(destroy);
101 m_destroyId = createPersistentIdentifier(QLatin1String("destroy"));
102 m_toString = scriptEngine->newFunction(tostring);
103 m_toStringId = createPersistentIdentifier(QLatin1String("toString"));
106 QDeclarativeObjectScriptClass::~QDeclarativeObjectScriptClass()
110 QScriptValue QDeclarativeObjectScriptClass::newQObject(QObject *object, int type)
112 QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
115 return scriptEngine->nullValue();
116 // return newObject(scriptEngine, this, new ObjectData(object, type));
118 if (QObjectPrivate::get(object)->wasDeleted)
119 return scriptEngine->undefinedValue();
121 QDeclarativeData *ddata = QDeclarativeData::get(object, true);
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;
133 return newObject(scriptEngine, this, new ObjectData(object, type));
137 QObject *QDeclarativeObjectScriptClass::toQObject(const QScriptValue &value) const
139 return value.toQObject();
142 int QDeclarativeObjectScriptClass::objectType(const QScriptValue &value) const
144 if (scriptClass(value) != this)
145 return QVariant::Invalid;
147 Object *o = object(value);
148 return ((ObjectData*)(o))->type;
151 QScriptClass::QueryFlags
152 QDeclarativeObjectScriptClass::queryProperty(Object *object, const Identifier &name,
153 QScriptClass::QueryFlags flags)
155 return queryProperty(toQObject(object), name, flags, 0);
158 QScriptClass::QueryFlags
159 QDeclarativeObjectScriptClass::queryProperty(QObject *obj, const Identifier &name,
160 QScriptClass::QueryFlags flags, QDeclarativeContextData *evalContext,
167 if (name == m_destroyId.identifier ||
168 name == m_toStringId.identifier)
169 return QScriptClass::HandlesReadAccess;
174 QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine);
175 lastData = QDeclarativePropertyCache::property(engine, obj, name, local);
176 if ((hints & ImplicitObject) && lastData && lastData->revision != 0) {
178 QDeclarativeData *ddata = QDeclarativeData::get(obj);
179 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(lastData))
184 return QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess;
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);
193 evalContext = enginePrivate->contextClass->contextFromValue(scopeNode);
197 if (evalContext && evalContext->imports) {
198 QDeclarativeTypeNameCache::Data *data = evalContext->imports->data(name);
201 return QScriptClass::HandlesReadAccess;
206 if (!(hints & ImplicitObject)) {
207 local.coreIndex = -1;
209 return QScriptClass::HandlesWriteAccess;
215 QDeclarativeObjectScriptClass::Value
216 QDeclarativeObjectScriptClass::property(Object *object, const Identifier &name)
218 return property(toQObject(object), name);
221 QDeclarativeObjectScriptClass::Value
222 QDeclarativeObjectScriptClass::property(QObject *obj, const Identifier &name)
224 QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
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);
231 if (lastData && !lastData->isValid())
236 QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine);
240 if (lastTNData->type)
241 return Value(scriptEngine, enginePriv->typeNameClass->newObject(obj, lastTNData->type));
243 return Value(scriptEngine, enginePriv->typeNameClass->newObject(obj, lastTNData->typeNamespace));
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));
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));
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());
260 enginePriv->capturedProperties <<
261 QDeclarativeEnginePrivate::CapturedProperty(obj, lastData->coreIndex, lastData->notifyIndex);
265 if (QDeclarativeValueTypeFactory::isValueType((uint)lastData->propType)) {
266 QDeclarativeValueType *valueType = enginePriv->valueTypes[lastData->propType];
268 return Value(scriptEngine, enginePriv->valueTypeClass->newObject(obj, lastData->coreIndex, valueType));
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) {
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) {
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) {
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) {
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) {
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) {
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) {
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) {
315 void *args[] = { &rv, 0 };
316 QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
317 return Value(scriptEngine, rv);
319 QVariant var = obj->metaObject()->property(lastData->coreIndex).read(obj);
320 return Value(scriptEngine, enginePriv->scriptValueFromVariant(var));
325 void QDeclarativeObjectScriptClass::setProperty(Object *object,
326 const Identifier &name,
327 const QScriptValue &value)
329 return setProperty(toQObject(object), name, value, context());
332 void QDeclarativeObjectScriptClass::setProperty(QObject *obj,
333 const Identifier &name,
334 const QScriptValue &value,
335 QScriptContext *context,
336 QDeclarativeContextData *evalContext)
344 if (!lastData->isValid()) {
345 QString error = QLatin1String("Cannot assign to non-existent property \"") +
346 toString(name) + QLatin1Char('\"');
347 context->throwError(error);
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);
359 QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine);
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);
367 evalContext = enginePriv->contextClass->contextFromValue(scopeNode);
371 QDeclarativeBinding *newBinding = 0;
372 if (value.isFunction() && !value.isRegExp()) {
373 QScriptContextInfo ctxtInfo(context);
374 QDeclarativePropertyCache::ValueTypeData valueTypeData;
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);
383 QDeclarativeAbstractBinding *delBinding =
384 QDeclarativePropertyPrivate::setBinding(obj, lastData->coreIndex, -1, newBinding);
386 delBinding->destroy();
388 if (value.isNull() && lastData->flags & QDeclarativePropertyCache::Data::IsQObjectDerived) {
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) {
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
407 if (lastData->flags & QDeclarativePropertyCache::Data::IsQList)
408 v = enginePriv->scriptValueToVariant(value, qMetaTypeId<QList<QObject *> >());
410 v = enginePriv->scriptValueToVariant(value, lastData->propType);
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());
417 QString error = QLatin1String("Cannot assign ") +
418 QLatin1String(valueType) +
419 QLatin1String(" to ") +
420 QLatin1String(QMetaType::typeName(lastData->propType));
421 context->throwError(error);
426 bool QDeclarativeObjectScriptClass::isQObject() const
431 QObject *QDeclarativeObjectScriptClass::toQObject(Object *object, bool *ok)
435 ObjectData *data = (ObjectData*)object;
436 return data->object.data();
439 QScriptValue QDeclarativeObjectScriptClass::tostring(QScriptContext *context, QScriptEngine *)
441 QObject* obj = context->thisObject().toQObject();
445 QString objectName = obj->objectName();
447 ret += QString::fromUtf8(obj->metaObject()->className());
448 ret += QLatin1String("(0x");
449 ret += QString::number((quintptr)obj,16);
451 if (!objectName.isEmpty()) {
452 ret += QLatin1String(", \"");
454 ret += QLatin1Char('\"');
457 ret += QLatin1Char(')');
459 ret += QLatin1String("null");
461 return QScriptValue(ret);
464 QScriptValue QDeclarativeObjectScriptClass::destroy(QScriptContext *context, QScriptEngine *engine)
466 QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
467 QScriptValue that = context->thisObject();
469 if (scriptClass(that) != p->objectClass)
470 return engine->undefinedValue();
472 ObjectData *data = (ObjectData *)p->objectClass->object(that);
474 return engine->undefinedValue();
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"));
480 QObject *obj = data->object;
482 if (context->argumentCount() > 0)
483 delay = context->argument(0).toInt32();
485 QTimer::singleShot(delay, obj, SLOT(deleteLater()));
489 return engine->undefinedValue();
492 QStringList QDeclarativeObjectScriptClass::propertyNames(Object *object)
494 QObject *obj = toQObject(object);
496 return QStringList();
498 QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine);
500 QDeclarativePropertyCache *cache = 0;
501 QDeclarativeData *ddata = QDeclarativeData::get(obj);
503 cache = ddata->propertyCache;
505 cache = enginePrivate->cache(obj);
507 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
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();
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());
521 return cache->propertyNames();
524 bool QDeclarativeObjectScriptClass::compare(Object *o1, Object *o2)
526 ObjectData *d1 = (ObjectData *)o1;
527 ObjectData *d2 = (ObjectData *)o2;
529 return d1 == d2 || d1->object == d2->object;
532 struct MethodData : public QScriptDeclarativeClass::Object {
533 MethodData(QObject *o, const QDeclarativePropertyCache::Data &d) : object(o), data(d) {}
535 QDeclarativeGuard<QObject> object;
536 QDeclarativePropertyCache::Data data;
539 QDeclarativeObjectMethodScriptClass::QDeclarativeObjectMethodScriptClass(QDeclarativeEngine *bindEngine)
540 : QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)),
543 qRegisterMetaType<QList<QObject *> >("QList<QObject *>");
545 setSupportsCall(true);
547 QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
549 m_connect = scriptEngine->newFunction(connect);
550 m_connectId = createPersistentIdentifier(QLatin1String("connect"));
551 m_disconnect = scriptEngine->newFunction(disconnect);
552 m_disconnectId = createPersistentIdentifier(QLatin1String("disconnect"));
555 QDeclarativeObjectMethodScriptClass::~QDeclarativeObjectMethodScriptClass()
559 QScriptValue QDeclarativeObjectMethodScriptClass::newMethod(QObject *object, const QDeclarativePropertyCache::Data *method)
561 QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
563 return newObject(scriptEngine, this, new MethodData(object, *method));
566 QScriptValue QDeclarativeObjectMethodScriptClass::connect(QScriptContext *context, QScriptEngine *engine)
568 QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
570 QScriptValue that = context->thisObject();
571 if (&p->objectClass->methods != scriptClass(that))
572 return engine->undefinedValue();
574 MethodData *data = (MethodData *)object(that);
576 if (!data->object || context->argumentCount() == 0)
577 return engine->undefinedValue();
579 QByteArray signal("2");
580 signal.append(data->object->metaObject()->method(data->data.coreIndex).signature());
582 if (context->argumentCount() == 1) {
583 qScriptConnect(data->object, signal.constData(), QScriptValue(), context->argument(0));
585 qScriptConnect(data->object, signal.constData(), context->argument(0), context->argument(1));
588 return engine->undefinedValue();
591 QScriptValue QDeclarativeObjectMethodScriptClass::disconnect(QScriptContext *context, QScriptEngine *engine)
593 QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
595 QScriptValue that = context->thisObject();
596 if (&p->objectClass->methods != scriptClass(that))
597 return engine->undefinedValue();
599 MethodData *data = (MethodData *)object(that);
601 if (!data->object || context->argumentCount() == 0)
602 return engine->undefinedValue();
604 QByteArray signal("2");
605 signal.append(data->object->metaObject()->method(data->data.coreIndex).signature());
607 if (context->argumentCount() == 1) {
608 qScriptDisconnect(data->object, signal.constData(), QScriptValue(), context->argument(0));
610 qScriptDisconnect(data->object, signal.constData(), context->argument(0), context->argument(1));
613 return engine->undefinedValue();
616 QScriptClass::QueryFlags
617 QDeclarativeObjectMethodScriptClass::queryProperty(Object *, const Identifier &name,
618 QScriptClass::QueryFlags flags)
621 if (name == m_connectId.identifier || name == m_disconnectId.identifier)
622 return QScriptClass::HandlesReadAccess;
628 QDeclarativeObjectMethodScriptClass::Value
629 QDeclarativeObjectMethodScriptClass::property(Object *, const Identifier &name)
631 QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
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);
642 struct MetaCallArgument {
643 inline MetaCallArgument();
644 inline ~MetaCallArgument();
645 inline void *dataPtr();
647 inline void initAsType(int type, QDeclarativeEngine *);
648 void fromScriptValue(int type, QDeclarativeEngine *, const QScriptValue &);
649 inline QScriptDeclarativeClass::Value toValue(QDeclarativeEngine *);
652 MetaCallArgument(const MetaCallArgument &);
654 inline void cleanup();
656 char data[4 * sizeof(void *)];
662 MetaCallArgument::MetaCallArgument()
663 : type(QVariant::Invalid), isObjectType(false)
667 MetaCallArgument::~MetaCallArgument()
672 void MetaCallArgument::cleanup()
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 *>();
685 void *MetaCallArgument::dataPtr()
688 return ((QVariant *)data)->data();
690 return (void *)&data;
693 void MetaCallArgument::initAsType(int callType, QDeclarativeEngine *e)
695 if (type != 0) { cleanup(); type = 0; }
696 if (callType == 0) return;
698 QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e);
700 if (callType == qMetaTypeId<QScriptValue>()) {
701 new (&data) QScriptValue(engine->undefinedValue());
703 } else if (callType == QMetaType::Int ||
704 callType == QMetaType::UInt ||
705 callType == QMetaType::Bool ||
706 callType == QMetaType::Double ||
707 callType == QMetaType::Float) {
709 } else if (callType == QMetaType::QObjectStar) {
710 *((QObject **)&data) = 0;
712 } else if (callType == QMetaType::QString) {
713 new (&data) QString();
715 } else if (callType == qMetaTypeId<QVariant>()) {
717 new (&data) QVariant();
718 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
720 new (&data) QList<QObject *>();
723 new (&data) QVariant(callType, (void *)0);
727 void MetaCallArgument::fromScriptValue(int callType, QDeclarativeEngine *engine, const QScriptValue &value)
729 if (type != 0) { cleanup(); type = 0; }
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());
737 } else if (callType == QMetaType::UInt) {
738 *((uint *)&data) = uint(value.toUInt32());
740 } else if (callType == QMetaType::Bool) {
741 *((bool *)&data) = value.toBool();
743 } else if (callType == QMetaType::Double) {
744 *((double *)&data) = double(value.toNumber());
746 } else if (callType == QMetaType::Float) {
747 *((float *)&data) = float(value.toNumber());
749 } else if (callType == QMetaType::QString) {
750 if (value.isNull() || value.isUndefined())
751 new (&data) QString();
753 new (&data) QString(value.toString());
755 } else if (callType == QMetaType::QObjectStar) {
756 *((QObject **)&data) = value.toQObject();
758 } else if (callType == qMetaTypeId<QVariant>()) {
759 new (&data) QVariant(QDeclarativeEnginePrivate::get(engine)->scriptValueToVariant(value));
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();
770 } else if (QObject *d = value.toQObject()) {
775 new (&data) QVariant();
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);
789 const QMetaObject *objMo = obj->metaObject();
790 while (objMo && objMo != mo) objMo = objMo->superClass();
794 *((QVariant *)&data) = QVariant(callType, &obj);
796 *((QVariant *)&data) = QVariant(callType, (void *)0);
801 QScriptDeclarativeClass::Value MetaCallArgument::toValue(QDeclarativeEngine *e)
803 QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e);
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);
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));
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();
841 QDeclarativeData::get(object, true)->setImplicitDestructible();
843 return QScriptDeclarativeClass::Value(engine, rv);
845 return QScriptDeclarativeClass::Value();
849 int QDeclarativeObjectMethodScriptClass::enumType(const QMetaObject *meta, const QString &strname)
851 QByteArray str = strname.toUtf8();
854 int scopeIdx = str.lastIndexOf("::");
855 if (scopeIdx != -1) {
856 scope = str.left(scopeIdx);
857 name = str.mid(scopeIdx + 2);
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;
866 return QVariant::Invalid;
869 QDeclarativeObjectMethodScriptClass::Value QDeclarativeObjectMethodScriptClass::call(Object *o, QScriptContext *ctxt)
871 MethodData *method = static_cast<MethodData *>(o);
873 if (method->data.relatedIndex == -1)
874 return callPrecise(method->object, method->data, ctxt);
876 return callOverloaded(method, ctxt);
879 QDeclarativeObjectMethodScriptClass::Value
880 QDeclarativeObjectMethodScriptClass::callPrecise(QObject *object, const QDeclarativePropertyCache::Data &data,
881 QScriptContext *ctxt)
883 if (data.flags & QDeclarativePropertyCache::Data::HasArguments) {
885 QMetaMethod m = object->metaObject()->method(data.coreIndex);
886 QList<QByteArray> argTypeNames = m.parameterTypes();
887 QVarLengthArray<int, 9> argTypes(argTypeNames.count());
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)))));
898 if (argTypes.count() > ctxt->argumentCount())
899 return Value(ctxt, ctxt->throwError(QLatin1String("Insufficient arguments")));
901 return callMethod(object, data.coreIndex, data.propType, argTypes.count(), argTypes.data(), ctxt);
905 return callMethod(object, data.coreIndex, data.propType, 0, 0, ctxt);
910 QDeclarativeObjectMethodScriptClass::Value
911 QDeclarativeObjectMethodScriptClass::callMethod(QObject *object, int index,
912 int returnType, int argCount, int *argTypes,
913 QScriptContext *ctxt)
917 QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
918 args[0].initAsType(returnType, engine);
920 for (int ii = 0; ii < argCount; ++ii)
921 args[ii + 1].fromScriptValue(argTypes[ii], engine, ctxt->argument(ii));
923 QVarLengthArray<void *, 9> argData(args.count());
924 for (int ii = 0; ii < args.count(); ++ii)
925 argData[ii] = args[ii].dataPtr();
927 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
929 return args[0].toValue(engine);
931 } else if (returnType != 0) {
933 MetaCallArgument arg;
934 arg.initAsType(returnType, engine);
936 void *args[] = { arg.dataPtr() };
938 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
940 return arg.toValue(engine);
944 void *args[] = { 0 };
945 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
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
956 2. Filter the set of overloads to only contain those with the closest number of
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.
964 QDeclarativeObjectMethodScriptClass::Value
965 QDeclarativeObjectMethodScriptClass::callOverloaded(MethodData *method, QScriptContext *ctxt)
967 int argumentCount = ctxt->argumentCount();
969 QDeclarativePropertyCache::Data *best = 0;
970 int bestParameterScore = INT_MAX;
971 int bestMatchScore = INT_MAX;
973 QDeclarativePropertyCache::Data dummy;
974 QDeclarativePropertyCache::Data *attempt = &method->data;
977 QList<QByteArray> methodArgTypeNames;
979 if (attempt->flags & QDeclarativePropertyCache::Data::HasArguments)
980 methodArgTypeNames = method->object->metaObject()->method(attempt->coreIndex).parameterTypes();
982 int methodArgumentCount = methodArgTypeNames.count();
984 if (methodArgumentCount > argumentCount)
985 continue; // We don't have sufficient arguments to call this method
987 int methodParameterScore = argumentCount - methodArgumentCount;
988 if (methodParameterScore > bestParameterScore)
989 continue; // We already have a better option
991 int methodMatchScore = 0;
992 QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount);
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;
1004 methodMatchScore += matchScore(ctxt->argument(ii), methodArgTypes[ii], methodArgTypeNames.at(ii));
1006 if (unknownArgument)
1007 continue; // We don't understand all the parameters
1009 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1011 bestParameterScore = methodParameterScore;
1012 bestMatchScore = methodMatchScore;
1015 if (bestParameterScore == 0 && bestMatchScore == 0)
1016 break; // We can't get better than that
1018 } while((attempt = relatedMethod(method->object, attempt, dummy)) != 0);
1021 return callPrecise(method->object, *best, ctxt);
1023 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1024 QDeclarativePropertyCache::Data *candidate = &method->data;
1026 error += QLatin1String("\n ") + QString::fromUtf8(method->object->metaObject()->method(candidate->coreIndex).signature());
1027 candidate = relatedMethod(method->object, candidate, dummy);
1029 return Value(ctxt, ctxt->throwError(error));
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.
1037 The conversion table is copied out of the QtScript callQtMethod() function.
1039 int QDeclarativeObjectMethodScriptClass::matchScore(const QScriptValue &actual, int conversionType,
1040 const QByteArray &conversionTypeName)
1042 if (actual.isNumber()) {
1043 switch (conversionType) {
1044 case QMetaType::Double:
1046 case QMetaType::Float:
1048 case QMetaType::LongLong:
1049 case QMetaType::ULongLong:
1051 case QMetaType::Long:
1052 case QMetaType::ULong:
1054 case QMetaType::Int:
1055 case QMetaType::UInt:
1057 case QMetaType::Short:
1058 case QMetaType::UShort:
1061 case QMetaType::Char:
1062 case QMetaType::UChar:
1067 } else if (actual.isString()) {
1068 switch (conversionType) {
1069 case QMetaType::QString:
1074 } else if (actual.isBoolean()) {
1075 switch (conversionType) {
1076 case QMetaType::Bool:
1081 } else if (actual.isDate()) {
1082 switch (conversionType) {
1083 case QMetaType::QDateTime:
1085 case QMetaType::QDate:
1087 case QMetaType::QTime:
1092 } else if (actual.isRegExp()) {
1093 switch (conversionType) {
1094 case QMetaType::QRegExp:
1099 } else if (actual.isVariant()) {
1100 if (conversionType == qMetaTypeId<QVariant>())
1102 else if (actual.toVariant().userType() == conversionType)
1106 } else if (actual.isArray()) {
1107 switch (conversionType) {
1108 case QMetaType::QStringList:
1109 case QMetaType::QVariantList:
1114 } else if (actual.isQObject()) {
1115 switch (conversionType) {
1116 case QMetaType::QObjectStar:
1121 } else if (actual.isNull()) {
1122 switch (conversionType) {
1123 case QMetaType::VoidStar:
1124 case QMetaType::QObjectStar:
1127 if (!conversionTypeName.endsWith('*'))
1137 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1143 int classInfoCount, classInfoData;
1144 int methodCount, methodData;
1147 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1150 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1152 QByteArray sig = m.signature();
1153 int paren = sig.indexOf('(');
1157 return sig.left(paren);
1161 Returns the next related method, if one, or 0.
1163 QDeclarativePropertyCache::Data *
1164 QDeclarativeObjectMethodScriptClass::relatedMethod(QObject *object, QDeclarativePropertyCache::Data *current,
1165 QDeclarativePropertyCache::Data &dummy)
1167 QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1168 if (current->relatedIndex == -1)
1172 return cache->method(current->relatedIndex);
1174 const QMetaObject *mo = object->metaObject();
1175 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1177 while (methodOffset > current->relatedIndex) {
1178 mo = mo->superClass();
1179 methodOffset -= QMetaObject_methods(mo);
1182 QMetaMethod method = mo->method(current->relatedIndex);
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;