2 * Copyright (C) 2006 Trolltech ASA
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "qt_runtime.h"
22 #include "qt_instance.h"
24 #include "array_instance.h"
25 #include "date_object.h"
27 #include "regexp_object.h"
28 #include <runtime_object.h>
29 #include <runtime_array.h>
31 #include "PropertyNameArray.h"
32 #include "qmetatype.h"
33 #include "qmetaobject.h"
35 #include "qstringlist.h"
37 #include "qvarlengtharray.h"
38 #include "qdatetime.h"
42 Q_DECLARE_METATYPE(QObjectList
);
43 Q_DECLARE_METATYPE(QList
<int>);
44 Q_DECLARE_METATYPE(QVariant
);
51 //#define QTWK_RUNTIME_CONVERSION_DEBUG
52 //#define QTWK_RUNTIME_MATCH_DEBUG
58 inline ~QWKNoDebug(){}
61 inline QWKNoDebug
&operator<<(const T
&) { return *this; }
64 #ifdef QTWK_RUNTIME_CONVERSION_DEBUG
65 #define qConvDebug() qDebug()
67 #define qConvDebug() QWKNoDebug()
70 #ifdef QTWK_RUNTIME_MATCH_DEBUG
71 #define qMatchDebug() qDebug()
73 #define qMatchDebug() QWKNoDebug()
89 static JSRealType
valueRealType(ExecState
* exec
, JSValue
* val
)
93 else if (val
->isString())
95 else if (val
->isBoolean())
97 else if (val
->isNull())
99 else if (val
->isObject()) {
100 JSObject
*object
= val
->toObject(exec
);
101 if (object
->inherits(&ArrayInstance::info
))
103 else if (object
->inherits(&DateInstance::info
))
105 else if (object
->inherits(&RegExpImp::info
))
107 else if (object
->inherits(&RuntimeObjectImp::info
))
112 return String
; // I don't know.
115 QVariant
convertValueToQVariant(ExecState
* exec
, JSValue
* value
, QMetaType::Type hint
, int *distance
)
117 // check magic pointer values before dereferencing value
118 if (value
== jsNaN() || value
== jsUndefined()) {
125 JSRealType type
= valueRealType(exec
, value
);
126 if (hint
== QMetaType::Void
) {
129 hint
= QMetaType::Double
;
132 hint
= QMetaType::Bool
;
136 hint
= QMetaType::QString
;
139 hint
= QMetaType::QDateTime
;
142 hint
= QMetaType::QRegExp
;
145 hint
= QMetaType::QObjectStar
;
148 hint
= QMetaType::QVariantList
;
153 if (value
== jsNull()
154 && hint
!= QMetaType::QObjectStar
155 && hint
!= QMetaType::VoidStar
) {
164 case QMetaType::Bool
:
165 ret
= QVariant(value
->toBoolean(exec
));
173 case QMetaType::UInt
:
174 case QMetaType::Long
:
175 case QMetaType::ULong
:
176 case QMetaType::LongLong
:
177 case QMetaType::ULongLong
:
178 case QMetaType::Short
:
179 case QMetaType::UShort
:
180 case QMetaType::Float
:
181 case QMetaType::Double
:
182 ret
= QVariant(value
->toNumber(exec
));
183 ret
.convert((QVariant::Type
)hint
);
184 if (type
== Number
) {
186 case QMetaType::Double
:
189 case QMetaType::Float
:
192 case QMetaType::LongLong
:
193 case QMetaType::ULongLong
:
196 case QMetaType::Long
:
197 case QMetaType::ULong
:
201 case QMetaType::UInt
:
204 case QMetaType::Short
:
205 case QMetaType::UShort
:
218 case QMetaType::QChar
:
219 if (type
== Number
|| type
== Boolean
) {
220 ret
= QVariant(QChar((ushort
)value
->toNumber(exec
)));
226 UString str
= value
->toString(exec
);
227 ret
= QVariant(QChar(str
.size() ? *(const ushort
*)str
.rep()->data() : 0));
235 case QMetaType::QString
: {
236 UString ustring
= value
->toString(exec
);
237 ret
= QVariant(QString::fromUtf16((const ushort
*)ustring
.rep()->data(),ustring
.size()));
245 case QMetaType::QVariantMap
:
246 if (type
== Object
|| type
== Array
) {
247 // Enumerate the contents of the object
248 JSObject
* object
= value
->toObject(exec
);
250 PropertyNameArray properties
;
251 object
->getPropertyNames(exec
, properties
);
252 PropertyNameArray::const_iterator it
= properties
.begin();
256 while(it
!= properties
.end()) {
257 if (object
->propertyIsEnumerable(exec
, *it
)) {
258 JSValue
* val
= object
->get(exec
, *it
);
259 QVariant v
= convertValueToQVariant(exec
, val
, QMetaType::Void
, &objdist
);
261 UString ustring
= (*it
).ustring();
262 QString id
= QString::fromUtf16((const ushort
*)ustring
.rep()->data(),ustring
.size());
263 result
.insert(id
, v
);
269 ret
= QVariant(result
);
273 case QMetaType::QVariantList
:
275 JSObject
* object
= value
->toObject(exec
);
276 ArrayInstance
* array
= static_cast<ArrayInstance
*>(object
);
279 int len
= array
->getLength();
281 for (int i
= 0; i
< len
; ++i
) {
282 JSValue
*val
= array
->getItem(i
);
283 result
.append(convertValueToQVariant(exec
, val
, QMetaType::Void
, &objdist
));
285 break; // Failed converting a list entry, so fail the array
289 ret
= QVariant(result
);
292 // Make a single length array
295 result
.append(convertValueToQVariant(exec
, value
, QMetaType::Void
, &objdist
));
297 ret
= QVariant(result
);
303 case QMetaType::QStringList
: {
305 JSObject
* object
= value
->toObject(exec
);
306 ArrayInstance
* array
= static_cast<ArrayInstance
*>(object
);
309 int len
= array
->getLength();
310 for (int i
= 0; i
< len
; ++i
) {
311 JSValue
* val
= array
->getItem(i
);
312 UString ustring
= val
->toString(exec
);
313 QString qstring
= QString::fromUtf16((const ushort
*)ustring
.rep()->data(),ustring
.size());
315 result
.append(qstring
);
318 ret
= QVariant(result
);
320 // Make a single length array
321 UString ustring
= value
->toString(exec
);
322 QString qstring
= QString::fromUtf16((const ushort
*)ustring
.rep()->data(),ustring
.size());
324 result
.append(qstring
);
325 ret
= QVariant(result
);
331 case QMetaType::QByteArray
: {
332 UString ustring
= value
->toString(exec
);
333 ret
= QVariant(QString::fromUtf16((const ushort
*)ustring
.rep()->data(),ustring
.size()).toLatin1());
341 case QMetaType::QDateTime
:
342 case QMetaType::QDate
:
343 case QMetaType::QTime
:
345 JSObject
* object
= value
->toObject(exec
);
346 DateInstance
* date
= static_cast<DateInstance
*>(object
);
347 GregorianDateTime gdt
;
348 date
->getUTCTime(gdt
);
349 if (hint
== QMetaType::QDateTime
) {
350 ret
= QDateTime(QDate(gdt
.year
+ 1900, gdt
.month
+ 1, gdt
.monthDay
), QTime(gdt
.hour
, gdt
.minute
, gdt
.second
), Qt::UTC
);
352 } else if (hint
== QMetaType::QDate
) {
353 ret
= QDate(gdt
.year
+ 1900, gdt
.month
+ 1, gdt
.monthDay
);
356 ret
= QTime(gdt
.hour
+ 1900, gdt
.minute
, gdt
.second
);
359 } else if (type
== Number
) {
360 double b
= value
->toNumber(exec
);
361 GregorianDateTime gdt
;
362 msToGregorianDateTime(b
, true, gdt
);
363 if (hint
== QMetaType::QDateTime
) {
364 ret
= QDateTime(QDate(gdt
.year
+ 1900, gdt
.month
+ 1, gdt
.monthDay
), QTime(gdt
.hour
, gdt
.minute
, gdt
.second
), Qt::UTC
);
366 } else if (hint
== QMetaType::QDate
) {
367 ret
= QDate(gdt
.year
+ 1900, gdt
.month
+ 1, gdt
.monthDay
);
370 ret
= QTime(gdt
.hour
, gdt
.minute
, gdt
.second
);
373 } else if (type
== String
) {
374 UString ustring
= value
->toString(exec
);
375 QString qstring
= QString::fromUtf16((const ushort
*)ustring
.rep()->data(),ustring
.size());
377 if (hint
== QMetaType::QDateTime
) {
378 QDateTime dt
= QDateTime::fromString(qstring
, Qt::ISODate
);
380 dt
= QDateTime::fromString(qstring
, Qt::TextDate
);
382 dt
= QDateTime::fromString(qstring
, Qt::SystemLocaleDate
);
384 dt
= QDateTime::fromString(qstring
, Qt::LocaleDate
);
389 } else if (hint
== QMetaType::QDate
) {
390 QDate dt
= QDate::fromString(qstring
, Qt::ISODate
);
392 dt
= QDate::fromString(qstring
, Qt::TextDate
);
394 dt
= QDate::fromString(qstring
, Qt::SystemLocaleDate
);
396 dt
= QDate::fromString(qstring
, Qt::LocaleDate
);
402 QTime dt
= QTime::fromString(qstring
, Qt::ISODate
);
404 dt
= QTime::fromString(qstring
, Qt::TextDate
);
406 dt
= QTime::fromString(qstring
, Qt::SystemLocaleDate
);
408 dt
= QTime::fromString(qstring
, Qt::LocaleDate
);
417 case QMetaType::QRegExp
:
418 if (type
== RegExp
) {
419 /* JSObject *object = value->toObject(exec);
420 RegExpImp *re = static_cast<RegExpImp*>(object);
422 // Attempt to convert.. a bit risky
423 UString ustring
= value
->toString(exec
);
424 QString qstring
= QString::fromUtf16((const ushort
*)ustring
.rep()->data(),ustring
.size());
426 // this is of the form '/xxxxxx/i'
427 int firstSlash
= qstring
.indexOf('/');
428 int lastSlash
= qstring
.lastIndexOf('/');
429 if (firstSlash
>=0 && lastSlash
> firstSlash
) {
432 realRe
.setPattern(qstring
.mid(firstSlash
+ 1, lastSlash
- firstSlash
- 1));
434 if (qstring
.mid(lastSlash
+ 1).contains('i'))
435 realRe
.setCaseSensitivity(Qt::CaseInsensitive
);
437 ret
= qVariantFromValue(realRe
);
440 qConvDebug() << "couldn't parse a JS regexp";
442 } else if (type
== String
) {
443 UString ustring
= value
->toString(exec
);
444 QString qstring
= QString::fromUtf16((const ushort
*)ustring
.rep()->data(),ustring
.size());
448 ret
= qVariantFromValue(re
);
454 case QMetaType::QObjectStar
:
456 JSObject
* object
= value
->toObject(exec
);
457 QtInstance
* qtinst
= static_cast<QtInstance
*>(Instance::getInstance(object
, Instance::QtLanguage
));
459 if (qtinst
->getObject()) {
460 qConvDebug() << "found instance, with object:" << (void*) qtinst
->getObject();
461 ret
= qVariantFromValue(qtinst
->getObject());
465 qConvDebug() << "can't convert deleted qobject";
468 qConvDebug() << "wasn't a qtinstance";
470 } else if (type
== Null
) {
471 QObject
* nullobj
= 0;
472 ret
= qVariantFromValue(nullobj
);
475 qConvDebug() << "previous type was not an object:" << type
;
479 case QMetaType::VoidStar
:
481 JSObject
* object
= value
->toObject(exec
);
482 QtInstance
* qtinst
= static_cast<QtInstance
*>(Instance::getInstance(object
, Instance::QtLanguage
));
484 if (qtinst
->getObject()) {
485 qConvDebug() << "found instance, with object:" << (void*) qtinst
->getObject();
486 ret
= qVariantFromValue((void *)qtinst
->getObject());
490 qConvDebug() << "can't convert deleted qobject";
493 qConvDebug() << "wasn't a qtinstance";
495 } else if (type
== Null
) {
496 ret
= qVariantFromValue((void*)0);
498 } else if (type
== Number
) {
499 // I don't think that converting a double to a pointer is a wise
500 // move. Except maybe 0.
501 qConvDebug() << "got number for void * - not converting, seems unsafe:" << value
->toNumber(exec
);
503 qConvDebug() << "void* - unhandled type" << type
;
508 // Non const type ids
509 if (hint
== (QMetaType::Type
) qMetaTypeId
<QObjectList
>())
512 JSObject
* object
= value
->toObject(exec
);
513 ArrayInstance
* array
= static_cast<ArrayInstance
*>(object
);
516 int len
= array
->getLength();
517 for (int i
= 0; i
< len
; ++i
) {
518 JSValue
*val
= array
->getItem(i
);
520 QVariant item
= convertValueToQVariant(exec
, val
, QMetaType::QObjectStar
, &itemdist
);
522 result
.append(item
.value
<QObject
*>());
526 // If we didn't fail conversion
527 if (result
.count() == len
) {
529 ret
= QVariant::fromValue(result
);
531 qConvDebug() << "type conversion failed (wanted" << len
<< ", got " << result
.count() << ")";
534 // Make a single length array
537 QVariant item
= convertValueToQVariant(exec
, value
, QMetaType::QObjectStar
, &itemdist
);
539 result
.append(item
.value
<QObject
*>());
541 ret
= QVariant::fromValue(result
);
545 } else if (hint
== (QMetaType::Type
) qMetaTypeId
<QList
<int> >()) {
547 JSObject
* object
= value
->toObject(exec
);
548 ArrayInstance
* array
= static_cast<ArrayInstance
*>(object
);
551 int len
= array
->getLength();
552 for (int i
= 0; i
< len
; ++i
) {
553 JSValue
* val
= array
->getItem(i
);
555 QVariant item
= convertValueToQVariant(exec
, val
, QMetaType::Int
, &itemdist
);
557 result
.append(item
.value
<int>());
561 // If we didn't fail conversion
562 if (result
.count() == len
) {
564 ret
= QVariant::fromValue(result
);
566 qConvDebug() << "type conversion failed (wanted" << len
<< ", got " << result
.count() << ")";
569 // Make a single length array
572 QVariant item
= convertValueToQVariant(exec
, value
, QMetaType::Int
, &itemdist
);
574 result
.append(item
.value
<int>());
576 ret
= QVariant::fromValue(result
);
580 } else if (hint
== (QMetaType::Type
) qMetaTypeId
<QVariant
>()) {
581 // Well.. we can do anything... just recurse with the autodetect flag
582 ret
= convertValueToQVariant(exec
, value
, QMetaType::Void
, distance
);
599 JSValue
* convertQVariantToValue(ExecState
* exec
, PassRefPtr
<RootObject
> root
, const QVariant
& variant
)
601 // Variants with QObject * can be isNull but not a null pointer
602 // An empty QString variant is also null
603 QMetaType::Type type
= (QMetaType::Type
) variant
.userType();
604 if (variant
.isNull() &&
605 type
!= QMetaType::QObjectStar
&&
606 type
!= QMetaType::VoidStar
&&
607 type
!= QMetaType::QWidgetStar
&&
608 type
!= QMetaType::QString
) {
614 if (type
== QMetaType::Bool
)
615 return jsBoolean(variant
.toBool());
617 if (type
== QMetaType::Int
||
618 type
== QMetaType::UInt
||
619 type
== QMetaType::Long
||
620 type
== QMetaType::ULong
||
621 type
== QMetaType::LongLong
||
622 type
== QMetaType::ULongLong
||
623 type
== QMetaType::Short
||
624 type
== QMetaType::UShort
||
625 type
== QMetaType::Float
||
626 type
== QMetaType::Double
)
627 return jsNumber(variant
.toDouble());
629 if (type
== QMetaType::QRegExp
) {
630 QRegExp re
= variant
.value
<QRegExp
>();
633 RegExpObjectImp
* regExpObj
= static_cast<RegExpObjectImp
*>(exec
->lexicalGlobalObject()->regExpConstructor());
637 if (re
.caseSensitivity() == Qt::CaseInsensitive
)
638 uflags
= "i"; // ### Can't do g or m
639 UString
ustring((KJS::UChar
*)re
.pattern().utf16(), re
.pattern().length());
640 args
.append(jsString(ustring
));
641 args
.append(jsString(uflags
));
642 return regExpObj
->construct(exec
, args
);
646 if (type
== QMetaType::QDateTime
||
647 type
== QMetaType::QDate
||
648 type
== QMetaType::QTime
) {
649 DateObjectImp
*dateObj
= static_cast<DateObjectImp
*>(exec
->lexicalGlobalObject()->dateConstructor());
652 QDate date
= QDate::currentDate();
653 QTime
time(0,0,0); // midnight
655 if (type
== QMetaType::QDate
)
656 date
= variant
.value
<QDate
>();
657 else if (type
== QMetaType::QTime
)
658 time
= variant
.value
<QTime
>();
660 QDateTime dt
= variant
.value
<QDateTime
>().toLocalTime();
665 // Dates specified this way are in local time (we convert DateTimes above)
666 args
.append(jsNumber(date
.year()));
667 args
.append(jsNumber(date
.month() - 1));
668 args
.append(jsNumber(date
.day()));
669 args
.append(jsNumber(time
.hour()));
670 args
.append(jsNumber(time
.minute()));
671 args
.append(jsNumber(time
.second()));
672 args
.append(jsNumber(time
.msec()));
673 return dateObj
->construct(exec
, args
);
676 if (type
== QMetaType::QByteArray
) {
677 QByteArray ba
= variant
.value
<QByteArray
>();
678 UString
ustring(ba
.constData());
679 return jsString(ustring
);
682 if (type
== QMetaType::QObjectStar
|| type
== QMetaType::QWidgetStar
) {
683 QObject
* obj
= variant
.value
<QObject
*>();
684 return Instance::createRuntimeObject(Instance::QtLanguage
, obj
, root
);
687 if (type
== QMetaType::QVariantMap
) {
688 // create a new object, and stuff properties into it
689 JSObject
* ret
= new JSObject(exec
->lexicalGlobalObject()->objectPrototype());
690 QVariantMap map
= variant
.value
<QVariantMap
>();
691 QVariantMap::const_iterator i
= map
.constBegin();
692 while (i
!= map
.constEnd()) {
694 JSValue
* val
= convertQVariantToValue(exec
, root
, i
.value());
696 ret
->put(exec
, Identifier((const UChar
*)s
.constData(), s
.length()), val
);
705 if (type
== QMetaType::QVariantList
) {
706 QVariantList vl
= variant
.toList();
707 return new RuntimeArray(exec
, new QtArray
<QVariant
>(vl
, QMetaType::Void
, root
));
708 } else if (type
== QMetaType::QStringList
) {
709 QStringList sl
= variant
.value
<QStringList
>();
710 return new RuntimeArray(exec
, new QtArray
<QString
>(sl
, QMetaType::QString
, root
));
711 } else if (type
== (QMetaType::Type
) qMetaTypeId
<QObjectList
>()) {
712 QObjectList ol
= variant
.value
<QObjectList
>();
713 return new RuntimeArray(exec
, new QtArray
<QObject
*>(ol
, QMetaType::QObjectStar
, root
));
714 } else if (type
== (QMetaType::Type
)qMetaTypeId
<QList
<int> >()) {
715 QList
<int> il
= variant
.value
<QList
<int> >();
716 return new RuntimeArray(exec
, new QtArray
<int>(il
, QMetaType::Int
, root
));
719 if (type
== (QMetaType::Type
)qMetaTypeId
<QVariant
>()) {
720 QVariant real
= variant
.value
<QVariant
>();
721 qConvDebug() << "real variant is:" << real
;
722 return convertQVariantToValue(exec
, root
, real
);
725 qConvDebug() << "fallback path for" << variant
<< variant
.userType();
727 QString string
= variant
.toString();
728 UString
ustring((KJS::UChar
*)string
.utf16(), string
.length());
729 return jsString(ustring
);
735 #define QW_D(Class) Class##Data* d = d_func()
736 #define QW_DS(Class,Instance) Class##Data* d = Instance->d_func()
738 QtRuntimeMethod::QtRuntimeMethod(QtRuntimeMethodData
* dd
, ExecState
*exec
, const Identifier
&ident
, PassRefPtr
<QtInstance
> inst
)
739 : InternalFunctionImp (static_cast<FunctionPrototype
*>(exec
->lexicalGlobalObject()->functionPrototype()), ident
)
742 QW_D(QtRuntimeMethod
);
743 d
->m_instance
= inst
;
746 QtRuntimeMethod::~QtRuntimeMethod()
751 CodeType
QtRuntimeMethod::codeType() const
756 Completion
QtRuntimeMethod::execute(ExecState
*)
758 return Completion(Normal
, jsUndefined());
763 QtRuntimeMethodData::~QtRuntimeMethodData()
767 QtRuntimeMetaMethodData::~QtRuntimeMetaMethodData()
772 QtRuntimeConnectionMethodData::~QtRuntimeConnectionMethodData()
779 // Type conversion metadata (from QtScript originally)
780 class QtMethodMatchType
793 : m_kind(Invalid
) { }
798 QMetaType::Type
typeId() const;
801 { return (m_kind
!= Invalid
); }
803 bool isVariant() const
804 { return (m_kind
== Variant
); }
806 bool isMetaType() const
807 { return (m_kind
== MetaType
); }
809 bool isUnresolved() const
810 { return (m_kind
== Unresolved
); }
812 bool isMetaEnum() const
813 { return (m_kind
== MetaEnum
); }
815 QByteArray
name() const;
817 int enumeratorIndex() const
818 { Q_ASSERT(isMetaEnum()); return m_typeId
; }
820 static QtMethodMatchType
variant()
821 { return QtMethodMatchType(Variant
); }
823 static QtMethodMatchType
metaType(int typeId
, const QByteArray
&name
)
824 { return QtMethodMatchType(MetaType
, typeId
, name
); }
826 static QtMethodMatchType
metaEnum(int enumIndex
, const QByteArray
&name
)
827 { return QtMethodMatchType(MetaEnum
, enumIndex
, name
); }
829 static QtMethodMatchType
unresolved(const QByteArray
&name
)
830 { return QtMethodMatchType(Unresolved
, /*typeId=*/0, name
); }
833 QtMethodMatchType(Kind kind
, int typeId
= 0, const QByteArray
&name
= QByteArray())
834 : m_kind(kind
), m_typeId(typeId
), m_name(name
) { }
841 QMetaType::Type
QtMethodMatchType::typeId() const
844 return (QMetaType::Type
) QMetaType::type("QVariant");
845 return (QMetaType::Type
) (isMetaEnum() ? QMetaType::Int
: m_typeId
);
848 QByteArray
QtMethodMatchType::name() const
850 if (!m_name
.isEmpty())
852 else if (m_kind
== Variant
)
857 struct QtMethodMatchData
861 QVector
<QtMethodMatchType
> types
;
862 QVarLengthArray
<QVariant
, 10> args
;
864 QtMethodMatchData(int dist
, int idx
, QVector
<QtMethodMatchType
> typs
,
865 const QVarLengthArray
<QVariant
, 10> &as
)
866 : matchDistance(dist
), index(idx
), types(typs
), args(as
) { }
871 { return (index
!= -1); }
873 int firstUnresolvedIndex() const
875 for (int i
=0; i
< types
.count(); i
++) {
876 if (types
.at(i
).isUnresolved())
883 static int indexOfMetaEnum(const QMetaObject
*meta
, const QByteArray
&str
)
887 int scopeIdx
= str
.indexOf("::");
888 if (scopeIdx
!= -1) {
889 scope
= str
.left(scopeIdx
);
890 name
= str
.mid(scopeIdx
+ 2);
894 for (int i
= meta
->enumeratorCount() - 1; i
>= 0; --i
) {
895 QMetaEnum m
= meta
->enumerator(i
);
896 if ((m
.name() == name
)/* && (scope.isEmpty() || (m.scope() == scope))*/)
902 // Helper function for resolving methods
903 // Largely based on code in QtScript for compatibility reasons
904 static int findMethodIndex(ExecState
* exec
,
905 const QMetaObject
* meta
,
906 const QByteArray
& signature
,
909 QVarLengthArray
<QVariant
, 10> &vars
,
913 QList
<int> matchingIndices
;
915 bool overloads
= !signature
.contains('(');
917 int count
= meta
->methodCount();
918 for (int i
= count
- 1; i
>= 0; --i
) {
919 const QMetaMethod m
= meta
->method(i
);
921 // Don't choose private methods
922 if (m
.access() == QMetaMethod::Private
&& !allowPrivate
)
925 // try and find all matching named methods
926 if (m
.signature() == signature
)
927 matchingIndices
.append(i
);
928 else if (overloads
) {
929 QByteArray rawsignature
= m
.signature();
930 rawsignature
.truncate(rawsignature
.indexOf('('));
931 if (rawsignature
== signature
)
932 matchingIndices
.append(i
);
936 int chosenIndex
= -1;
938 QVector
<QtMethodMatchType
> chosenTypes
;
940 QVarLengthArray
<QVariant
, 10> args
;
941 QVector
<QtMethodMatchData
> candidates
;
942 QVector
<QtMethodMatchData
> unresolved
;
943 QVector
<int> tooFewArgs
;
944 QVector
<int> conversionFailed
;
946 foreach(int index
, matchingIndices
) {
947 QMetaMethod method
= meta
->method(index
);
949 QVector
<QtMethodMatchType
> types
;
950 bool unresolvedTypes
= false;
952 // resolve return type
953 QByteArray returnTypeName
= method
.typeName();
954 int rtype
= QMetaType::type(returnTypeName
);
955 if ((rtype
== 0) && !returnTypeName
.isEmpty()) {
956 if (returnTypeName
== "QVariant") {
957 types
.append(QtMethodMatchType::variant());
958 } else if (returnTypeName
.endsWith('*')) {
959 types
.append(QtMethodMatchType::metaType(QMetaType::VoidStar
, returnTypeName
));
961 int enumIndex
= indexOfMetaEnum(meta
, returnTypeName
);
963 types
.append(QtMethodMatchType::metaEnum(enumIndex
, returnTypeName
));
965 unresolvedTypes
= true;
966 types
.append(QtMethodMatchType::unresolved(returnTypeName
));
970 if (returnTypeName
== "QVariant")
971 types
.append(QtMethodMatchType::variant());
973 types
.append(QtMethodMatchType::metaType(rtype
, returnTypeName
));
976 // resolve argument types
977 QList
<QByteArray
> parameterTypeNames
= method
.parameterTypes();
978 for (int i
= 0; i
< parameterTypeNames
.count(); ++i
) {
979 QByteArray argTypeName
= parameterTypeNames
.at(i
);
980 int atype
= QMetaType::type(argTypeName
);
982 if (argTypeName
== "QVariant") {
983 types
.append(QtMethodMatchType::variant());
985 int enumIndex
= indexOfMetaEnum(meta
, argTypeName
);
987 types
.append(QtMethodMatchType::metaEnum(enumIndex
, argTypeName
));
989 unresolvedTypes
= true;
990 types
.append(QtMethodMatchType::unresolved(argTypeName
));
994 if (argTypeName
== "QVariant")
995 types
.append(QtMethodMatchType::variant());
997 types
.append(QtMethodMatchType::metaType(atype
, argTypeName
));
1001 if (jsArgs
.size() < (types
.count() - 1)) {
1002 qMatchDebug() << "Match:too few args for" << method
.signature();
1003 tooFewArgs
.append(index
);
1007 if (unresolvedTypes
) {
1008 qMatchDebug() << "Match:unresolved arg types for" << method
.signature();
1009 // remember it so we can give an error message later, if necessary
1010 unresolved
.append(QtMethodMatchData(/*matchDistance=*/INT_MAX
, index
,
1011 types
, QVarLengthArray
<QVariant
, 10>()));
1015 // Now convert arguments
1016 if (args
.count() != types
.count())
1017 args
.resize(types
.count());
1019 QtMethodMatchType retType
= types
[0];
1020 args
[0] = QVariant(retType
.typeId(), (void *)0); // the return value
1022 bool converted
= true;
1023 int matchDistance
= 0;
1024 for (int i
= 0; converted
&& i
< types
.count() - 1; ++i
) {
1025 JSValue
* arg
= i
< jsArgs
.size() ? jsArgs
[i
] : jsUndefined();
1027 int argdistance
= -1;
1028 QVariant v
= convertValueToQVariant(exec
, arg
, types
.at(i
+1).typeId(), &argdistance
);
1029 if (argdistance
>= 0) {
1030 matchDistance
+= argdistance
;
1033 qMatchDebug() << "failed to convert argument " << i
<< "type" << types
.at(i
+1).typeId() << QMetaType::typeName(types
.at(i
+1).typeId());
1038 qMatchDebug() << "Match: " << method
.signature() << (converted
? "converted":"failed to convert") << "distance " << matchDistance
;
1041 if ((jsArgs
.size() == types
.count() - 1)
1042 && (matchDistance
== 0)) {
1043 // perfect match, use this one
1044 chosenIndex
= index
;
1047 QtMethodMatchData
metaArgs(matchDistance
, index
, types
, args
);
1048 if (candidates
.isEmpty()) {
1049 candidates
.append(metaArgs
);
1051 QtMethodMatchData otherArgs
= candidates
.at(0);
1052 if ((args
.count() > otherArgs
.args
.count())
1053 || ((args
.count() == otherArgs
.args
.count())
1054 && (matchDistance
<= otherArgs
.matchDistance
))) {
1055 candidates
.prepend(metaArgs
);
1057 candidates
.append(metaArgs
);
1062 conversionFailed
.append(index
);
1069 if (chosenIndex
== -1 && candidates
.count() == 0) {
1070 // No valid functions at all - format an error message
1071 if (!conversionFailed
.isEmpty()) {
1072 QString message
= QString::fromLatin1("incompatible type of argument(s) in call to %0(); candidates were\n")
1073 .arg(QLatin1String(signature
));
1074 for (int i
= 0; i
< conversionFailed
.size(); ++i
) {
1076 message
+= QLatin1String("\n");
1077 QMetaMethod mtd
= meta
->method(conversionFailed
.at(i
));
1078 message
+= QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd
.signature()));
1080 *pError
= throwError(exec
, TypeError
, message
.toLatin1().constData());
1081 } else if (!unresolved
.isEmpty()) {
1082 QtMethodMatchData argsInstance
= unresolved
.first();
1083 int unresolvedIndex
= argsInstance
.firstUnresolvedIndex();
1084 Q_ASSERT(unresolvedIndex
!= -1);
1085 QtMethodMatchType unresolvedType
= argsInstance
.types
.at(unresolvedIndex
);
1086 QString message
= QString::fromLatin1("cannot call %0(): unknown type `%1'")
1087 .arg(QString::fromLatin1(signature
))
1088 .arg(QLatin1String(unresolvedType
.name()));
1089 *pError
= throwError(exec
, TypeError
, message
.toLatin1().constData());
1091 QString message
= QString::fromLatin1("too few arguments in call to %0(); candidates are\n")
1092 .arg(QLatin1String(signature
));
1093 for (int i
= 0; i
< tooFewArgs
.size(); ++i
) {
1095 message
+= QLatin1String("\n");
1096 QMetaMethod mtd
= meta
->method(tooFewArgs
.at(i
));
1097 message
+= QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd
.signature()));
1099 *pError
= throwError(exec
, SyntaxError
, message
.toLatin1().constData());
1103 if (chosenIndex
== -1 && candidates
.count() > 0) {
1104 QtMethodMatchData metaArgs
= candidates
.at(0);
1105 if ((candidates
.size() > 1)
1106 && (metaArgs
.args
.count() == candidates
.at(1).args
.count())
1107 && (metaArgs
.matchDistance
== candidates
.at(1).matchDistance
)) {
1109 QString message
= QString::fromLatin1("ambiguous call of overloaded function %0(); candidates were\n")
1110 .arg(QLatin1String(signature
));
1111 for (int i
= 0; i
< candidates
.size(); ++i
) {
1113 message
+= QLatin1String("\n");
1114 QMetaMethod mtd
= meta
->method(candidates
.at(i
).index
);
1115 message
+= QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd
.signature()));
1117 *pError
= throwError(exec
, TypeError
, message
.toLatin1().constData());
1119 chosenIndex
= metaArgs
.index
;
1120 args
= metaArgs
.args
;
1124 if (chosenIndex
!= -1) {
1125 /* Copy the stuff over */
1127 vars
.resize(args
.count());
1128 for (i
=0; i
< args
.count(); i
++) {
1130 vvars
[i
] = vars
[i
].data();
1137 // Signals are not fuzzy matched as much as methods
1138 static int findSignalIndex(const QMetaObject
* meta
, int initialIndex
, QByteArray signature
)
1140 int index
= initialIndex
;
1141 QMetaMethod method
= meta
->method(index
);
1142 bool overloads
= !signature
.contains('(');
1143 if (overloads
&& (method
.attributes() & QMetaMethod::Cloned
)) {
1144 // find the most general method
1146 method
= meta
->method(--index
);
1147 } while (method
.attributes() & QMetaMethod::Cloned
);
1152 QtRuntimeMetaMethod::QtRuntimeMetaMethod(ExecState
* exec
, const Identifier
& ident
, PassRefPtr
<QtInstance
> inst
, int index
, const QByteArray
& signature
, bool allowPrivate
)
1153 : QtRuntimeMethod (new QtRuntimeMetaMethodData(), exec
, ident
, inst
)
1155 QW_D(QtRuntimeMetaMethod
);
1156 d
->m_signature
= signature
;
1159 d
->m_disconnect
= 0;
1160 d
->m_allowPrivate
= allowPrivate
;
1163 void QtRuntimeMetaMethod::mark()
1165 QtRuntimeMethod::mark();
1166 QW_D(QtRuntimeMetaMethod
);
1168 d
->m_connect
->mark();
1169 if (d
->m_disconnect
)
1170 d
->m_disconnect
->mark();
1173 JSValue
* QtRuntimeMetaMethod::callAsFunction(ExecState
* exec
, JSObject
*, const List
& args
)
1175 QW_D(QtRuntimeMetaMethod
);
1177 // We're limited to 10 args
1178 if (args
.size() > 10)
1179 return jsUndefined();
1181 // We have to pick a method that matches..
1184 QObject
*obj
= d
->m_instance
->getObject();
1186 QVarLengthArray
<QVariant
, 10> vargs
;
1190 JSObject
* errorObj
= 0;
1191 if ((methodIndex
= findMethodIndex(exec
, obj
->metaObject(), d
->m_signature
, d
->m_allowPrivate
, args
, vargs
, (void **)qargs
, &errorObj
)) != -1) {
1192 if (obj
->qt_metacall(QMetaObject::InvokeMetaMethod
, methodIndex
, qargs
) >= 0)
1193 return jsUndefined();
1195 if (vargs
[0].isValid())
1196 return convertQVariantToValue(exec
, d
->m_instance
->rootObject(), vargs
[0]);
1202 return throwError(exec
, GeneralError
, "cannot call function of deleted QObject");
1205 // void functions return undefined
1206 return jsUndefined();
1209 bool QtRuntimeMetaMethod::getOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
1211 if (propertyName
== "connect") {
1212 slot
.setCustom(this, connectGetter
);
1214 } else if (propertyName
== "disconnect") {
1215 slot
.setCustom(this, disconnectGetter
);
1217 } else if (propertyName
== exec
->propertyNames().length
) {
1218 slot
.setCustom(this, lengthGetter
);
1222 return QtRuntimeMethod::getOwnPropertySlot(exec
, propertyName
, slot
);
1225 JSValue
*QtRuntimeMetaMethod::lengthGetter(ExecState
*, JSObject
*, const Identifier
&, const PropertySlot
&)
1227 // QtScript always returns 0
1231 JSValue
*QtRuntimeMetaMethod::connectGetter(ExecState
* exec
, JSObject
*, const Identifier
& ident
, const PropertySlot
& slot
)
1233 QtRuntimeMetaMethod
* thisObj
= static_cast<QtRuntimeMetaMethod
*>(slot
.slotBase());
1234 QW_DS(QtRuntimeMetaMethod
, thisObj
);
1237 d
->m_connect
= new QtRuntimeConnectionMethod(exec
, ident
, true, d
->m_instance
, d
->m_index
, d
->m_signature
);
1238 return d
->m_connect
;
1241 JSValue
* QtRuntimeMetaMethod::disconnectGetter(ExecState
* exec
, JSObject
*, const Identifier
& ident
, const PropertySlot
& slot
)
1243 QtRuntimeMetaMethod
* thisObj
= static_cast<QtRuntimeMetaMethod
*>(slot
.slotBase());
1244 QW_DS(QtRuntimeMetaMethod
, thisObj
);
1246 if (!d
->m_disconnect
)
1247 d
->m_disconnect
= new QtRuntimeConnectionMethod(exec
, ident
, false, d
->m_instance
, d
->m_index
, d
->m_signature
);
1248 return d
->m_disconnect
;
1253 QMultiMap
<QObject
*, QtConnectionObject
*> QtRuntimeConnectionMethod::connections
;
1255 QtRuntimeConnectionMethod::QtRuntimeConnectionMethod(ExecState
* exec
, const Identifier
& ident
, bool isConnect
, PassRefPtr
<QtInstance
> inst
, int index
, const QByteArray
& signature
)
1256 : QtRuntimeMethod (new QtRuntimeConnectionMethodData(), exec
, ident
, inst
)
1258 QW_D(QtRuntimeConnectionMethod
);
1260 d
->m_signature
= signature
;
1262 d
->m_isConnect
= isConnect
;
1265 JSValue
*QtRuntimeConnectionMethod::callAsFunction(ExecState
* exec
, JSObject
*, const List
& args
)
1267 QW_D(QtRuntimeConnectionMethod
);
1271 QObject
* sender
= d
->m_instance
->getObject();
1275 JSObject
* thisObject
= exec
->lexicalGlobalObject();
1276 JSObject
* funcObject
= 0;
1278 // QtScript checks signalness first, arguments second
1279 int signalIndex
= -1;
1281 // Make sure the initial index is a signal
1282 QMetaMethod m
= sender
->metaObject()->method(d
->m_index
);
1283 if (m
.methodType() == QMetaMethod::Signal
)
1284 signalIndex
= findSignalIndex(sender
->metaObject(), d
->m_index
, d
->m_signature
);
1286 if (signalIndex
!= -1) {
1287 if (args
.size() == 1) {
1288 funcObject
= args
[0]->toObject(exec
);
1289 if (!funcObject
->implementsCall()) {
1291 return throwError(exec
, TypeError
, "QtMetaMethod.connect: target is not a function");
1293 return throwError(exec
, TypeError
, "QtMetaMethod.disconnect: target is not a function");
1295 } else if (args
.size() >= 2) {
1296 if (args
[0]->type() == ObjectType
) {
1297 thisObject
= args
[0]->toObject(exec
);
1299 // Get the actual function to call
1300 JSObject
*asObj
= args
[1]->toObject(exec
);
1301 if (asObj
->implementsCall()) {
1305 // Convert it to a string
1306 UString funcName
= args
[1]->toString(exec
);
1307 Identifier
funcIdent(funcName
);
1310 // This is resolved at this point in QtScript
1311 JSValue
* val
= thisObject
->get(exec
, funcIdent
);
1312 JSObject
* asFuncObj
= val
->toObject(exec
);
1314 if (asFuncObj
->implementsCall()) {
1315 funcObject
= asFuncObj
;
1318 return throwError(exec
, TypeError
, "QtMetaMethod.connect: target is not a function");
1320 return throwError(exec
, TypeError
, "QtMetaMethod.disconnect: target is not a function");
1325 return throwError(exec
, TypeError
, "QtMetaMethod.connect: thisObject is not an object");
1327 return throwError(exec
, TypeError
, "QtMetaMethod.disconnect: thisObject is not an object");
1331 return throwError(exec
, GeneralError
, "QtMetaMethod.connect: no arguments given");
1333 return throwError(exec
, GeneralError
, "QtMetaMethod.disconnect: no arguments given");
1336 if (d
->m_isConnect
) {
1337 // to connect, we need:
1338 // target object [from ctor]
1339 // target signal index etc. [from ctor]
1340 // receiver function [from arguments]
1341 // receiver this object [from arguments]
1343 QtConnectionObject
* conn
= new QtConnectionObject(d
->m_instance
, signalIndex
, thisObject
, funcObject
);
1344 bool ok
= QMetaObject::connect(sender
, signalIndex
, conn
, conn
->metaObject()->methodOffset());
1347 QString msg
= QString("QtMetaMethod.connect: failed to connect to %1::%2()")
1348 .arg(sender
->metaObject()->className())
1349 .arg(QLatin1String(d
->m_signature
));
1350 return throwError(exec
, GeneralError
, msg
.toLatin1().constData());
1354 connections
.insert(sender
, conn
);
1357 // Now to find our previous connection object. Hmm.
1358 QList
<QtConnectionObject
*> conns
= connections
.values(sender
);
1361 foreach(QtConnectionObject
* conn
, conns
) {
1362 // Is this the right connection?
1363 if (conn
->match(sender
, signalIndex
, thisObject
, funcObject
)) {
1364 // Yep, disconnect it
1365 QMetaObject::disconnect(sender
, signalIndex
, conn
, conn
->metaObject()->methodOffset());
1366 delete conn
; // this will also remove it from the map
1373 QString msg
= QString("QtMetaMethod.disconnect: failed to disconnect from %1::%2()")
1374 .arg(sender
->metaObject()->className())
1375 .arg(QLatin1String(d
->m_signature
));
1376 return throwError(exec
, GeneralError
, msg
.toLatin1().constData());
1380 QString msg
= QString("QtMetaMethod.%1: %2::%3() is not a signal")
1381 .arg(d
->m_isConnect
? "connect": "disconnect")
1382 .arg(sender
->metaObject()->className())
1383 .arg(QLatin1String(d
->m_signature
));
1384 return throwError(exec
, TypeError
, msg
.toLatin1().constData());
1387 return throwError(exec
, GeneralError
, "cannot call function of deleted QObject");
1390 return jsUndefined();
1393 bool QtRuntimeConnectionMethod::getOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
1395 if (propertyName
== exec
->propertyNames().length
) {
1396 slot
.setCustom(this, lengthGetter
);
1400 return QtRuntimeMethod::getOwnPropertySlot(exec
, propertyName
, slot
);
1403 JSValue
*QtRuntimeConnectionMethod::lengthGetter(ExecState
*, JSObject
*, const Identifier
&, const PropertySlot
&)
1405 // we have one formal argument, and one optional
1411 QtConnectionObject::QtConnectionObject(PassRefPtr
<QtInstance
> instance
, int signalIndex
, JSObject
* thisObject
, JSObject
* funcObject
)
1412 : m_instance(instance
)
1413 , m_signalIndex(signalIndex
)
1414 , m_originalObject(m_instance
->getObject())
1415 , m_thisObject(thisObject
)
1416 , m_funcObject(funcObject
)
1418 setParent(m_originalObject
);
1419 ASSERT(JSLock::currentThreadIsHoldingLock()); // so our ProtectedPtrs are safe
1422 QtConnectionObject::~QtConnectionObject()
1424 // Remove us from the map of active connections
1425 QtRuntimeConnectionMethod::connections
.remove(m_originalObject
, this);
1428 static const uint qt_meta_data_QtConnectionObject
[] = {
1438 // slots: signature, parameters, type, tag, flags
1439 28, 27, 27, 27, 0x0a,
1444 static const char qt_meta_stringdata_QtConnectionObject
[] = {
1445 "KJS::Bindings::QtConnectionObject\0\0execute()\0"
1448 const QMetaObject
QtConnectionObject::staticMetaObject
= {
1449 { &QObject::staticMetaObject
, qt_meta_stringdata_QtConnectionObject
,
1450 qt_meta_data_QtConnectionObject
, 0 }
1453 const QMetaObject
*QtConnectionObject::metaObject() const
1455 return &staticMetaObject
;
1458 void *QtConnectionObject::qt_metacast(const char *_clname
)
1460 if (!_clname
) return 0;
1461 if (!strcmp(_clname
, qt_meta_stringdata_QtConnectionObject
))
1462 return static_cast<void*>(const_cast<QtConnectionObject
*>(this));
1463 return QObject::qt_metacast(_clname
);
1466 int QtConnectionObject::qt_metacall(QMetaObject::Call _c
, int _id
, void **_a
)
1468 _id
= QObject::qt_metacall(_c
, _id
, _a
);
1471 if (_c
== QMetaObject::InvokeMetaMethod
) {
1473 case 0: execute(_a
); break;
1480 void QtConnectionObject::execute(void **argv
)
1482 QObject
* obj
= m_instance
->getObject();
1484 const QMetaObject
* meta
= obj
->metaObject();
1485 const QMetaMethod method
= meta
->method(m_signalIndex
);
1487 QList
<QByteArray
> parameterTypes
= method
.parameterTypes();
1489 int argc
= parameterTypes
.count();
1493 // ### Should the Interpreter/ExecState come from somewhere else?
1494 RefPtr
<RootObject
> ro
= m_instance
->rootObject();
1496 JSGlobalObject
* globalobj
= ro
->globalObject();
1498 ExecState
* exec
= globalobj
->globalExec();
1500 // Build the argument list (up to the formal argument length of the slot)
1502 // ### DropAllLocks?
1503 int funcArgC
= m_funcObject
->get(exec
, exec
->propertyNames().length
)->toInt32(exec
);
1504 int argTotal
= qMax(funcArgC
, argc
);
1505 for(int i
=0; i
< argTotal
; i
++) {
1507 int argType
= QMetaType::type(parameterTypes
.at(i
));
1508 l
.append(convertQVariantToValue(exec
, ro
, QVariant(argType
, argv
[i
+1])));
1510 l
.append(jsUndefined());
1513 // Stuff in the __qt_sender property, if we can
1514 if (m_funcObject
->inherits(&FunctionImp::info
)) {
1515 FunctionImp
* fimp
= static_cast<FunctionImp
*>(m_funcObject
.get());
1517 JSObject
* qt_sender
= Instance::createRuntimeObject(Instance::QtLanguage
, sender(), ro
);
1518 JSObject
* wrapper
= new JSObject();
1519 wrapper
->put(exec
, "__qt_sender__", qt_sender
);
1520 ScopeChain oldsc
= fimp
->scope();
1521 ScopeChain sc
= oldsc
;
1524 fimp
->call(exec
, m_thisObject
, l
);
1525 fimp
->setScope(oldsc
);
1527 m_funcObject
->call(exec
, m_thisObject
, l
);
1532 // A strange place to be - a deleted object emitted a signal here.
1533 qWarning() << "sender deleted, cannot deliver signal";
1537 bool QtConnectionObject::match(QObject
* sender
, int signalIndex
, JSObject
* thisObject
, JSObject
*funcObject
)
1539 if (m_originalObject
== sender
&& m_signalIndex
== signalIndex
1540 && thisObject
== (JSObject
*)m_thisObject
&& funcObject
== (JSObject
*)m_funcObject
)
1547 template <typename T
> QtArray
<T
>::QtArray(QList
<T
> list
, QMetaType::Type type
, PassRefPtr
<RootObject
> rootObject
)
1552 m_length
= m_list
.count();
1555 template <typename T
> QtArray
<T
>::~QtArray ()
1559 template <typename T
> RootObject
* QtArray
<T
>::rootObject() const
1561 return _rootObject
&& _rootObject
->isValid() ? _rootObject
.get() : 0;
1564 template <typename T
> void QtArray
<T
>::setValueAt(ExecState
*exec
, unsigned int index
, JSValue
*aValue
) const
1566 // QtScript sets the value, but doesn't forward it to the original source
1567 // (e.g. if you do 'object.intList[5] = 6', the object is not updated, but the
1568 // copy of the list is).
1570 QVariant val
= convertValueToQVariant(exec
, aValue
, m_type
, &dist
);
1573 m_list
[index
] = val
.value
<T
>();
1578 template <typename T
> JSValue
* QtArray
<T
>::valueAt(ExecState
*exec
, unsigned int index
) const
1580 if (index
< m_length
) {
1581 T val
= m_list
.at(index
);
1582 return convertQVariantToValue(exec
, rootObject(), QVariant::fromValue(val
));
1585 return jsUndefined();