blob: a48151f64bdb1c03171a88a9ab75b9040a9aa45b [file] [log] [blame]
/*
* 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 "APIShims.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 <JSFunction.h>
#include <wtf/DateMath.h>
#include <limits.h>
#include <runtime/Error.h>
#include <runtime_array.h>
#include <runtime_object.h>
// QtScript has these
Q_DECLARE_METATYPE(QObjectList);
Q_DECLARE_METATYPE(QList<int>);
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<typename T>
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
} 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"};
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<int, RuntimeConversion> 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::s_info);
}
static bool isJSArray(JSObjectRef object)
{
return toJS(object)->inherits(&JSArray::s_info);
}
static bool isJSDate(JSObjectRef object)
{
return toJS(object)->inherits(&DateInstance::s_info);
}
static bool isQtObject(JSObjectRef object)
{
return toJS(object)->inherits(&RuntimeObject::s_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 (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<const QChar*>(JSStringGetCharactersPtr(stringRef)), JSStringGetLength(stringRef));
}
static JSValueRef unwrapBoxedPrimitive(JSContextRef context, JSValueRef value, JSObjectRef obj)
{
ExecState* exec = toJS(context);
APIEntryShim entryShim(exec);
JSObject* object = toJS(obj);
if (object->inherits(&NumberObject::s_info))
return toRef(exec, jsNumber(object->toNumber(exec)));
if (object->inherits(&StringObject::s_info))
return toRef(exec, object->toString(exec));
if (object->inherits(&BooleanObject::s_info))
return toRef(exec, object->toPrimitive(exec));
return value;
}
QVariant convertValueToQVariant(JSContextRef, JSValueRef, QMetaType::Type, int*, HashSet<JSObjectRef>*, int, JSValueRef *exception);
static QVariantMap convertValueToQVariantMap(JSContextRef context, JSObjectRef object, HashSet<JSObjectRef>* 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 <typename ItemType>
QList<ItemType> convertToList(JSContextRef context, JSRealType type, JSObjectRef object,
JSValueRef value, int* distance, HashSet<JSObjectRef>* visitedObjects, int recursionLimit, JSValueRef* exception,
const QMetaType::Type typeId = static_cast<QMetaType::Type>(qMetaTypeId<ItemType>()))
{
QList<ItemType> 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<ItemType>();
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<ItemType>();
if (distance)
*distance = 10;
}
}
return list;
}
static QString toQString(JSContextRef context, JSValueRef value)
{
JSRetainPtr<JSStringRef> string(Adopt, JSValueToStringCopy(context, value, 0));
if (!string)
return QString();
return QString(reinterpret_cast<const QChar*>(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);
APIEntryShim entryShim(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, ms, /*utc*/ true, 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<JSObjectRef>* 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<QVariant>()) {
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:
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<QVariant>()) {
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<JSStringRef> str(Adopt, JSValueToStringCopy(context, value, exception));
QChar ch;
if (str && JSStringGetLength(str.get()) > 0)
ch = *reinterpret_cast<const QChar*>(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<JSStringRef> str(Adopt, JSValueToStringCopy(context, value, exception));
if (str) {
QString string(reinterpret_cast<const QChar*>(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<QVariant>(context, type, object, value, &dist, visitedObjects, recursionLimit, exception, QMetaType::Void));
break;
case QMetaType::QStringList: {
ret = QVariant(convertToList<QString>(context, type, object, value, &dist, visitedObjects, recursionLimit, exception));
break;
}
case QMetaType::QByteArray: {
if (type == RTUint8Array) {
JSC::Uint8Array* arr = toUint8Array(toJS(toJS(context), value));
ret = QVariant(QByteArray(reinterpret_cast<const char*>(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<QObjectList>()) {
ret = QVariant::fromValue(convertToList<QObject*>(context, type, object, value, &dist, visitedObjects, recursionLimit, exception));
break;
}
if (hint == (QMetaType::Type) qMetaTypeId<QList<int> >()) {
ret = QVariant::fromValue(convertToList<int>(context, type, object, value, &dist, visitedObjects, recursionLimit, exception));
break;
}
if (QtPixmapRuntime::canHandle(static_cast<QMetaType::Type>(hint))) {
ret = QtPixmapRuntime::toQt(context, object, static_cast<QMetaType::Type>(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<QVariant>()) {
if (JSValueIsNull(context, value) || JSValueIsUndefined(context, value)) {
if (distance)
*distance = 1;
return QVariant();
}
if (type == Object) {
// 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<JSObjectRef> visitedObjects;
return convertValueToQVariant(context, value, hint, distance, &visitedObjects, recursionLimit, exception);
}
JSValueRef convertQVariantToValue(JSContextRef context, PassRefPtr<RootObject> 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<QDate>();
else if (type == QMetaType::QTime)
time = variant.value<QTime>();
else {
QDateTime dt = variant.value<QDateTime>().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<QByteArray>();
WTF::RefPtr<JSC::Uint8ClampedArray> wtfByteArray = JSC::Uint8ClampedArray::createUninitialized(qtByteArray.length());
memcpy(wtfByteArray->data(), qtByteArray.constData(), qtByteArray.length());
ExecState* exec = toJS(context);
APIEntryShim entryShim(exec);
return toRef(exec, toJS(exec, static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()), wtfByteArray.get()));
}
if (QMetaType::typeFlags(type).testFlag(QMetaType::PointerToQObject)) {
QObject* obj = variant.value<QObject*>();
if (!obj)
return JSValueMakeNull(context);
ExecState* exec = toJS(context);
APIEntryShim entryShim(exec);
return toRef(exec, QtInstance::getQtInstance(obj, root, QtInstance::QtOwnership)->createRuntimeObject(exec));
}
if (QtPixmapRuntime::canHandle(static_cast<QMetaType::Type>(variant.type())))
return QtPixmapRuntime::toJS(context, variant, exception);
if (customRuntimeConversions()->contains(type)) {
if (!root->globalObject()->inherits(&JSDOMWindow::s_info))
return JSValueMakeUndefined(context);
Document* document = (static_cast<JSDOMWindow*>(root->globalObject()))->impl()->document();
if (!document)
return JSValueMakeUndefined(context);
ExecState* exec = toJS(context);
APIEntryShim entryShim(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>();
QVariantMap::const_iterator i = map.constBegin();
while (i != map.constEnd()) {
QString s = i.key();
JSStringRef propertyName = JSStringCreateWithCharacters(reinterpret_cast<const JSChar*>(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<QStringList>();
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<const JSChar*>(s.constData()), s.length());
JSObjectSetPropertyAtIndex(context, array, i, JSValueMakeString(context, jsString), /*ignored exception*/0);
JSStringRelease(jsString);
}
return array;
} else if (type == static_cast<QMetaType::Type>(qMetaTypeId<QObjectList>())) {
QObjectList ol = variant.value<QObjectList>();
JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception);
ExecState* exec = toJS(context);
APIEntryShim entryShim(exec);
for (int i = 0; i < ol.count(); ++i) {
JSValueRef jsObject = toRef(exec, QtInstance::getQtInstance(ol.at(i), root, QtInstance::QtOwnership)->createRuntimeObject(exec));
JSObjectSetPropertyAtIndex(context, array, i, jsObject, /*ignored exception*/0);
}
return array;
} else if (type == static_cast<QMetaType::Type>(qMetaTypeId<QList<int> >())) {
QList<int> il = variant.value<QList<int> >();
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>()) {
QVariant real = variant.value<QVariant>();
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<const JSChar*>(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<QVariant>();
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<QtMethodMatchType> types;
QVarLengthArray<QVariant, 10> args;
QtMethodMatchData(int dist, int idx, QVector<QtMethodMatchType> typs,
const QVarLengthArray<QVariant, 10> &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<QVariant, 10> &vars,
void** vvars,
JSValueRef* exception)
{
QList<int> 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<QtMethodMatchType> chosenTypes;
QVarLengthArray<QVariant, 10> args;
QVector<QtMethodMatchData> candidates;
QVector<QtMethodMatchData> unresolved;
QVector<int> tooFewArgs;
QVector<int> conversionFailed;
foreach(int index, matchingIndices) {
QMetaMethod method = meta->method(index);
QVector<QtMethodMatchType> 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<QByteArray> 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<unsigned>(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<QVariant, 10>()));
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<unsigned>(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<unsigned>(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<QVariant, 10> 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<JSStringRef> 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 = PassWeak<JSObject>(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<QtRuntimeMethod*>(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<JSStringRef> 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<QtConnectionObject*> 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<QObject*, QtConnectionObject*> QtConnectionObject::connections;
QtConnectionObject::QtConnectionObject(JSContextRef context, PassRefPtr<QtInstance> 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<QtConnectionObject *>(_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<void*>(const_cast<QtConnectionObject*>(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<JSStringRef> 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<JSValueRef> 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);
}
} }