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 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
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
406 //### expand optimization for other known types
407 if (lastData->propType == QMetaType::Int && value.isNumber()) {
408 int rawValue = qRound(value.toNumber());
411 void *a[] = { (void *)&rawValue, 0, &status, &flags };
412 QMetaObject::metacall(obj, QMetaObject::WriteProperty,
413 lastData->coreIndex, a);
415 } else if (lastData->propType == QMetaType::QReal && value.isNumber()) {
416 qreal rawValue = qreal(value.toNumber());
419 void *a[] = { (void *)&rawValue, 0, &status, &flags };
420 QMetaObject::metacall(obj, QMetaObject::WriteProperty,
421 lastData->coreIndex, a);
423 } else if (lastData->propType == QMetaType::QString && value.isString()) {
424 const QString &rawValue = value.toString();
427 void *a[] = { (void *)&rawValue, 0, &status, &flags };
428 QMetaObject::metacall(obj, QMetaObject::WriteProperty,
429 lastData->coreIndex, a);
434 if (lastData->flags & QDeclarativePropertyCache::Data::IsQList)
435 v = enginePriv->scriptValueToVariant(value, qMetaTypeId<QList<QObject *> >());
437 v = enginePriv->scriptValueToVariant(value, lastData->propType);
439 if (!QDeclarativePropertyPrivate::write(obj, *lastData, v, evalContext)) {
440 const char *valueType = 0;
441 if (v.userType() == QVariant::Invalid) valueType = "null";
442 else valueType = QMetaType::typeName(v.userType());
444 QString error = QLatin1String("Cannot assign ") +
445 QLatin1String(valueType) +
446 QLatin1String(" to ") +
447 QLatin1String(QMetaType::typeName(lastData->propType));
448 context->throwError(error);
453 bool QDeclarativeObjectScriptClass::isQObject() const
458 QObject *QDeclarativeObjectScriptClass::toQObject(Object *object, bool *ok)
462 ObjectData *data = (ObjectData*)object;
463 return data->object.data();
466 QScriptValue QDeclarativeObjectScriptClass::tostring(QScriptContext *context, QScriptEngine *)
468 QObject* obj = context->thisObject().toQObject();
472 QString objectName = obj->objectName();
474 ret += QString::fromUtf8(obj->metaObject()->className());
475 ret += QLatin1String("(0x");
476 ret += QString::number((quintptr)obj,16);
478 if (!objectName.isEmpty()) {
479 ret += QLatin1String(", \"");
481 ret += QLatin1Char('\"');
484 ret += QLatin1Char(')');
486 ret += QLatin1String("null");
488 return QScriptValue(ret);
491 QScriptValue QDeclarativeObjectScriptClass::destroy(QScriptContext *context, QScriptEngine *engine)
493 QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
494 QScriptValue that = context->thisObject();
496 if (scriptClass(that) != p->objectClass)
497 return engine->undefinedValue();
499 ObjectData *data = (ObjectData *)p->objectClass->object(that);
501 return engine->undefinedValue();
503 QDeclarativeData *ddata = QDeclarativeData::get(data->object, false);
504 if (!ddata || ddata->indestructible)
505 return engine->currentContext()->throwError(QLatin1String("Invalid attempt to destroy() an indestructible object"));
507 QObject *obj = data->object;
509 if (context->argumentCount() > 0)
510 delay = context->argument(0).toInt32();
512 QTimer::singleShot(delay, obj, SLOT(deleteLater()));
516 return engine->undefinedValue();
519 QStringList QDeclarativeObjectScriptClass::propertyNames(Object *object)
521 QObject *obj = toQObject(object);
523 return QStringList();
525 QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine);
527 QDeclarativePropertyCache *cache = 0;
528 QDeclarativeData *ddata = QDeclarativeData::get(obj);
530 cache = ddata->propertyCache;
532 cache = enginePrivate->cache(obj);
534 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
536 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
537 // XXX QDeclarativeOpenMetaObject has a cache, so this is suboptimal.
538 // XXX This is a workaround for QTBUG-9420.
539 const QMetaObject *mo = obj->metaObject();
541 int pc = mo->propertyCount();
542 int po = mo->propertyOffset();
543 for (int i=po; i<pc; ++i)
544 r += QString::fromUtf8(mo->property(i).name());
548 return cache->propertyNames();
551 bool QDeclarativeObjectScriptClass::compare(Object *o1, Object *o2)
553 ObjectData *d1 = (ObjectData *)o1;
554 ObjectData *d2 = (ObjectData *)o2;
556 return d1 == d2 || d1->object == d2->object;
559 struct MethodData : public QScriptDeclarativeClass::Object {
560 MethodData(QObject *o, const QDeclarativePropertyCache::Data &d) : object(o), data(d) {}
562 QDeclarativeGuard<QObject> object;
563 QDeclarativePropertyCache::Data data;
566 QDeclarativeObjectMethodScriptClass::QDeclarativeObjectMethodScriptClass(QDeclarativeEngine *bindEngine)
567 : QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)),
570 qRegisterMetaType<QList<QObject *> >("QList<QObject *>");
572 setSupportsCall(true);
574 QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
576 m_connect = scriptEngine->newFunction(connect);
577 m_connectId = createPersistentIdentifier(QLatin1String("connect"));
578 m_disconnect = scriptEngine->newFunction(disconnect);
579 m_disconnectId = createPersistentIdentifier(QLatin1String("disconnect"));
582 QDeclarativeObjectMethodScriptClass::~QDeclarativeObjectMethodScriptClass()
586 QScriptValue QDeclarativeObjectMethodScriptClass::newMethod(QObject *object, const QDeclarativePropertyCache::Data *method)
588 QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
590 return newObject(scriptEngine, this, new MethodData(object, *method));
593 QScriptValue QDeclarativeObjectMethodScriptClass::connect(QScriptContext *context, QScriptEngine *engine)
595 QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
597 QScriptValue that = context->thisObject();
598 if (&p->objectClass->methods != scriptClass(that))
599 return engine->undefinedValue();
601 MethodData *data = (MethodData *)object(that);
603 if (!data->object || context->argumentCount() == 0)
604 return engine->undefinedValue();
606 QByteArray signal("2");
607 signal.append(data->object->metaObject()->method(data->data.coreIndex).signature());
609 if (context->argumentCount() == 1) {
610 qScriptConnect(data->object, signal.constData(), QScriptValue(), context->argument(0));
612 qScriptConnect(data->object, signal.constData(), context->argument(0), context->argument(1));
615 return engine->undefinedValue();
618 QScriptValue QDeclarativeObjectMethodScriptClass::disconnect(QScriptContext *context, QScriptEngine *engine)
620 QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
622 QScriptValue that = context->thisObject();
623 if (&p->objectClass->methods != scriptClass(that))
624 return engine->undefinedValue();
626 MethodData *data = (MethodData *)object(that);
628 if (!data->object || context->argumentCount() == 0)
629 return engine->undefinedValue();
631 QByteArray signal("2");
632 signal.append(data->object->metaObject()->method(data->data.coreIndex).signature());
634 if (context->argumentCount() == 1) {
635 qScriptDisconnect(data->object, signal.constData(), QScriptValue(), context->argument(0));
637 qScriptDisconnect(data->object, signal.constData(), context->argument(0), context->argument(1));
640 return engine->undefinedValue();
643 QScriptClass::QueryFlags
644 QDeclarativeObjectMethodScriptClass::queryProperty(Object *, const Identifier &name,
645 QScriptClass::QueryFlags flags)
648 if (name == m_connectId.identifier || name == m_disconnectId.identifier)
649 return QScriptClass::HandlesReadAccess;
655 QDeclarativeObjectMethodScriptClass::Value
656 QDeclarativeObjectMethodScriptClass::property(Object *, const Identifier &name)
658 QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
660 if (name == m_connectId.identifier)
661 return Value(scriptEngine, m_connect);
662 else if (name == m_disconnectId.identifier)
663 return Value(scriptEngine, m_disconnect);
669 struct MetaCallArgument {
670 inline MetaCallArgument();
671 inline ~MetaCallArgument();
672 inline void *dataPtr();
674 inline void initAsType(int type, QDeclarativeEngine *);
675 void fromScriptValue(int type, QDeclarativeEngine *, const QScriptValue &);
676 inline QScriptDeclarativeClass::Value toValue(QDeclarativeEngine *);
679 MetaCallArgument(const MetaCallArgument &);
681 inline void cleanup();
683 char data[4 * sizeof(void *)];
689 MetaCallArgument::MetaCallArgument()
690 : type(QVariant::Invalid), isObjectType(false)
694 MetaCallArgument::~MetaCallArgument()
699 void MetaCallArgument::cleanup()
701 if (type == QMetaType::QString) {
702 ((QString *)&data)->~QString();
703 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
704 ((QVariant *)&data)->~QVariant();
705 } else if (type == qMetaTypeId<QScriptValue>()) {
706 ((QScriptValue *)&data)->~QScriptValue();
707 } else if (type == qMetaTypeId<QList<QObject *> >()) {
708 ((QList<QObject *> *)&data)->~QList<QObject *>();
712 void *MetaCallArgument::dataPtr()
715 return ((QVariant *)data)->data();
717 return (void *)&data;
720 void MetaCallArgument::initAsType(int callType, QDeclarativeEngine *e)
722 if (type != 0) { cleanup(); type = 0; }
723 if (callType == 0) return;
725 QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e);
727 if (callType == qMetaTypeId<QScriptValue>()) {
728 new (&data) QScriptValue(engine->undefinedValue());
730 } else if (callType == QMetaType::Int ||
731 callType == QMetaType::UInt ||
732 callType == QMetaType::Bool ||
733 callType == QMetaType::Double ||
734 callType == QMetaType::Float) {
736 } else if (callType == QMetaType::QObjectStar) {
737 *((QObject **)&data) = 0;
739 } else if (callType == QMetaType::QString) {
740 new (&data) QString();
742 } else if (callType == qMetaTypeId<QVariant>()) {
744 new (&data) QVariant();
745 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
747 new (&data) QList<QObject *>();
750 new (&data) QVariant(callType, (void *)0);
754 void MetaCallArgument::fromScriptValue(int callType, QDeclarativeEngine *engine, const QScriptValue &value)
756 if (type != 0) { cleanup(); type = 0; }
758 if (callType == qMetaTypeId<QScriptValue>()) {
759 new (&data) QScriptValue(value);
760 type = qMetaTypeId<QScriptValue>();
761 } else if (callType == QMetaType::Int) {
762 *((int *)&data) = int(value.toInt32());
764 } else if (callType == QMetaType::UInt) {
765 *((uint *)&data) = uint(value.toUInt32());
767 } else if (callType == QMetaType::Bool) {
768 *((bool *)&data) = value.toBool();
770 } else if (callType == QMetaType::Double) {
771 *((double *)&data) = double(value.toNumber());
773 } else if (callType == QMetaType::Float) {
774 *((float *)&data) = float(value.toNumber());
776 } else if (callType == QMetaType::QString) {
777 if (value.isNull() || value.isUndefined())
778 new (&data) QString();
780 new (&data) QString(value.toString());
782 } else if (callType == QMetaType::QObjectStar) {
783 *((QObject **)&data) = value.toQObject();
785 } else if (callType == qMetaTypeId<QVariant>()) {
786 new (&data) QVariant(QDeclarativeEnginePrivate::get(engine)->scriptValueToVariant(value));
788 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
789 QList<QObject *> *list = new (&data) QList<QObject *>();
790 if (value.isArray()) {
791 int length = value.property(QLatin1String("length")).toInt32();
792 for (int ii = 0; ii < length; ++ii) {
793 QScriptValue arrayItem = value.property(ii);
794 QObject *d = arrayItem.toQObject();
797 } else if (QObject *d = value.toQObject()) {
802 new (&data) QVariant();
805 QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(engine);
806 QVariant v = priv->scriptValueToVariant(value);
807 if (v.userType() == callType) {
808 *((QVariant *)&data) = v;
809 } else if (v.canConvert((QVariant::Type)callType)) {
810 *((QVariant *)&data) = v;
811 ((QVariant *)&data)->convert((QVariant::Type)callType);
812 } else if (const QMetaObject *mo = priv->rawMetaObjectForType(callType)) {
813 QObject *obj = priv->toQObject(v);
816 const QMetaObject *objMo = obj->metaObject();
817 while (objMo && objMo != mo) objMo = objMo->superClass();
821 *((QVariant *)&data) = QVariant(callType, &obj);
823 *((QVariant *)&data) = QVariant(callType, (void *)0);
828 QScriptDeclarativeClass::Value MetaCallArgument::toValue(QDeclarativeEngine *e)
830 QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e);
832 if (type == qMetaTypeId<QScriptValue>()) {
833 return QScriptDeclarativeClass::Value(engine, *((QScriptValue *)&data));
834 } else if (type == QMetaType::Int) {
835 return QScriptDeclarativeClass::Value(engine, *((int *)&data));
836 } else if (type == QMetaType::UInt) {
837 return QScriptDeclarativeClass::Value(engine, *((uint *)&data));
838 } else if (type == QMetaType::Bool) {
839 return QScriptDeclarativeClass::Value(engine, *((bool *)&data));
840 } else if (type == QMetaType::Double) {
841 return QScriptDeclarativeClass::Value(engine, *((double *)&data));
842 } else if (type == QMetaType::Float) {
843 return QScriptDeclarativeClass::Value(engine, *((float *)&data));
844 } else if (type == QMetaType::QString) {
845 return QScriptDeclarativeClass::Value(engine, *((QString *)&data));
846 } else if (type == QMetaType::QObjectStar) {
847 QObject *object = *((QObject **)&data);
849 QDeclarativeData::get(object, true)->setImplicitDestructible();
850 QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(e);
851 return QScriptDeclarativeClass::Value(engine, priv->objectClass->newQObject(object));
852 } else if (type == qMetaTypeId<QList<QObject *> >()) {
853 QList<QObject *> &list = *(QList<QObject *>*)&data;
854 QScriptValue rv = engine->newArray(list.count());
855 QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(e);
856 for (int ii = 0; ii < list.count(); ++ii) {
857 QObject *object = list.at(ii);
858 QDeclarativeData::get(object, true)->setImplicitDestructible();
859 rv.setProperty(ii, priv->objectClass->newQObject(object));
861 return QScriptDeclarativeClass::Value(engine, rv);
862 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
863 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(e);
864 QScriptValue rv = ep->scriptValueFromVariant(*((QVariant *)&data));
865 if (rv.isQObject()) {
866 QObject *object = rv.toQObject();
868 QDeclarativeData::get(object, true)->setImplicitDestructible();
870 return QScriptDeclarativeClass::Value(engine, rv);
872 return QScriptDeclarativeClass::Value();
876 int QDeclarativeObjectMethodScriptClass::enumType(const QMetaObject *meta, const QString &strname)
878 QByteArray str = strname.toUtf8();
881 int scopeIdx = str.lastIndexOf("::");
882 if (scopeIdx != -1) {
883 scope = str.left(scopeIdx);
884 name = str.mid(scopeIdx + 2);
888 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
889 QMetaEnum m = meta->enumerator(i);
890 if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
891 return QVariant::Int;
893 return QVariant::Invalid;
896 QDeclarativeObjectMethodScriptClass::Value QDeclarativeObjectMethodScriptClass::call(Object *o, QScriptContext *ctxt)
898 MethodData *method = static_cast<MethodData *>(o);
900 if (method->data.relatedIndex == -1)
901 return callPrecise(method->object, method->data, ctxt);
903 return callOverloaded(method, ctxt);
906 QDeclarativeObjectMethodScriptClass::Value
907 QDeclarativeObjectMethodScriptClass::callPrecise(QObject *object, const QDeclarativePropertyCache::Data &data,
908 QScriptContext *ctxt)
910 if (data.flags & QDeclarativePropertyCache::Data::HasArguments) {
912 QMetaMethod m = object->metaObject()->method(data.coreIndex);
913 QList<QByteArray> argTypeNames = m.parameterTypes();
914 QVarLengthArray<int, 9> argTypes(argTypeNames.count());
917 for (int ii = 0; ii < argTypeNames.count(); ++ii) {
918 argTypes[ii] = QMetaType::type(argTypeNames.at(ii));
919 if (argTypes[ii] == QVariant::Invalid)
920 argTypes[ii] = enumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii)));
921 if (argTypes[ii] == QVariant::Invalid)
922 return Value(ctxt, ctxt->throwError(QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii)))));
925 if (argTypes.count() > ctxt->argumentCount())
926 return Value(ctxt, ctxt->throwError(QLatin1String("Insufficient arguments")));
928 return callMethod(object, data.coreIndex, data.propType, argTypes.count(), argTypes.data(), ctxt);
932 return callMethod(object, data.coreIndex, data.propType, 0, 0, ctxt);
937 QDeclarativeObjectMethodScriptClass::Value
938 QDeclarativeObjectMethodScriptClass::callMethod(QObject *object, int index,
939 int returnType, int argCount, int *argTypes,
940 QScriptContext *ctxt)
944 QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
945 args[0].initAsType(returnType, engine);
947 for (int ii = 0; ii < argCount; ++ii)
948 args[ii + 1].fromScriptValue(argTypes[ii], engine, ctxt->argument(ii));
950 QVarLengthArray<void *, 9> argData(args.count());
951 for (int ii = 0; ii < args.count(); ++ii)
952 argData[ii] = args[ii].dataPtr();
954 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
956 return args[0].toValue(engine);
958 } else if (returnType != 0) {
960 MetaCallArgument arg;
961 arg.initAsType(returnType, engine);
963 void *args[] = { arg.dataPtr() };
965 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
967 return arg.toValue(engine);
971 void *args[] = { 0 };
972 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
979 Resolve the overloaded method to call. The algorithm works conceptually like this:
980 1. Resolve the set of overloads it is *possible* to call.
981 Impossible overloads include those that have too many parameters or have parameters
983 2. Filter the set of overloads to only contain those with the closest number of
985 For example, if we are called with 3 parameters and there are 2 overloads that
986 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
987 3. Find the best remaining overload based on its match score.
988 If two or more overloads have the same match score, call the last one. The match
989 score is constructed by adding the matchScore() result for each of the parameters.
991 QDeclarativeObjectMethodScriptClass::Value
992 QDeclarativeObjectMethodScriptClass::callOverloaded(MethodData *method, QScriptContext *ctxt)
994 int argumentCount = ctxt->argumentCount();
996 QDeclarativePropertyCache::Data *best = 0;
997 int bestParameterScore = INT_MAX;
998 int bestMatchScore = INT_MAX;
1000 QDeclarativePropertyCache::Data dummy;
1001 QDeclarativePropertyCache::Data *attempt = &method->data;
1004 QList<QByteArray> methodArgTypeNames;
1006 if (attempt->flags & QDeclarativePropertyCache::Data::HasArguments)
1007 methodArgTypeNames = method->object->metaObject()->method(attempt->coreIndex).parameterTypes();
1009 int methodArgumentCount = methodArgTypeNames.count();
1011 if (methodArgumentCount > argumentCount)
1012 continue; // We don't have sufficient arguments to call this method
1014 int methodParameterScore = argumentCount - methodArgumentCount;
1015 if (methodParameterScore > bestParameterScore)
1016 continue; // We already have a better option
1018 int methodMatchScore = 0;
1019 QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount);
1021 bool unknownArgument = false;
1022 for (int ii = 0; ii < methodArgumentCount; ++ii) {
1023 methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii));
1024 if (methodArgTypes[ii] == QVariant::Invalid)
1025 methodArgTypes[ii] = enumType(method->object->metaObject(),
1026 QString::fromLatin1(methodArgTypeNames.at(ii)));
1027 if (methodArgTypes[ii] == QVariant::Invalid) {
1028 unknownArgument = true;
1031 methodMatchScore += matchScore(ctxt->argument(ii), methodArgTypes[ii], methodArgTypeNames.at(ii));
1033 if (unknownArgument)
1034 continue; // We don't understand all the parameters
1036 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1038 bestParameterScore = methodParameterScore;
1039 bestMatchScore = methodMatchScore;
1042 if (bestParameterScore == 0 && bestMatchScore == 0)
1043 break; // We can't get better than that
1045 } while((attempt = relatedMethod(method->object, attempt, dummy)) != 0);
1048 return callPrecise(method->object, *best, ctxt);
1050 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1051 QDeclarativePropertyCache::Data *candidate = &method->data;
1053 error += QLatin1String("\n ") + QString::fromUtf8(method->object->metaObject()->method(candidate->coreIndex).signature());
1054 candidate = relatedMethod(method->object, candidate, dummy);
1056 return Value(ctxt, ctxt->throwError(error));
1061 Returns the match score for converting \a actual to be of type \a conversionType. A
1062 zero score means "perfect match" whereas a higher score is worse.
1064 The conversion table is copied out of the QtScript callQtMethod() function.
1066 int QDeclarativeObjectMethodScriptClass::matchScore(const QScriptValue &actual, int conversionType,
1067 const QByteArray &conversionTypeName)
1069 if (actual.isNumber()) {
1070 switch (conversionType) {
1071 case QMetaType::Double:
1073 case QMetaType::Float:
1075 case QMetaType::LongLong:
1076 case QMetaType::ULongLong:
1078 case QMetaType::Long:
1079 case QMetaType::ULong:
1081 case QMetaType::Int:
1082 case QMetaType::UInt:
1084 case QMetaType::Short:
1085 case QMetaType::UShort:
1088 case QMetaType::Char:
1089 case QMetaType::UChar:
1094 } else if (actual.isString()) {
1095 switch (conversionType) {
1096 case QMetaType::QString:
1101 } else if (actual.isBoolean()) {
1102 switch (conversionType) {
1103 case QMetaType::Bool:
1108 } else if (actual.isDate()) {
1109 switch (conversionType) {
1110 case QMetaType::QDateTime:
1112 case QMetaType::QDate:
1114 case QMetaType::QTime:
1119 } else if (actual.isRegExp()) {
1120 switch (conversionType) {
1121 case QMetaType::QRegExp:
1126 } else if (actual.isVariant()) {
1127 if (conversionType == qMetaTypeId<QVariant>())
1129 else if (actual.toVariant().userType() == conversionType)
1133 } else if (actual.isArray()) {
1134 switch (conversionType) {
1135 case QMetaType::QStringList:
1136 case QMetaType::QVariantList:
1141 } else if (actual.isQObject()) {
1142 switch (conversionType) {
1143 case QMetaType::QObjectStar:
1148 } else if (actual.isNull()) {
1149 switch (conversionType) {
1150 case QMetaType::VoidStar:
1151 case QMetaType::QObjectStar:
1154 if (!conversionTypeName.endsWith('*'))
1164 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1170 int classInfoCount, classInfoData;
1171 int methodCount, methodData;
1174 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1177 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1179 QByteArray sig = m.signature();
1180 int paren = sig.indexOf('(');
1184 return sig.left(paren);
1188 Returns the next related method, if one, or 0.
1190 QDeclarativePropertyCache::Data *
1191 QDeclarativeObjectMethodScriptClass::relatedMethod(QObject *object, QDeclarativePropertyCache::Data *current,
1192 QDeclarativePropertyCache::Data &dummy)
1194 QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1195 if (current->relatedIndex == -1)
1199 return cache->method(current->relatedIndex);
1201 const QMetaObject *mo = object->metaObject();
1202 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1204 while (methodOffset > current->relatedIndex) {
1205 mo = mo->superClass();
1206 methodOffset -= QMetaObject_methods(mo);
1209 QMetaMethod method = mo->method(current->relatedIndex);
1212 // Look for overloaded methods
1213 QByteArray methodName = QMetaMethod_name(method);
1214 for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) {
1215 if (methodName == QMetaMethod_name(mo->method(ii))) {
1216 dummy.relatedIndex = ii;