source: trunk/src/declarative/qml/qdeclarativeobjectscriptclass.cpp@ 1147

Last change on this file since 1147 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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