/* * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "config.h" #include "qt_runtime.h" #include "APICast.h" #include "BooleanObject.h" #include "DateInstance.h" #include "DatePrototype.h" #include "FunctionPrototype.h" #include "Interpreter.h" #include "JSArray.h" #include "JSContextRefPrivate.h" #include "JSDOMBinding.h" #include "JSDOMWindow.h" #include "JSDocument.h" #include "JSGlobalObject.h" #include "JSHTMLElement.h" #include "JSLock.h" #include "JSObject.h" #include "JSRetainPtr.h" #include "JSUint8ClampedArray.h" #include "ObjectPrototype.h" #include "PropertyNameArray.h" #include "qdatetime.h" #include "qdebug.h" #include "qmetaobject.h" #include "qmetatype.h" #include "qobject.h" #include "qstringlist.h" #include "qt_instance.h" #include "qt_pixmapruntime.h" #include "qvarlengtharray.h" #include #include #include #include #include #include // QtScript has these Q_DECLARE_METATYPE(QObjectList); Q_DECLARE_METATYPE(QList); Q_DECLARE_METATYPE(QVariant); using namespace WebCore; namespace JSC { namespace Bindings { // Debugging //#define QTWK_RUNTIME_CONVERSION_DEBUG //#define QTWK_RUNTIME_MATCH_DEBUG class QWKNoDebug { public: inline QWKNoDebug(){} inline ~QWKNoDebug(){} template inline QWKNoDebug &operator<<(const T &) { return *this; } }; #ifdef QTWK_RUNTIME_CONVERSION_DEBUG #define qConvDebug() qDebug() #else #define qConvDebug() QWKNoDebug() #endif #ifdef QTWK_RUNTIME_MATCH_DEBUG #define qMatchDebug() qDebug() #else #define qMatchDebug() QWKNoDebug() #endif typedef enum { Variant = 0, Number, Boolean, RTString, Date, Array, QObj, Object, Null, RTUint8Array, RTUint8ClampedArray } JSRealType; #if defined(QTWK_RUNTIME_CONVERSION_DEBUG) || defined(QTWK_RUNTIME_MATCH_DEBUG) QDebug operator<<(QDebug dbg, const JSRealType &c) { const char *map[] = { "Variant", "Number", "Boolean", "RTString", "Date", "Array", "RTObject", "Object", "Null", "RTUint8Array", "RTUint8ClampedArray"}; dbg.nospace() << "JSType(" << ((int)c) << ", " << map[c] << ")"; return dbg.space(); } #endif void setException(JSContextRef context, JSValueRef* exception, const QString& text) { if (!exception) return; JSStringRef errorStr = JSStringCreateWithUTF8CString(text.toUtf8()); JSValueRef errorVal[] = { JSValueMakeString(context, errorStr) }; *exception = JSObjectMakeError(context, 1, errorVal, 0); JSStringRelease(errorStr); } struct RuntimeConversion { ConvertToJSValueFunction toJSValueFunc; ConvertToVariantFunction toVariantFunc; }; typedef QHash RuntimeConversionTable; Q_GLOBAL_STATIC(RuntimeConversionTable, customRuntimeConversions) void registerCustomType(int qtMetaTypeId, ConvertToVariantFunction toVariantFunc, ConvertToJSValueFunction toJSValueFunc) { RuntimeConversion conversion; conversion.toJSValueFunc = toJSValueFunc; conversion.toVariantFunc = toVariantFunc; customRuntimeConversions()->insert(qtMetaTypeId, conversion); } static bool isJSUint8Array(JSObjectRef object) { return toJS(object)->inherits(JSUint8Array::info()); } static bool isJSUint8ClampedArray(JSObjectRef object) { return toJS(object)->inherits(JSUint8ClampedArray::info()); } static bool isJSArray(JSObjectRef object) { return toJS(object)->inherits(JSArray::info()); } static bool isJSDate(JSObjectRef object) { return toJS(object)->inherits(DateInstance::info()); } static bool isQtObject(JSObjectRef object) { return toJS(object)->inherits(RuntimeObject::info()); } static JSRealType valueRealType(JSContextRef context, JSValueRef value, JSValueRef* exception) { if (JSValueIsNumber(context, value)) return Number; if (JSValueIsString(context, value)) return RTString; if (JSValueIsBoolean(context, value)) return Boolean; if (JSValueIsNull(context, value)) return Null; if (!JSValueIsObject(context, value)) return RTString; // I don't know. JSObjectRef object = JSValueToObject(context, value, exception); if (isJSUint8Array(object)) return RTUint8Array; if (isJSUint8ClampedArray(object)) return RTUint8ClampedArray; if (isJSArray(object)) return Array; if (isJSDate(object)) return Date; if (isQtObject(object)) return QObj; return Object; } static QString toString(JSStringRef stringRef) { return QString(reinterpret_cast(JSStringGetCharactersPtr(stringRef)), JSStringGetLength(stringRef)); } static JSValueRef unwrapBoxedPrimitive(JSContextRef context, JSValueRef value, JSObjectRef obj) { ExecState* exec = toJS(context); JSLockHolder locker(exec); JSObject* object = toJS(obj); if (object->inherits(NumberObject::info())) return toRef(exec, jsNumber(object->toNumber(exec))); if (object->inherits(StringObject::info())) return toRef(exec, object->toString(exec)); if (object->inherits(BooleanObject::info())) return toRef(exec, object->toPrimitive(exec)); return value; } QVariant convertValueToQVariant(JSContextRef, JSValueRef, QMetaType::Type, int*, HashSet*, int, JSValueRef *exception); static QVariantMap convertValueToQVariantMap(JSContextRef context, JSObjectRef object, HashSet* visitedObjects, int recursionLimit, JSValueRef* exception) { QVariantMap result; JSPropertyNameArrayRef properties = JSObjectCopyPropertyNames(context, object); size_t propertyCount = JSPropertyNameArrayGetCount(properties); for (size_t i = 0; i < propertyCount; ++i) { JSStringRef name = JSPropertyNameArrayGetNameAtIndex(properties, i); int propertyConversionDistance = 0; JSValueRef property = JSObjectGetProperty(context, object, name, exception); QVariant v = convertValueToQVariant(context, property, QMetaType::Void, &propertyConversionDistance, visitedObjects, recursionLimit, exception); if (exception && *exception) *exception = 0; else if (propertyConversionDistance >= 0) { result.insert(toString(name), v); } } JSPropertyNameArrayRelease(properties); return result; } template QList convertToList(JSContextRef context, JSRealType type, JSObjectRef object, JSValueRef value, int* distance, HashSet* visitedObjects, int recursionLimit, JSValueRef* exception, const QMetaType::Type typeId = static_cast(qMetaTypeId())) { QList list; if (type == Array) { static JSStringRef lengthStr = JSStringCreateWithUTF8CString("length"); JSValueRef lengthVal = JSObjectGetProperty(context, object, lengthStr, exception); size_t length = JSValueToNumber(context, lengthVal, exception); list.reserve(length); for (size_t i = 0; i < length; ++i) { JSValueRef value = JSObjectGetPropertyAtIndex(context, object, i, exception); int itemDistance = -1; QVariant variant = convertValueToQVariant(context, value, typeId, &itemDistance, visitedObjects, recursionLimit, exception); if (itemDistance >= 0) list << variant.value(); else break; } if (list.count() != length) list.clear(); else if (distance) *distance = 5; } else { int itemDistance = -1; QVariant variant = convertValueToQVariant(context, value, typeId, &itemDistance, visitedObjects, recursionLimit, exception); if (itemDistance >= 0) { list << variant.value(); if (distance) *distance = 10; } } return list; } static QString toQString(JSContextRef context, JSValueRef value) { JSRetainPtr string(Adopt, JSValueToStringCopy(context, value, 0)); if (!string) return QString(); return QString(reinterpret_cast(JSStringGetCharactersPtr(string.get())), JSStringGetLength(string.get())); } static void getGregorianDateTimeUTC(JSContextRef context, JSRealType type, JSValueRef value, JSObjectRef object, JSValueRef* exception, GregorianDateTime* gdt) { ExecState* exec = toJS(context); JSLockHolder locker(exec); if (type == Date) { JSObject* jsObject = toJS(object); DateInstance* date = asDateInstance(jsObject); gdt->copyFrom(*date->gregorianDateTimeUTC(exec)); } else { double ms = JSValueToNumber(context, value, exception); GregorianDateTime convertedGdt; msToGregorianDateTime(exec->vm(), ms, WTF::UTCTime, convertedGdt); gdt->copyFrom(convertedGdt); } } static QDateTime toQDateTimeUTC(JSContextRef context, JSRealType type, JSValueRef value, JSObjectRef object, JSValueRef* exception) { GregorianDateTime gdt; getGregorianDateTimeUTC(context, type, value, object, exception, &gdt); QDate date(gdt.year(), gdt.month() + 1, gdt.monthDay()); QTime time(gdt.hour(), gdt.minute(), gdt.second()); return QDateTime(date, time, Qt::UTC); } QVariant convertValueToQVariant(JSContextRef context, JSValueRef value, QMetaType::Type hint, int *distance, HashSet* visitedObjects, int recursionLimit, JSValueRef* exception) { --recursionLimit; if (!value || !recursionLimit) return QVariant(); JSObjectRef object = 0; if (JSValueIsObject(context, value)) { object = JSValueToObject(context, value, 0); if (visitedObjects->contains(object)) return QVariant(); visitedObjects->add(object); value = unwrapBoxedPrimitive(context, value, object); } // check magic pointer values before dereferencing value if (JSValueIsNumber(context, value) && std::isnan(JSValueToNumber(context, value, exception))) { if (distance) *distance = -1; return QVariant(); } if (JSValueIsUndefined(context, value) && hint != QMetaType::QString && hint != (QMetaType::Type) qMetaTypeId()) { if (distance) *distance = -1; return QVariant(); } JSRealType type = valueRealType(context, value, exception); if (hint == QMetaType::Void) { switch(type) { case Number: hint = QMetaType::Double; break; case Boolean: hint = QMetaType::Bool; break; case RTString: default: hint = QMetaType::QString; break; case Date: hint = QMetaType::QDateTime; break; case Object: hint = QMetaType::QVariantMap; break; case QObj: hint = QMetaType::QObjectStar; break; case RTUint8Array: case RTUint8ClampedArray: hint = QMetaType::QByteArray; break; case Array: hint = QMetaType::QVariantList; break; } } qConvDebug() << "convertValueToQVariant: jstype is " << type << ", hint is" << hint; if (JSValueIsNull(context, value) && hint != QMetaType::QObjectStar && hint != QMetaType::VoidStar && hint != QMetaType::QString && hint != (QMetaType::Type) qMetaTypeId()) { if (distance) *distance = -1; return QVariant(); } QVariant ret; int dist = -1; switch (hint) { case QMetaType::Bool: ret = QVariant(JSValueToBoolean(context, value)); if (type == Boolean) dist = 0; else dist = 10; break; case QMetaType::Int: case QMetaType::UInt: case QMetaType::Long: case QMetaType::ULong: case QMetaType::LongLong: case QMetaType::ULongLong: case QMetaType::Short: case QMetaType::UShort: case QMetaType::Float: case QMetaType::Double: ret = QVariant(JSValueToNumber(context, value, 0)); ret.convert((QVariant::Type)hint); if (type == Number) { switch (hint) { case QMetaType::Double: dist = 0; break; case QMetaType::Float: dist = 1; break; case QMetaType::LongLong: case QMetaType::ULongLong: dist = 2; break; case QMetaType::Long: case QMetaType::ULong: dist = 3; break; case QMetaType::Int: case QMetaType::UInt: dist = 4; break; case QMetaType::Short: case QMetaType::UShort: dist = 5; break; break; default: dist = 10; break; } } else { dist = 10; } break; case QMetaType::QChar: if (type == Number || type == Boolean) { ret = QVariant(QChar((ushort)JSValueToNumber(context, value, 0))); if (type == Boolean) dist = 3; else dist = 6; } else { JSRetainPtr str(Adopt, JSValueToStringCopy(context, value, exception)); QChar ch; if (str && JSStringGetLength(str.get()) > 0) ch = *reinterpret_cast(JSStringGetCharactersPtr(str.get())); ret = QVariant(ch); if (type == RTString) dist = 3; else dist = 10; } break; case QMetaType::QString: { if (JSValueIsNull(context, value) || JSValueIsUndefined(context, value)) { if (distance) *distance = 1; return QString(); } JSRetainPtr str(Adopt, JSValueToStringCopy(context, value, exception)); if (str) { QString string(reinterpret_cast(JSStringGetCharactersPtr(str.get())), JSStringGetLength(str.get())); ret = QVariant(string); if (type == RTString) dist = 0; else dist = 10; } break; } case QMetaType::QVariantMap: if (type == Object || type == Array) { ret = QVariant(convertValueToQVariantMap(context, object, visitedObjects, recursionLimit, exception)); // Those types can still have perfect matches, e.g. 'bool' if value is a Boolean Object. dist = 1; } break; case QMetaType::QVariantList: ret = QVariant(convertToList(context, type, object, value, &dist, visitedObjects, recursionLimit, exception, QMetaType::Void)); break; case QMetaType::QStringList: { ret = QVariant(convertToList(context, type, object, value, &dist, visitedObjects, recursionLimit, exception)); break; } case QMetaType::QByteArray: { if (type == RTUint8Array) { RefPtr arr = toUint8Array(toJS(toJS(context), value)); ret = QVariant(QByteArray(reinterpret_cast(arr->data()), arr->length())); dist = 0; } else if (type == RTUint8ClampedArray) { RefPtr arr = toUint8ClampedArray(toJS(toJS(context), value)); ret = QVariant(QByteArray(reinterpret_cast(arr->data()), arr->length())); dist = 0; } else { ret = QVariant(toQString(context, value).toLatin1()); if (type == RTString) dist = 5; else dist = 10; } break; } case QMetaType::QDateTime: case QMetaType::QDate: case QMetaType::QTime: if (type == Date || type == Number) { QDateTime dt = toQDateTimeUTC(context, type, value, object, exception); const bool isNumber = (type == Number); if (hint == QMetaType::QDateTime) { ret = dt; dist = isNumber ? 6 : 0; } else if (hint == QMetaType::QDate) { ret = dt.date(); dist = isNumber ? 8 : 1; } else { ret = dt.time(); dist = isNumber ? 10 : 2; } } else if (type == RTString) { QString qstring = toQString(context, value); if (hint == QMetaType::QDateTime) { QDateTime dt = QDateTime::fromString(qstring, Qt::ISODate); if (!dt.isValid()) dt = QDateTime::fromString(qstring, Qt::TextDate); if (!dt.isValid()) dt = QDateTime::fromString(qstring, Qt::SystemLocaleDate); if (!dt.isValid()) dt = QDateTime::fromString(qstring, Qt::LocaleDate); if (dt.isValid()) { ret = dt; dist = 2; } } else if (hint == QMetaType::QDate) { QDate dt = QDate::fromString(qstring, Qt::ISODate); if (!dt.isValid()) dt = QDate::fromString(qstring, Qt::TextDate); if (!dt.isValid()) dt = QDate::fromString(qstring, Qt::SystemLocaleDate); if (!dt.isValid()) dt = QDate::fromString(qstring, Qt::LocaleDate); if (dt.isValid()) { ret = dt; dist = 3; } } else { QTime dt = QTime::fromString(qstring, Qt::ISODate); if (!dt.isValid()) dt = QTime::fromString(qstring, Qt::TextDate); if (!dt.isValid()) dt = QTime::fromString(qstring, Qt::SystemLocaleDate); if (!dt.isValid()) dt = QTime::fromString(qstring, Qt::LocaleDate); if (dt.isValid()) { ret = dt; dist = 3; } } } break; case QMetaType::QObjectStar: if (type == QObj) { QtInstance* qtinst = QtInstance::getInstance(toJS(object)); if (qtinst) { if (qtinst->getObject()) { qConvDebug() << "found instance, with object:" << (void*) qtinst->getObject(); ret = QVariant::fromValue(qtinst->getObject()); qConvDebug() << ret; dist = 0; } else { qConvDebug() << "can't convert deleted qobject"; } } else { qConvDebug() << "wasn't a qtinstance"; } } else if (type == Null) { QObject* nullobj = 0; ret = QVariant::fromValue(nullobj); dist = 0; } else { qConvDebug() << "previous type was not an object:" << type; } break; case QMetaType::VoidStar: if (type == QObj) { QtInstance* qtinst = QtInstance::getInstance(toJS(object)); if (qtinst) { if (qtinst->getObject()) { qConvDebug() << "found instance, with object:" << (void*) qtinst->getObject(); ret = QVariant::fromValue((void *)qtinst->getObject()); qConvDebug() << ret; dist = 0; } else { qConvDebug() << "can't convert deleted qobject"; } } else { qConvDebug() << "wasn't a qtinstance"; } } else if (type == Null) { ret = QVariant::fromValue((void*)0); dist = 0; } else if (type == Number) { // I don't think that converting a double to a pointer is a wise // move. Except maybe 0. qConvDebug() << "got number for void * - not converting, seems unsafe:" << JSValueToNumber(context, value, 0); } else { qConvDebug() << "void* - unhandled type" << type; } break; default: // Non const type ids if (hint == (QMetaType::Type) qMetaTypeId()) { ret = QVariant::fromValue(convertToList(context, type, object, value, &dist, visitedObjects, recursionLimit, exception)); break; } if (hint == (QMetaType::Type) qMetaTypeId >()) { ret = QVariant::fromValue(convertToList(context, type, object, value, &dist, visitedObjects, recursionLimit, exception)); break; } if (QtPixmapRuntime::canHandle(static_cast(hint))) { ret = QtPixmapRuntime::toQt(context, object, static_cast(hint), exception); } else if (customRuntimeConversions()->contains(hint)) { ret = customRuntimeConversions()->value(hint).toVariantFunc(toJS(object), &dist, visitedObjects); if (dist == 0) break; } else if (hint == (QMetaType::Type) qMetaTypeId()) { if (JSValueIsNull(context, value) || JSValueIsUndefined(context, value)) { if (distance) *distance = 1; return QVariant(); } if (JSValueIsObject(context, value)) { // Since we haven't really visited this object yet, we remove it visitedObjects->remove(object); } // And then recurse with the autodetect flag ret = convertValueToQVariant(context, value, QMetaType::Void, distance, visitedObjects, recursionLimit, exception); dist = 10; break; } dist = 10; break; } if (!ret.isValid()) dist = -1; if (distance) *distance = dist; return ret; } QVariant convertValueToQVariant(JSContextRef context, JSValueRef value, QMetaType::Type hint, int *distance, JSValueRef *exception) { const int recursionLimit = 200; HashSet visitedObjects; return convertValueToQVariant(context, value, hint, distance, &visitedObjects, recursionLimit, exception); } JSValueRef convertQVariantToValue(JSContextRef context, PassRefPtr root, const QVariant& variant, JSValueRef *exception) { // Variants with QObject * can be isNull but not a null pointer // An empty QString variant is also null QMetaType::Type type = (QMetaType::Type) variant.userType(); qConvDebug() << "convertQVariantToValue: metatype:" << type << ", isnull: " << variant.isNull(); if (variant.isNull() && !QMetaType::typeFlags(type).testFlag(QMetaType::PointerToQObject) && type != QMetaType::VoidStar && type != QMetaType::QString) { return JSValueMakeNull(context); } if (type == QMetaType::Bool) return JSValueMakeBoolean(context, variant.toBool()); if (type == QMetaType::Int || type == QMetaType::UInt || type == QMetaType::Long || type == QMetaType::ULong || type == QMetaType::LongLong || type == QMetaType::ULongLong || type == QMetaType::Short || type == QMetaType::UShort || type == QMetaType::Float || type == QMetaType::Double) return JSValueMakeNumber(context, variant.toDouble()); if (type == QMetaType::QDateTime || type == QMetaType::QDate || type == QMetaType::QTime) { QDate date = QDate::currentDate(); QTime time(0,0,0); // midnight if (type == QMetaType::QDate) date = variant.value(); else if (type == QMetaType::QTime) time = variant.value(); else { QDateTime dt = variant.value().toLocalTime(); date = dt.date(); time = dt.time(); } // Dates specified this way are in local time (we convert DateTimes above) const JSValueRef arguments[] = { JSValueMakeNumber(context, date.year()), JSValueMakeNumber(context, date.month() - 1), JSValueMakeNumber(context, date.day()), JSValueMakeNumber(context, time.hour()), JSValueMakeNumber(context, time.minute()), JSValueMakeNumber(context, time.second()), JSValueMakeNumber(context, time.msec()) }; return JSObjectMakeDate(context, 7, arguments, exception); } if (type == QMetaType::QByteArray) { QByteArray qtByteArray = variant.value(); WTF::RefPtr wtfByteArray = JSC::Uint8ClampedArray::createUninitialized(qtByteArray.length()); memcpy(wtfByteArray->data(), qtByteArray.constData(), qtByteArray.length()); ExecState* exec = toJS(context); JSLockHolder locker(exec); return toRef(exec, toJS(exec, static_cast(exec->lexicalGlobalObject()), wtfByteArray.get())); } if (QMetaType::typeFlags(type).testFlag(QMetaType::PointerToQObject)) { QObject* obj = variant.value(); if (!obj) return JSValueMakeNull(context); ExecState* exec = toJS(context); JSLockHolder locker(exec); return toRef(exec, QtInstance::getQtInstance(obj, root, QtInstance::QtOwnership)->createRuntimeObject(exec)); } if (QtPixmapRuntime::canHandle(static_cast(variant.type()))) return QtPixmapRuntime::toJS(context, variant, exception); if (customRuntimeConversions()->contains(type)) { if (!root->globalObject()->inherits(JSDOMWindow::info())) return JSValueMakeUndefined(context); Document* document = JSDOMWindow::toWrapped(root->globalObject())->document(); if (!document) return JSValueMakeUndefined(context); ExecState* exec = toJS(context); JSLockHolder locker(exec); return toRef(exec, customRuntimeConversions()->value(type).toJSValueFunc(exec, toJSDOMGlobalObject(document, exec), variant)); } if (type == QMetaType::QVariantMap) { // create a new object, and stuff properties into it JSObjectRef ret = JSObjectMake(context, 0, 0); QVariantMap map = variant.value(); QVariantMap::const_iterator i = map.constBegin(); while (i != map.constEnd()) { QString s = i.key(); JSStringRef propertyName = JSStringCreateWithCharacters(reinterpret_cast(s.constData()), s.length()); JSValueRef propertyValue = convertQVariantToValue(context, root.get(), i.value(), /*ignored exception*/0); if (propertyValue) JSObjectSetProperty(context, ret, propertyName, propertyValue, kJSPropertyAttributeNone, /*ignored exception*/0); JSStringRelease(propertyName); ++i; } return ret; } // List types if (type == QMetaType::QVariantList) { // ### TODO: Could use special array class that lazily converts. // See https://bugs.webkit.org/show_bug.cgi?id=94691 QVariantList vl = variant.toList(); JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception); if (exception && *exception) return array; for (int i = 0; i < vl.count(); ++i) { JSValueRef property = convertQVariantToValue(context, root.get(), vl.at(i), /*ignored exception*/0); if (property) JSObjectSetPropertyAtIndex(context, array, i, property, /*ignored exception*/0); } return array; } else if (type == QMetaType::QStringList) { QStringList sl = variant.value(); JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception); for (int i = 0; i < sl.count(); ++i) { const QString& s = sl.at(i); JSStringRef jsString = JSStringCreateWithCharacters(reinterpret_cast(s.constData()), s.length()); JSObjectSetPropertyAtIndex(context, array, i, JSValueMakeString(context, jsString), /*ignored exception*/0); JSStringRelease(jsString); } return array; } else if (type == static_cast(qMetaTypeId())) { QObjectList ol = variant.value(); JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception); RefPtr rootRef(root); // We need a real reference, since PassRefPtr may only be passed on to one call. ExecState* exec = toJS(context); JSLockHolder locker(exec); for (int i = 0; i < ol.count(); ++i) { JSValueRef jsObject = toRef(exec, QtInstance::getQtInstance(ol.at(i), rootRef, QtInstance::QtOwnership)->createRuntimeObject(exec)); JSObjectSetPropertyAtIndex(context, array, i, jsObject, /*ignored exception*/0); } return array; } else if (type == static_cast(qMetaTypeId >())) { QList il = variant.value >(); JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception); for (int i = 0; i < il.count(); ++i) JSObjectSetPropertyAtIndex(context, array, i, JSValueMakeNumber(context, il.at(i)), /*ignored exception*/0); return array; } if (type == (QMetaType::Type)qMetaTypeId()) { QVariant real = variant.value(); qConvDebug() << "real variant is:" << real; return convertQVariantToValue(context, root.get(), real, exception); } qConvDebug() << "fallback path for" << variant << variant.userType(); QString string = variant.toString(); JSStringRef jsstring = JSStringCreateWithCharacters(reinterpret_cast(string.constData()), string.length()); JSValueRef value = JSValueMakeString(context, jsstring); JSStringRelease(jsstring); return value; } // Type conversion metadata (from QtScript originally) class QtMethodMatchType { public: enum Kind { Invalid, Variant, MetaType, Unresolved, MetaEnum }; QtMethodMatchType() : m_kind(Invalid) { } Kind kind() const { return m_kind; } QMetaType::Type typeId() const; bool isValid() const { return (m_kind != Invalid); } bool isVariant() const { return (m_kind == Variant); } bool isMetaType() const { return (m_kind == MetaType); } bool isUnresolved() const { return (m_kind == Unresolved); } bool isMetaEnum() const { return (m_kind == MetaEnum); } QByteArray name() const; int enumeratorIndex() const { Q_ASSERT(isMetaEnum()); return m_typeId; } static QtMethodMatchType variant() { return QtMethodMatchType(Variant); } static QtMethodMatchType metaType(int typeId, const QByteArray &name) { return QtMethodMatchType(MetaType, typeId, name); } static QtMethodMatchType metaEnum(int enumIndex, const QByteArray &name) { return QtMethodMatchType(MetaEnum, enumIndex, name); } static QtMethodMatchType unresolved(const QByteArray &name) { return QtMethodMatchType(Unresolved, /*typeId=*/0, name); } private: QtMethodMatchType(Kind kind, int typeId = 0, const QByteArray &name = QByteArray()) : m_kind(kind), m_typeId(typeId), m_name(name) { } Kind m_kind; int m_typeId; QByteArray m_name; }; QMetaType::Type QtMethodMatchType::typeId() const { if (isVariant()) return (QMetaType::Type) qMetaTypeId(); return (QMetaType::Type) (isMetaEnum() ? QMetaType::Int : m_typeId); } QByteArray QtMethodMatchType::name() const { if (!m_name.isEmpty()) return m_name; else if (m_kind == Variant) return "QVariant"; return QByteArray(); } struct QtMethodMatchData { int matchDistance; int index; QVector types; QVarLengthArray args; QtMethodMatchData(int dist, int idx, QVector typs, const QVarLengthArray &as) : matchDistance(dist), index(idx), types(typs), args(as) { } QtMethodMatchData() : index(-1) { } bool isValid() const { return (index != -1); } int firstUnresolvedIndex() const { for (int i=0; i < types.count(); i++) { if (types.at(i).isUnresolved()) return i; } return -1; } }; static int indexOfMetaEnum(const QMetaObject *meta, const QByteArray &str) { QByteArray scope; QByteArray name; int scopeIdx = str.indexOf("::"); if (scopeIdx != -1) { scope = str.left(scopeIdx); name = str.mid(scopeIdx + 2); } else { name = str; } for (int i = meta->enumeratorCount() - 1; i >= 0; --i) { QMetaEnum m = meta->enumerator(i); if ((m.name() == name)/* && (scope.isEmpty() || (m.scope() == scope))*/) return i; } return -1; } // Helper function for resolving methods // Largely based on code in QtScript for compatibility reasons static int findMethodIndex(JSContextRef context, const QMetaObject* meta, const QByteArray& signature, int argumentCount, const JSValueRef arguments[], bool allowPrivate, QVarLengthArray &vars, void** vvars, JSValueRef* exception) { QList matchingIndices; bool overloads = !signature.contains('('); int count = meta->methodCount(); for (int i = count - 1; i >= 0; --i) { const QMetaMethod m = meta->method(i); // Don't choose private methods if (m.access() == QMetaMethod::Private && !allowPrivate) continue; // try and find all matching named methods if (!overloads && m.methodSignature() == signature) matchingIndices.append(i); else if (overloads && m.name() == signature) matchingIndices.append(i); } int chosenIndex = -1; QVector chosenTypes; QVarLengthArray args; QVector candidates; QVector unresolved; QVector tooFewArgs; QVector conversionFailed; foreach(int index, matchingIndices) { QMetaMethod method = meta->method(index); QVector types; bool unresolvedTypes = false; // resolve return type QByteArray returnTypeName = method.typeName(); int rtype = method.returnType(); if (rtype == QMetaType::UnknownType) { if (returnTypeName.endsWith('*')) { types.append(QtMethodMatchType::metaType(QMetaType::VoidStar, returnTypeName)); } else { int enumIndex = indexOfMetaEnum(meta, returnTypeName); if (enumIndex != -1) types.append(QtMethodMatchType::metaEnum(enumIndex, returnTypeName)); else { unresolvedTypes = true; types.append(QtMethodMatchType::unresolved(returnTypeName)); } } } else { if (rtype == QMetaType::QVariant) types.append(QtMethodMatchType::variant()); else types.append(QtMethodMatchType::metaType(rtype, returnTypeName)); } // resolve argument types QList parameterTypeNames = method.parameterTypes(); for (int i = 0; i < parameterTypeNames.count(); ++i) { QByteArray argTypeName = parameterTypeNames.at(i); int atype = method.parameterType(i); if (atype == QMetaType::UnknownType) { int enumIndex = indexOfMetaEnum(meta, argTypeName); if (enumIndex != -1) types.append(QtMethodMatchType::metaEnum(enumIndex, argTypeName)); else { unresolvedTypes = true; types.append(QtMethodMatchType::unresolved(argTypeName)); } } else { if (atype == QMetaType::QVariant) types.append(QtMethodMatchType::variant()); else types.append(QtMethodMatchType::metaType(atype, argTypeName)); } } // If the native method requires more arguments than what was passed from JavaScript if (argumentCount + 1 < static_cast(types.count())) { qMatchDebug() << "Match:too few args for" << method.methodSignature(); tooFewArgs.append(index); continue; } if (unresolvedTypes) { qMatchDebug() << "Match:unresolved arg types for" << method.methodSignature(); // remember it so we can give an error message later, if necessary unresolved.append(QtMethodMatchData(/*matchDistance=*/INT_MAX, index, types, QVarLengthArray())); continue; } // Now convert arguments if (args.count() != types.count()) args.resize(types.count()); QtMethodMatchType retType = types[0]; if (retType.typeId() != QMetaType::Void) args[0] = QVariant(retType.typeId(), (void *)0); // the return value bool converted = true; int matchDistance = 0; for (unsigned i = 0; converted && i + 1 < static_cast(types.count()); ++i) { JSValueRef arg = i < argumentCount ? arguments[i] : JSValueMakeUndefined(context); int argdistance = -1; QVariant v = convertValueToQVariant(context, arg, types.at(i+1).typeId(), &argdistance, exception); if (argdistance >= 0) { matchDistance += argdistance; args[i+1] = v; } else { qMatchDebug() << "failed to convert argument " << i << "type" << types.at(i+1).typeId() << QMetaType::typeName(types.at(i+1).typeId()); converted = false; } } qMatchDebug() << "Match: " << method.methodSignature() << (converted ? "converted":"failed to convert") << "distance " << matchDistance; if (converted) { if ((argumentCount + 1 == static_cast(types.count())) && (matchDistance == 0)) { // perfect match, use this one chosenIndex = index; chosenTypes = types; break; } QtMethodMatchData currentMatch(matchDistance, index, types, args); if (candidates.isEmpty()) candidates.append(currentMatch); else { QtMethodMatchData bestMatchSoFar = candidates.at(0); if ((args.count() > bestMatchSoFar.args.count()) || ((args.count() == bestMatchSoFar.args.count()) && (matchDistance <= bestMatchSoFar.matchDistance))) candidates.prepend(currentMatch); else candidates.append(currentMatch); } } else { conversionFailed.append(index); } if (!overloads) break; } if (chosenIndex == -1 && candidates.count() == 0) { // No valid functions at all - format an error message if (!conversionFailed.isEmpty()) { QString message = QString::fromLatin1("incompatible type of argument(s) in call to %0(); candidates were\n") .arg(QString::fromLatin1(signature)); for (int i = 0; i < conversionFailed.size(); ++i) { if (i > 0) message += QLatin1String("\n"); QMetaMethod mtd = meta->method(conversionFailed.at(i)); message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.methodSignature())); } setException(context, exception, message); } else if (!unresolved.isEmpty()) { QtMethodMatchData argsInstance = unresolved.first(); int unresolvedIndex = argsInstance.firstUnresolvedIndex(); Q_ASSERT(unresolvedIndex != -1); QtMethodMatchType unresolvedType = argsInstance.types.at(unresolvedIndex); QString message = QString::fromLatin1("cannot call %0(): unknown type `%1'") .arg(QString::fromLatin1(signature)) .arg(QLatin1String(unresolvedType.name())); setException(context, exception, message); } else { QString message = QString::fromLatin1("too few arguments in call to %0(); candidates are\n") .arg(QString::fromLatin1(signature)); for (int i = 0; i < tooFewArgs.size(); ++i) { if (i > 0) message += QLatin1String("\n"); QMetaMethod mtd = meta->method(tooFewArgs.at(i)); message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.methodSignature())); } setException(context, exception, message); } } if (chosenIndex == -1 && candidates.count() > 0) { QtMethodMatchData bestMatch = candidates.at(0); if ((candidates.size() > 1) && (bestMatch.args.count() == candidates.at(1).args.count()) && (bestMatch.matchDistance == candidates.at(1).matchDistance)) { // ambiguous call QString message = QString::fromLatin1("ambiguous call of overloaded function %0(); candidates were\n") .arg(QLatin1String(signature)); for (int i = 0; i < candidates.size(); ++i) { // Only candidate for overload if argument count and match distance is same as best match if (candidates.at(i).args.count() == bestMatch.args.count() || candidates.at(i).matchDistance == bestMatch.matchDistance) { if (i > 0) message += QLatin1String("\n"); QMetaMethod mtd = meta->method(candidates.at(i).index); message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.methodSignature())); } } setException(context, exception, message); } else { chosenIndex = bestMatch.index; chosenTypes = bestMatch.types; args = bestMatch.args; } } if (chosenIndex != -1) { /* Copy the stuff over */ int i; vars.resize(args.count()); for (i=0; i < args.count(); i++) { vars[i] = args[i]; if (chosenTypes[i].isVariant()) vvars[i] = &vars[i]; else vvars[i] = vars[i].data(); } } return chosenIndex; } // Signals are not fuzzy matched as much as methods static int findSignalIndex(const QMetaObject* meta, int initialIndex, QByteArray signature) { int index = initialIndex; QMetaMethod method = meta->method(index); bool overloads = !signature.contains('('); if (overloads && (method.attributes() & QMetaMethod::Cloned)) { // find the most general method do { method = meta->method(--index); } while (method.attributes() & QMetaMethod::Cloned); } return index; } static JSClassRef prototypeForSignalsAndSlots() { static JSClassDefinition classDef = { 0, kJSClassAttributeNoAutomaticPrototype, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static JSClassRef cls = JSClassCreate(&classDef); return cls; } QtRuntimeMethod::QtRuntimeMethod(JSContextRef ctx, QObject* object, const QByteArray& identifier, int index, int flags, QtInstance* instance) : m_object(object) , m_identifier(identifier) , m_index(index) , m_flags(flags) , m_instance(instance) { } QtRuntimeMethod::~QtRuntimeMethod() { } JSValueRef QtRuntimeMethod::call(JSContextRef context, JSObjectRef function, JSObjectRef /*thisObject*/, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { QtRuntimeMethod* d = toRuntimeMethod(context, function); if (!d) { setException(context, exception, QStringLiteral("cannot call function of deleted runtime method")); return JSValueMakeUndefined(context); } QObject* obj = d->m_object; if (!obj) { setException(context, exception, QStringLiteral("cannot call function of deleted QObject")); return JSValueMakeUndefined(context); } // Allow for maximum of 10 arguments and size stack arrays accordingly. if (argumentCount > 10) return JSValueMakeUndefined(context); QVarLengthArray vargs; void* qargs[11]; const QMetaObject* metaObject = obj->metaObject(); int methodIndex = findMethodIndex(context, metaObject, d->m_identifier, argumentCount, arguments, (d->m_flags & AllowPrivate), vargs, (void **)qargs, exception); if (QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, methodIndex, qargs) >= 0) return JSValueMakeUndefined(context); if (vargs.size() > 0 && metaObject->method(methodIndex).returnType() != QMetaType::Void) return convertQVariantToValue(context, d->m_instance->rootObject(), vargs[0], exception); return JSValueMakeUndefined(context); } JSValueRef QtRuntimeMethod::connect(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { return connectOrDisconnect(context, function, thisObject, argumentCount, arguments, exception, true); } JSValueRef QtRuntimeMethod::disconnect(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { return connectOrDisconnect(context, function, thisObject, argumentCount, arguments, exception, false); } JSObjectRef QtRuntimeMethod::jsObjectRef(JSContextRef context, JSValueRef* exception) { if (m_jsObject) return toRef(m_jsObject.get()); static JSStringRef connectStr = JSStringCreateWithUTF8CString("connect"); static JSStringRef disconnectStr = JSStringCreateWithUTF8CString("disconnect"); JSRetainPtr actualNameStr(Adopt, JSStringCreateWithUTF8CString(m_identifier.constData())); JSObjectRef object = JSObjectMakeFunctionWithCallback(context, actualNameStr.get(), call); JSObjectRef generalFunctionProto = JSValueToObject(context, JSObjectGetPrototype(context, object), 0); JSObjectRef runtimeMethodProto = JSObjectMake(context, prototypeForSignalsAndSlots(), this); JSObjectSetPrototype(context, runtimeMethodProto, generalFunctionProto); JSObjectSetPrototype(context, object, runtimeMethodProto); JSObjectRef connectFunction = JSObjectMakeFunctionWithCallback(context, connectStr, connect); JSObjectSetPrototype(context, connectFunction, runtimeMethodProto); JSObjectRef disconnectFunction = JSObjectMakeFunctionWithCallback(context, disconnectStr, disconnect); JSObjectSetPrototype(context, disconnectFunction, runtimeMethodProto); const JSPropertyAttributes attributes = kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete; JSObjectSetProperty(context, object, connectStr, connectFunction, attributes, exception); JSObjectSetProperty(context, object, disconnectStr, disconnectFunction, attributes, exception); m_jsObject = Weak(toJS(object)); return object; } QtRuntimeMethod* QtRuntimeMethod::toRuntimeMethod(JSContextRef context, JSObjectRef object) { JSObjectRef proto = JSValueToObject(context, JSObjectGetPrototype(context, object), 0); if (!proto) return 0; if (!JSValueIsObjectOfClass(context, proto, prototypeForSignalsAndSlots())) return 0; return static_cast(JSObjectGetPrivate(proto)); } JSValueRef QtRuntimeMethod::connectOrDisconnect(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception, bool connect) { QtRuntimeMethod* d = toRuntimeMethod(context, thisObject); if (!d) d = toRuntimeMethod(context, function); if (!d) { QString errorStr = QStringLiteral("QtMetaMethod.%1: Cannot connect to/from deleted QObject").arg(connect ? QStringLiteral("connect") : QStringLiteral("disconnect")); setException(context, exception, errorStr); return JSValueMakeUndefined(context); } QString functionName = connect ? QStringLiteral("connect") : QStringLiteral("disconnect"); if (!argumentCount) { QString errorStr = QStringLiteral("QtMetaMethod.%1: no arguments given").arg(connect ? QStringLiteral("connect") : QStringLiteral("disconnect")); setException(context, exception, errorStr); return JSValueMakeUndefined(context); } if ((!(d->m_flags & QtRuntimeMethod::MethodIsSignal))) { setException(context, exception, QStringLiteral("QtMetaMethod.%3: %1::%2() is not a signal").arg(QString::fromUtf8(d->m_object.data()->metaObject()->className())).arg(QString::fromLatin1(d->m_identifier)).arg(functionName)); return JSValueMakeUndefined(context); } QObject* sender = d->m_object.data(); if (!sender) { setException(context, exception, QStringLiteral("cannot call function of deleted QObject")); return JSValueMakeUndefined(context); } int signalIndex = findSignalIndex(sender->metaObject(), d->m_index, d->m_identifier); JSObjectRef targetObject = 0; JSObjectRef targetFunction = 0; if (argumentCount == 1) { if (!JSValueIsObject(context, arguments[0])) { setException(context, exception, QStringLiteral("QtMetaMethod.%1: target is not a function").arg(functionName)); return JSValueMakeUndefined(context); } targetFunction = JSValueToObject(context, arguments[0], exception); // object.signal.connect(someFunction); if (JSObjectIsFunction(context, targetFunction)) { // object.signal.connect(otherObject.slot); if (QtRuntimeMethod* targetMethod = toRuntimeMethod(context, targetFunction)) targetObject = toRef(QtInstance::getQtInstance(targetMethod->m_object.data(), d->m_instance->rootObject(), QtInstance::QtOwnership)->createRuntimeObject(toJS(context))); } else targetFunction = 0; } else { // object.signal.connect(object, someFunction); targetObject = JSValueToObject(context, arguments[0], exception); if (JSValueIsObject(context, arguments[1])) { JSObjectRef obj = JSValueToObject(context, arguments[1], exception); if (JSObjectIsFunction(context, obj)) targetFunction = obj; } if (!targetFunction) { // Maybe the second argument is a string JSValueRef conversionException = 0; JSRetainPtr functionName(Adopt, JSValueToStringCopy(context, arguments[1], &conversionException)); if (functionName && !conversionException) { JSValueRef functionProperty = JSObjectGetProperty(context, targetObject, functionName.get(), &conversionException); if (!conversionException && functionProperty && JSValueIsObject(context, functionProperty)) { targetFunction = JSValueToObject(context, functionProperty, 0); if (!JSObjectIsFunction(context, targetFunction)) targetFunction = 0; } } } } // object.signal.connect(someObject); if (!targetFunction) { QString message = QStringLiteral("QtMetaMethod.%1: target is not a function"); if (connect) message = message.arg(QStringLiteral("connect")); else message = message.arg(QStringLiteral("disconnect")); setException(context, exception, message); return JSValueMakeUndefined(context); } if (connect) { // to connect, we need: // target object [from ctor] // target signal index etc. [from ctor] // receiver function [from arguments] // receiver this object [from arguments] QtConnectionObject* conn = new QtConnectionObject(context, QtInstance::getQtInstance(sender, d->m_instance->rootObject(), QtInstance::QtOwnership), signalIndex, targetObject, targetFunction); bool ok = QMetaObject::connect(sender, signalIndex, conn, conn->metaObject()->methodOffset()); if (!ok) { delete conn; QString msg = QString(QLatin1String("QtMetaMethod.connect: failed to connect to %1::%2()")) .arg(QLatin1String(sender->metaObject()->className())) .arg(QLatin1String(d->m_identifier)); setException(context, exception, msg); return JSValueMakeUndefined(context); } // Store connection QtConnectionObject::connections.insert(sender, conn); return JSValueMakeUndefined(context); } // Now to find our previous connection object. QList conns = QtConnectionObject::connections.values(sender); foreach (QtConnectionObject* conn, conns) { // Is this the right connection? if (!conn->match(context, sender, signalIndex, targetObject, targetFunction)) continue; // Yep, disconnect it QMetaObject::disconnect(sender, signalIndex, conn, conn->metaObject()->methodOffset()); delete conn; // this will also remove it from the map return JSValueMakeUndefined(context); } QString msg = QStringLiteral("QtMetaMethod.disconnect: failed to disconnect from %1::%2()") .arg(QLatin1String(sender->metaObject()->className())) .arg(QLatin1String(d->m_identifier)); setException(context, exception, msg); return JSValueMakeUndefined(context); } // =============== QMultiMap QtConnectionObject::connections; QtConnectionObject::QtConnectionObject(JSContextRef context, PassRefPtr senderInstance, int signalIndex, JSObjectRef receiver, JSObjectRef receiverFunction) : QObject(senderInstance->getObject()) , m_context(JSContextGetGlobalContext(context)) , m_rootObject(senderInstance->rootObject()) , m_signalIndex(signalIndex) , m_receiver(receiver) , m_receiverFunction(receiverFunction) { if (m_receiver) JSValueProtect(m_context, m_receiver); JSValueProtect(m_context, m_receiverFunction); } QtConnectionObject::~QtConnectionObject() { connections.remove(parent(), this); if (m_receiver) JSValueUnprotect(m_context, m_receiver); JSValueUnprotect(m_context, m_receiverFunction); } // Begin moc-generated code -- modify with care! Check "HAND EDIT" parts struct qt_meta_stringdata_QtConnectionObject_t { QByteArrayData data[3]; char stringdata[44]; }; #define QT_MOC_LITERAL(idx, ofs, len) { \ Q_REFCOUNT_INITIALIZE_STATIC, len, 0, 0, \ offsetof(qt_meta_stringdata_QtConnectionObject_t, stringdata) + ofs \ - idx * sizeof(QByteArrayData) \ } static const qt_meta_stringdata_QtConnectionObject_t qt_meta_stringdata_QtConnectionObject = { { QT_MOC_LITERAL(0, 0, 33), QT_MOC_LITERAL(1, 34, 7), QT_MOC_LITERAL(2, 42, 0) }, "JSC::Bindings::QtConnectionObject\0" "execute\0\0" }; #undef QT_MOC_LITERAL static const uint qt_meta_data_QtConnectionObject[] = { // content: 7, // revision 0, // classname 0, 0, // classinfo 1, 14, // methods 0, 0, // properties 0, 0, // enums/sets 0, 0, // constructors 0, // flags 0, // signalCount // slots: name, argc, parameters, tag, flags 1, 0, 19, 2, 0x0a, // slots: parameters QMetaType::Void, 0 // eod }; void QtConnectionObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) { if (_c == QMetaObject::InvokeMetaMethod) { Q_ASSERT(staticMetaObject.cast(_o)); QtConnectionObject *_t = static_cast(_o); switch (_id) { case 0: _t->execute(_a); break; // HAND EDIT: add _a parameter default: ; } } } const QMetaObject QtConnectionObject::staticMetaObject = { { &QObject::staticMetaObject, qt_meta_stringdata_QtConnectionObject.data, qt_meta_data_QtConnectionObject, qt_static_metacall, 0, 0 } }; const QMetaObject *QtConnectionObject::metaObject() const { return &staticMetaObject; } void *QtConnectionObject::qt_metacast(const char *_clname) { if (!_clname) return 0; if (!strcmp(_clname, qt_meta_stringdata_QtConnectionObject.stringdata)) return static_cast(const_cast(this)); return QObject::qt_metacast(_clname); } int QtConnectionObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a) { _id = QObject::qt_metacall(_c, _id, _a); if (_id < 0) return _id; if (_c == QMetaObject::InvokeMetaMethod) { if (_id < 1) qt_static_metacall(this, _c, _id, _a); _id -= 1; } return _id; } // End of moc-generated code void QtConnectionObject::execute(void** argv) { QObject* sender = parent(); const QMetaObject* meta = sender->metaObject(); const QMetaMethod method = meta->method(m_signalIndex); JSValueRef* ignoredException = 0; JSRetainPtr lengthProperty(Adopt, JSStringCreateWithUTF8CString("length")); int receiverLength = int(JSValueToNumber(m_context, JSObjectGetProperty(m_context, m_receiverFunction, lengthProperty.get(), ignoredException), ignoredException)); int argc = qMax(method.parameterCount(), receiverLength); WTF::Vector args(argc); for (int i = 0; i < argc; i++) { int argType = method.parameterType(i); args[i] = convertQVariantToValue(m_context, m_rootObject, QVariant(argType, argv[i+1]), ignoredException); } JSObjectCallAsFunction(m_context, m_receiverFunction, m_receiver, argc, args.data(), 0); } bool QtConnectionObject::match(JSContextRef context, QObject* sender, int signalIndex, JSObjectRef receiver, JSObjectRef receiverFunction) { if (sender != parent() || signalIndex != m_signalIndex) return false; JSValueRef* ignoredException = 0; const bool receiverMatch = (!receiver && !m_receiver) || (receiver && m_receiver && JSValueIsEqual(context, receiver, m_receiver, ignoredException)); return receiverMatch && JSValueIsEqual(context, receiverFunction, m_receiverFunction, ignoredException); } } }