summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/bridge/qt/qt_runtime.cpp
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2012-01-06 14:44:00 +0100
committerSimon Hausmann <simon.hausmann@nokia.com>2012-01-06 14:44:00 +0100
commit40736c5763bf61337c8c14e16d8587db021a87d4 (patch)
treeb17a9c00042ad89cb1308e2484491799aa14e9f8 /Source/WebCore/bridge/qt/qt_runtime.cpp
Imported WebKit commit 2ea9d364d0f6efa8fa64acf19f451504c59be0e4 (http://svn.webkit.org/repository/webkit/trunk@104285)
Diffstat (limited to 'Source/WebCore/bridge/qt/qt_runtime.cpp')
-rw-r--r--Source/WebCore/bridge/qt/qt_runtime.cpp1922
1 files changed, 1922 insertions, 0 deletions
diff --git a/Source/WebCore/bridge/qt/qt_runtime.cpp b/Source/WebCore/bridge/qt/qt_runtime.cpp
new file mode 100644
index 000000000..ac61986a3
--- /dev/null
+++ b/Source/WebCore/bridge/qt/qt_runtime.cpp
@@ -0,0 +1,1922 @@
+/*
+ * 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 "DateMath.h"
+#include "DatePrototype.h"
+#include "FunctionPrototype.h"
+#include "Interpreter.h"
+#include "JSArray.h"
+#include "JSByteArray.h"
+#include "JSDocument.h"
+#include "JSDOMBinding.h"
+#include "JSDOMWindow.h"
+#include <JSFunction.h>
+#include "JSGlobalObject.h"
+#include "JSHTMLElement.h"
+#include "JSLock.h"
+#include "JSObject.h"
+#include "JSRetainPtr.h"
+#include "ObjectPrototype.h"
+#include "PropertyNameArray.h"
+#include "RegExpConstructor.h"
+#include "RegExpObject.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 <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,
+ String,
+ Date,
+ RegExp,
+ Array,
+ QObj,
+ Object,
+ Null,
+ RTArray,
+ JSByteArray
+} 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", "String", "Date",
+ "RegExp", "Array", "RTObject", "Object", "Null", "RTArray"};
+
+ dbg.nospace() << "JSType(" << ((int)c) << ", " << map[c] << ")";
+
+ return dbg.space();
+}
+#endif
+
+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 JSRealType valueRealType(ExecState* exec, JSValue val)
+{
+ if (val.isNumber())
+ return Number;
+ else if (val.isString())
+ return String;
+ else if (val.isBoolean())
+ return Boolean;
+ else if (val.isNull())
+ return Null;
+ else if (isJSByteArray(val))
+ return JSByteArray;
+ else if (val.isObject()) {
+ JSObject *object = val.toObject(exec);
+ if (object->inherits(&RuntimeArray::s_info)) // RuntimeArray 'inherits' from Array, but not in C++
+ return RTArray;
+ else if (object->inherits(&JSArray::s_info))
+ return Array;
+ else if (object->inherits(&DateInstance::s_info))
+ return Date;
+ else if (object->inherits(&RegExpObject::s_info))
+ return RegExp;
+ else if (object->inherits(&RuntimeObject::s_info))
+ return QObj;
+ return Object;
+ }
+
+ return String; // I don't know.
+}
+
+QVariant convertValueToQVariant(ExecState*, JSValue, QMetaType::Type, int*, HashSet<JSObject*>*, int);
+
+static QVariantMap convertValueToQVariantMap(ExecState* exec, JSObject* object, HashSet<JSObject*>* visitedObjects, int recursionLimit)
+{
+ Q_ASSERT(!exec->hadException());
+
+ PropertyNameArray properties(exec);
+ object->methodTable()->getPropertyNames(object, exec, properties, ExcludeDontEnumProperties);
+ PropertyNameArray::const_iterator it = properties.begin();
+ QVariantMap result;
+ int objdist = 0;
+
+ while (it != properties.end()) {
+ if (object->propertyIsEnumerable(exec, *it)) {
+ JSValue val = object->get(exec, *it);
+ if (exec->hadException())
+ exec->clearException();
+ else {
+ QVariant v = convertValueToQVariant(exec, val, QMetaType::Void, &objdist, visitedObjects, recursionLimit);
+ if (objdist >= 0) {
+ UString ustring = (*it).ustring();
+ QString id = QString((const QChar*)ustring.impl()->characters(), ustring.length());
+ result.insert(id, v);
+ }
+ }
+ }
+ ++it;
+ }
+ return result;
+}
+
+QVariant convertValueToQVariant(ExecState* exec, JSValue value, QMetaType::Type hint, int *distance, HashSet<JSObject*>* visitedObjects, int recursionLimit)
+{
+ --recursionLimit;
+
+ if (!value || !recursionLimit)
+ return QVariant();
+
+ JSObject* object = 0;
+ if (value.isObject()) {
+ object = value.toObject(exec);
+ if (visitedObjects->contains(object))
+ return QVariant();
+
+ visitedObjects->add(object);
+ }
+
+ // check magic pointer values before dereferencing value
+ if (value == jsNaN()
+ || (value == jsUndefined()
+ && hint != QMetaType::QString
+ && hint != (QMetaType::Type) qMetaTypeId<QVariant>())) {
+ if (distance)
+ *distance = -1;
+ return QVariant();
+ }
+
+ JSLock lock(SilenceAssertionsOnly);
+ JSRealType type = valueRealType(exec, value);
+ if (hint == QMetaType::Void) {
+ switch(type) {
+ case Number:
+ hint = QMetaType::Double;
+ break;
+ case Boolean:
+ hint = QMetaType::Bool;
+ break;
+ case String:
+ default:
+ hint = QMetaType::QString;
+ break;
+ case Date:
+ hint = QMetaType::QDateTime;
+ break;
+ case RegExp:
+ hint = QMetaType::QRegExp;
+ break;
+ case Object:
+ if (object->inherits(&NumberObject::s_info))
+ hint = QMetaType::Double;
+ else if (object->inherits(&BooleanObject::s_info))
+ hint = QMetaType::Bool;
+ else
+ hint = QMetaType::QVariantMap;
+ break;
+ case QObj:
+ hint = QMetaType::QObjectStar;
+ break;
+ case JSByteArray:
+ hint = QMetaType::QByteArray;
+ break;
+ case Array:
+ case RTArray:
+ hint = QMetaType::QVariantList;
+ break;
+ }
+ }
+
+ qConvDebug() << "convertValueToQVariant: jstype is " << type << ", hint is" << hint;
+
+ if (value == jsNull()
+ && 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:
+ if (type == Object && object->inherits(&BooleanObject::s_info))
+ ret = QVariant(asBooleanObject(value)->internalValue().toBoolean(exec));
+ else
+ ret = QVariant(value.toBoolean(exec));
+ 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(value.toNumber(exec));
+ 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)value.toNumber(exec)));
+ if (type == Boolean)
+ dist = 3;
+ else
+ dist = 6;
+ } else {
+ UString str = value.toString(exec);
+ ret = QVariant(QChar(str.length() ? *(const ushort*)str.impl()->characters() : 0));
+ if (type == String)
+ dist = 3;
+ else
+ dist = 10;
+ }
+ break;
+
+ case QMetaType::QString: {
+ if (value.isUndefinedOrNull()) {
+ if (distance)
+ *distance = 1;
+ return QString();
+ } else {
+ UString ustring = value.toString(exec);
+ ret = QVariant(QString((const QChar*)ustring.impl()->characters(), ustring.length()));
+ if (type == String)
+ dist = 0;
+ else
+ dist = 10;
+ }
+ break;
+ }
+
+ case QMetaType::QVariantMap:
+ if (type == Object || type == Array || type == RTArray) {
+ ret = QVariant(convertValueToQVariantMap(exec, object, visitedObjects, recursionLimit));
+ // Those types can still have perfect matches, e.g. 'bool' if value is a Boolean Object.
+ dist = 1;
+ }
+ break;
+
+ case QMetaType::QVariantList:
+ if (type == RTArray) {
+ RuntimeArray* rtarray = static_cast<RuntimeArray*>(object);
+
+ QVariantList result;
+ int len = rtarray->getLength();
+ int objdist = 0;
+ qConvDebug() << "converting a " << len << " length Array";
+ for (int i = 0; i < len; ++i) {
+ JSValue val = rtarray->getConcreteArray()->valueAt(exec, i);
+ result.append(convertValueToQVariant(exec, val, QMetaType::Void, &objdist, visitedObjects, recursionLimit));
+ if (objdist == -1) {
+ qConvDebug() << "Failed converting element at index " << i;
+ break; // Failed converting a list entry, so fail the array
+ }
+ }
+ if (objdist != -1) {
+ dist = 5;
+ ret = QVariant(result);
+ }
+ } else if (type == Array) {
+ JSArray* array = static_cast<JSArray*>(object);
+
+ QVariantList result;
+ int len = array->length();
+ int objdist = 0;
+ qConvDebug() << "converting a " << len << " length Array";
+ for (int i = 0; i < len; ++i) {
+ JSValue val = array->get(exec, i);
+ result.append(convertValueToQVariant(exec, val, QMetaType::Void, &objdist, visitedObjects, recursionLimit));
+ if (objdist == -1) {
+ qConvDebug() << "Failed converting element at index " << i;
+ break; // Failed converting a list entry, so fail the array
+ }
+ }
+ if (objdist != -1) {
+ dist = 5;
+ ret = QVariant(result);
+ }
+ } else {
+ // Make a single length array
+ int objdist;
+ qConvDebug() << "making a single length variantlist";
+ QVariant var = convertValueToQVariant(exec, value, QMetaType::Void, &objdist, visitedObjects, recursionLimit);
+ if (objdist != -1) {
+ QVariantList result;
+ result << var;
+ ret = QVariant(result);
+ dist = 10;
+ } else {
+ qConvDebug() << "failed making single length varlist";
+ }
+ }
+ break;
+
+ case QMetaType::QStringList: {
+ if (type == RTArray) {
+ RuntimeArray* rtarray = static_cast<RuntimeArray*>(object);
+
+ QStringList result;
+ int len = rtarray->getLength();
+ for (int i = 0; i < len; ++i) {
+ JSValue val = rtarray->getConcreteArray()->valueAt(exec, i);
+ UString ustring = val.toString(exec);
+ QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length());
+
+ result.append(qstring);
+ }
+ dist = 5;
+ ret = QVariant(result);
+ } else if (type == Array) {
+ JSArray* array = static_cast<JSArray*>(object);
+
+ QStringList result;
+ int len = array->length();
+ for (int i = 0; i < len; ++i) {
+ JSValue val = array->get(exec, i);
+ UString ustring = val.toString(exec);
+ QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length());
+
+ result.append(qstring);
+ }
+ dist = 5;
+ ret = QVariant(result);
+ } else {
+ // Make a single length array
+ UString ustring = value.toString(exec);
+ QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length());
+ QStringList result;
+ result.append(qstring);
+ ret = QVariant(result);
+ dist = 10;
+ }
+ break;
+ }
+
+ case QMetaType::QByteArray: {
+ if (type == JSByteArray) {
+ WTF::ByteArray* arr = asByteArray(value)->storage();
+ ret = QVariant(QByteArray(reinterpret_cast<const char*>(arr->data()), arr->length()));
+ dist = 0;
+ } else {
+ UString ustring = value.toString(exec);
+ ret = QVariant(QString((const QChar*)ustring.impl()->characters(), ustring.length()).toLatin1());
+ if (type == String)
+ dist = 5;
+ else
+ dist = 10;
+ }
+ break;
+ }
+
+ case QMetaType::QDateTime:
+ case QMetaType::QDate:
+ case QMetaType::QTime:
+ if (type == Date) {
+ DateInstance* date = static_cast<DateInstance*>(object);
+ GregorianDateTime gdt;
+ msToGregorianDateTime(exec, date->internalNumber(), true, gdt);
+ if (hint == QMetaType::QDateTime) {
+ ret = QDateTime(QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay), QTime(gdt.hour, gdt.minute, gdt.second), Qt::UTC);
+ dist = 0;
+ } else if (hint == QMetaType::QDate) {
+ ret = QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay);
+ dist = 1;
+ } else {
+ ret = QTime(gdt.hour + 1900, gdt.minute, gdt.second);
+ dist = 2;
+ }
+ } else if (type == Number) {
+ double b = value.toNumber(exec);
+ GregorianDateTime gdt;
+ msToGregorianDateTime(exec, b, true, gdt);
+ if (hint == QMetaType::QDateTime) {
+ ret = QDateTime(QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay), QTime(gdt.hour, gdt.minute, gdt.second), Qt::UTC);
+ dist = 6;
+ } else if (hint == QMetaType::QDate) {
+ ret = QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay);
+ dist = 8;
+ } else {
+ ret = QTime(gdt.hour, gdt.minute, gdt.second);
+ dist = 10;
+ }
+#ifndef QT_NO_DATESTRING
+ } else if (type == String) {
+ UString ustring = value.toString(exec);
+ QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length());
+
+ 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;
+ }
+ }
+#endif // QT_NO_DATESTRING
+ }
+ break;
+
+ case QMetaType::QRegExp:
+ if (type == RegExp) {
+/*
+ RegExpObject *re = static_cast<RegExpObject*>(object);
+*/
+ // Attempt to convert.. a bit risky
+ UString ustring = value.toString(exec);
+ QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length());
+
+ // this is of the form '/xxxxxx/i'
+ int firstSlash = qstring.indexOf(QLatin1Char('/'));
+ int lastSlash = qstring.lastIndexOf(QLatin1Char('/'));
+ if (firstSlash >=0 && lastSlash > firstSlash) {
+ QRegExp realRe;
+
+ realRe.setPattern(qstring.mid(firstSlash + 1, lastSlash - firstSlash - 1));
+
+ if (qstring.mid(lastSlash + 1).contains(QLatin1Char('i')))
+ realRe.setCaseSensitivity(Qt::CaseInsensitive);
+
+ ret = QVariant::fromValue(realRe);
+ dist = 0;
+ } else {
+ qConvDebug() << "couldn't parse a JS regexp";
+ }
+ } else if (type == String) {
+ UString ustring = value.toString(exec);
+ QString qstring = QString((const QChar*)ustring.impl()->characters(), ustring.length());
+
+ QRegExp re(qstring);
+ if (re.isValid()) {
+ ret = QVariant::fromValue(re);
+ dist = 10;
+ }
+ }
+ break;
+
+ case QMetaType::QObjectStar:
+ if (type == QObj) {
+ QtInstance* qtinst = QtInstance::getInstance(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(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:" << value.toNumber(exec);
+ } else {
+ qConvDebug() << "void* - unhandled type" << type;
+ }
+ break;
+
+ default:
+ // Non const type ids
+ if (hint == (QMetaType::Type) qMetaTypeId<QObjectList>())
+ {
+ if (type == RTArray) {
+ RuntimeArray* rtarray = static_cast<RuntimeArray*>(object);
+
+ QObjectList result;
+ int len = rtarray->getLength();
+ for (int i = 0; i < len; ++i) {
+ JSValue val = rtarray->getConcreteArray()->valueAt(exec, i);
+ int itemdist = -1;
+ QVariant item = convertValueToQVariant(exec, val, QMetaType::QObjectStar, &itemdist, visitedObjects, recursionLimit);
+ if (itemdist >= 0)
+ result.append(item.value<QObject*>());
+ else
+ break;
+ }
+ // If we didn't fail conversion
+ if (result.count() == len) {
+ dist = 5;
+ ret = QVariant::fromValue(result);
+ }
+ } else if (type == Array) {
+ JSObject* object = value.toObject(exec);
+ JSArray* array = static_cast<JSArray *>(object);
+ QObjectList result;
+ int len = array->length();
+ for (int i = 0; i < len; ++i) {
+ JSValue val = array->get(exec, i);
+ int itemdist = -1;
+ QVariant item = convertValueToQVariant(exec, val, QMetaType::QObjectStar, &itemdist, visitedObjects, recursionLimit);
+ if (itemdist >= 0)
+ result.append(item.value<QObject*>());
+ else
+ break;
+ }
+ // If we didn't fail conversion
+ if (result.count() == len) {
+ dist = 5;
+ ret = QVariant::fromValue(result);
+ }
+ } else {
+ // Make a single length array
+ QObjectList result;
+ int itemdist = -1;
+ QVariant item = convertValueToQVariant(exec, value, QMetaType::QObjectStar, &itemdist, visitedObjects, recursionLimit);
+ if (itemdist >= 0) {
+ result.append(item.value<QObject*>());
+ dist = 10;
+ ret = QVariant::fromValue(result);
+ }
+ }
+ break;
+ } else if (hint == (QMetaType::Type) qMetaTypeId<QList<int> >()) {
+ if (type == RTArray) {
+ RuntimeArray* rtarray = static_cast<RuntimeArray*>(object);
+
+ QList<int> result;
+ int len = rtarray->getLength();
+ for (int i = 0; i < len; ++i) {
+ JSValue val = rtarray->getConcreteArray()->valueAt(exec, i);
+ int itemdist = -1;
+ QVariant item = convertValueToQVariant(exec, val, QMetaType::Int, &itemdist, visitedObjects, recursionLimit);
+ if (itemdist >= 0)
+ result.append(item.value<int>());
+ else
+ break;
+ }
+ // If we didn't fail conversion
+ if (result.count() == len) {
+ dist = 5;
+ ret = QVariant::fromValue(result);
+ }
+ } else if (type == Array) {
+ JSArray* array = static_cast<JSArray *>(object);
+
+ QList<int> result;
+ int len = array->length();
+ for (int i = 0; i < len; ++i) {
+ JSValue val = array->get(exec, i);
+ int itemdist = -1;
+ QVariant item = convertValueToQVariant(exec, val, QMetaType::Int, &itemdist, visitedObjects, recursionLimit);
+ if (itemdist >= 0)
+ result.append(item.value<int>());
+ else
+ break;
+ }
+ // If we didn't fail conversion
+ if (result.count() == len) {
+ dist = 5;
+ ret = QVariant::fromValue(result);
+ }
+ } else {
+ // Make a single length array
+ QList<int> result;
+ int itemdist = -1;
+ QVariant item = convertValueToQVariant(exec, value, QMetaType::Int, &itemdist, visitedObjects, recursionLimit);
+ if (itemdist >= 0) {
+ result.append(item.value<int>());
+ dist = 10;
+ ret = QVariant::fromValue(result);
+ }
+ }
+ break;
+ } else if (QtPixmapInstance::canHandle(static_cast<QMetaType::Type>(hint))) {
+ ret = QtPixmapInstance::variantFromObject(object, static_cast<QMetaType::Type>(hint));
+ } else if (customRuntimeConversions()->contains(hint)) {
+ ret = customRuntimeConversions()->value(hint).toVariantFunc(object, &dist, visitedObjects);
+ if (dist == 0)
+ break;
+ } else if (hint == (QMetaType::Type) qMetaTypeId<QVariant>()) {
+ if (value.isUndefinedOrNull()) {
+ if (distance)
+ *distance = 1;
+ return QVariant();
+ } else {
+ 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(exec, value, QMetaType::Void, distance, visitedObjects, recursionLimit);
+ dist = 10;
+ }
+ break;
+ }
+
+ dist = 10;
+ break;
+ }
+
+ if (!ret.isValid())
+ dist = -1;
+ if (distance)
+ *distance = dist;
+
+ return ret;
+}
+
+QVariant convertValueToQVariant(ExecState* exec, JSValue value, QMetaType::Type hint, int *distance)
+{
+ const int recursionLimit = 200;
+ HashSet<JSObject*> visitedObjects;
+ return convertValueToQVariant(exec, value, hint, distance, &visitedObjects, recursionLimit);
+}
+
+JSValue convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& variant)
+{
+ // 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() &&
+ type != QMetaType::QObjectStar &&
+ type != QMetaType::VoidStar &&
+ type != QMetaType::QWidgetStar &&
+ type != QMetaType::QString) {
+ return jsNull();
+ }
+
+ JSLock lock(SilenceAssertionsOnly);
+
+ if (type == QMetaType::Bool)
+ return jsBoolean(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 jsNumber(variant.toDouble());
+
+ if (type == QMetaType::QRegExp) {
+ QRegExp re = variant.value<QRegExp>();
+
+ if (re.isValid()) {
+ UString pattern((UChar*)re.pattern().utf16(), re.pattern().length());
+ RegExpFlags flags = (re.caseSensitivity() == Qt::CaseInsensitive) ? FlagIgnoreCase : NoFlags;
+
+ JSC::RegExp* regExp = JSC::RegExp::create(exec->globalData(), pattern, flags);
+ if (regExp->isValid())
+ return RegExpObject::create(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->regExpStructure(), regExp);
+ return jsNull();
+ }
+ }
+
+ 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)
+ GregorianDateTime dt;
+ dt.year = date.year() - 1900;
+ dt.month = date.month() - 1;
+ dt.monthDay = date.day();
+ dt.hour = time.hour();
+ dt.minute = time.minute();
+ dt.second = time.second();
+ dt.isDST = -1;
+ double ms = gregorianDateTimeToMS(exec, dt, time.msec(), /*inputIsUTC*/ false);
+
+ return DateInstance::create(exec, exec->lexicalGlobalObject()->dateStructure(), trunc(ms));
+ }
+
+ if (type == QMetaType::QByteArray) {
+ QByteArray qtByteArray = variant.value<QByteArray>();
+ WTF::RefPtr<WTF::ByteArray> wtfByteArray = WTF::ByteArray::create(qtByteArray.length());
+ memcpy(wtfByteArray->data(), qtByteArray.constData(), qtByteArray.length());
+ return JSC::JSByteArray::create(exec, JSC::JSByteArray::createStructure(exec->globalData(), exec->lexicalGlobalObject(), jsNull()), wtfByteArray.get());
+ }
+
+ if (type == QMetaType::QObjectStar || type == QMetaType::QWidgetStar) {
+ QObject* obj = variant.value<QObject*>();
+ if (!obj)
+ return jsNull();
+ return QtInstance::getQtInstance(obj, root, QScriptEngine::QtOwnership)->createRuntimeObject(exec);
+ }
+
+ if (QtPixmapInstance::canHandle(static_cast<QMetaType::Type>(variant.type())))
+ return QtPixmapInstance::createPixmapRuntimeObject(exec, root, variant);
+
+ if (customRuntimeConversions()->contains(type)) {
+ if (!root->globalObject()->inherits(&JSDOMWindow::s_info))
+ return jsUndefined();
+
+ Document* document = (static_cast<JSDOMWindow*>(root->globalObject()))->impl()->document();
+ if (!document)
+ return jsUndefined();
+ return customRuntimeConversions()->value(type).toJSValueFunc(exec, toJSDOMGlobalObject(document, exec), variant);
+ }
+
+ if (type == QMetaType::QVariantMap) {
+ // create a new object, and stuff properties into it
+ JSObject* ret = constructEmptyObject(exec);
+ QVariantMap map = variant.value<QVariantMap>();
+ QVariantMap::const_iterator i = map.constBegin();
+ while (i != map.constEnd()) {
+ QString s = i.key();
+ JSValue val = convertQVariantToValue(exec, root.get(), i.value());
+ if (val) {
+ PutPropertySlot slot;
+ ret->methodTable()->put(ret, exec, Identifier(&exec->globalData(), reinterpret_cast_ptr<const UChar *>(s.constData()), s.length()), val, slot);
+ // ### error case?
+ }
+ ++i;
+ }
+
+ return ret;
+ }
+
+ // List types
+ if (type == QMetaType::QVariantList) {
+ QVariantList vl = variant.toList();
+ qConvDebug() << "got a " << vl.count() << " length list:" << vl;
+ return RuntimeArray::create(exec, new QtArray<QVariant>(vl, QMetaType::Void, root));
+ } else if (type == QMetaType::QStringList) {
+ QStringList sl = variant.value<QStringList>();
+ return RuntimeArray::create(exec, new QtArray<QString>(sl, QMetaType::QString, root));
+ } else if (type == (QMetaType::Type) qMetaTypeId<QObjectList>()) {
+ QObjectList ol= variant.value<QObjectList>();
+ return RuntimeArray::create(exec, new QtArray<QObject*>(ol, QMetaType::QObjectStar, root));
+ } else if (type == (QMetaType::Type)qMetaTypeId<QList<int> >()) {
+ QList<int> il= variant.value<QList<int> >();
+ return RuntimeArray::create(exec, new QtArray<int>(il, QMetaType::Int, root));
+ }
+
+ if (type == (QMetaType::Type)qMetaTypeId<QVariant>()) {
+ QVariant real = variant.value<QVariant>();
+ qConvDebug() << "real variant is:" << real;
+ return convertQVariantToValue(exec, root, real);
+ }
+
+ qConvDebug() << "fallback path for" << variant << variant.userType();
+
+ QString string = variant.toString();
+ UString ustring((UChar*)string.utf16(), string.length());
+ return jsString(exec, ustring);
+}
+
+// ===============
+
+// Qt-like macros
+#define QW_D(Class) Class##Data* d = d_func()
+#define QW_DS(Class,Instance) Class##Data* d = Instance->d_func()
+
+const ClassInfo QtRuntimeMethod::s_info = { "QtRuntimeMethod", &InternalFunction::s_info, 0, 0, CREATE_METHOD_TABLE(QtRuntimeMethod) };
+
+QtRuntimeMethod::QtRuntimeMethod(QtRuntimeMethodData* dd, ExecState* exec, Structure* structure, const Identifier& identifier)
+ : InternalFunction(exec->lexicalGlobalObject(), structure)
+ , d_ptr(dd)
+{
+}
+
+void QtRuntimeMethod::finishCreation(ExecState* exec, const Identifier& identifier, PassRefPtr<QtInstance> instance)
+{
+ Base::finishCreation(exec->globalData(), identifier);
+ QW_D(QtRuntimeMethod);
+ d->m_instance = instance;
+ d->m_finalizer.set(exec->globalData(), this, d);
+}
+
+QtRuntimeMethod::~QtRuntimeMethod()
+{
+ delete d_ptr;
+}
+
+void QtRuntimeMethod::destroy(JSCell* cell)
+{
+ jsCast<QtRuntimeMethod*>(cell)->QtRuntimeMethod::~QtRuntimeMethod();
+}
+
+// ===============
+
+QtRuntimeMethodData::~QtRuntimeMethodData()
+{
+}
+
+void QtRuntimeMethodData::finalize(Handle<Unknown> value, void*)
+{
+ m_instance->removeCachedMethod(static_cast<JSObject*>(value.get().asCell()));
+}
+
+QtRuntimeMetaMethodData::~QtRuntimeMetaMethodData()
+{
+
+}
+
+QtRuntimeConnectionMethodData::~QtRuntimeConnectionMethodData()
+{
+
+}
+
+// ===============
+
+// 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) QMetaType::type("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(ExecState* exec,
+ const QMetaObject* meta,
+ const QByteArray& signature,
+ bool allowPrivate,
+ QVarLengthArray<QVariant, 10> &vars,
+ void** vvars,
+ JSObject **pError)
+{
+ 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 (m.signature() == signature)
+ matchingIndices.append(i);
+ else if (overloads) {
+ QByteArray rawsignature = m.signature();
+ rawsignature.truncate(rawsignature.indexOf('('));
+ if (rawsignature == signature)
+ matchingIndices.append(i);
+ }
+ }
+
+ int chosenIndex = -1;
+ *pError = 0;
+ 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 = QMetaType::type(returnTypeName);
+ if ((rtype == 0) && !returnTypeName.isEmpty()) {
+ if (returnTypeName == "QVariant") {
+ types.append(QtMethodMatchType::variant());
+ } else 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 (returnTypeName == "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 = QMetaType::type(argTypeName);
+ if (atype == 0) {
+ if (argTypeName == "QVariant") {
+ types.append(QtMethodMatchType::variant());
+ } else {
+ int enumIndex = indexOfMetaEnum(meta, argTypeName);
+ if (enumIndex != -1)
+ types.append(QtMethodMatchType::metaEnum(enumIndex, argTypeName));
+ else {
+ unresolvedTypes = true;
+ types.append(QtMethodMatchType::unresolved(argTypeName));
+ }
+ }
+ } else {
+ if (argTypeName == "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 (exec->argumentCount() + 1 < static_cast<unsigned>(types.count())) {
+ qMatchDebug() << "Match:too few args for" << method.signature();
+ tooFewArgs.append(index);
+ continue;
+ }
+
+ if (unresolvedTypes) {
+ qMatchDebug() << "Match:unresolved arg types for" << method.signature();
+ // 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];
+ 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) {
+ JSValue arg = i < exec->argumentCount() ? exec->argument(i) : jsUndefined();
+
+ int argdistance = -1;
+ QVariant v = convertValueToQVariant(exec, arg, types.at(i+1).typeId(), &argdistance);
+ 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.signature() << (converted ? "converted":"failed to convert") << "distance " << matchDistance;
+
+ if (converted) {
+ if ((exec->argumentCount() + 1 == static_cast<unsigned>(types.count()))
+ && (matchDistance == 0)) {
+ // perfect match, use this one
+ chosenIndex = index;
+ break;
+ } else {
+ 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(QLatin1String(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.signature()));
+ }
+ *pError = throwError(exec, createTypeError(exec, message.toLatin1().constData()));
+ } 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()));
+ *pError = throwError(exec, createTypeError(exec, message.toLatin1().constData()));
+ } else {
+ QString message = QString::fromLatin1("too few arguments in call to %0(); candidates are\n")
+ .arg(QLatin1String(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.signature()));
+ }
+ *pError = throwError(exec, createSyntaxError(exec, message.toLatin1().constData()));
+ }
+ }
+
+ 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.signature()));
+ }
+ }
+ *pError = throwError(exec, createTypeError(exec, message.toLatin1().constData()));
+ } else {
+ chosenIndex = bestMatch.index;
+ 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];
+ 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;
+}
+
+const ClassInfo QtRuntimeMetaMethod::s_info = { "QtRuntimeMethod", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(QtRuntimeMetaMethod) };
+
+QtRuntimeMetaMethod::QtRuntimeMetaMethod(ExecState* exec, Structure* structure, const Identifier& identifier)
+ : QtRuntimeMethod (new QtRuntimeMetaMethodData(), exec, structure, identifier)
+{
+}
+
+void QtRuntimeMetaMethod::finishCreation(ExecState* exec, const Identifier& identifier, PassRefPtr<QtInstance> instance, int index, const QByteArray& signature, bool allowPrivate)
+{
+ Base::finishCreation(exec, identifier, instance);
+ QW_D(QtRuntimeMetaMethod);
+ d->m_signature = signature;
+ d->m_index = index;
+ d->m_allowPrivate = allowPrivate;
+}
+
+void QtRuntimeMetaMethod::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+ QtRuntimeMetaMethod* thisObject = jsCast<QtRuntimeMetaMethod*>(cell);
+ QtRuntimeMethod::visitChildren(thisObject, visitor);
+ QtRuntimeMetaMethodData* d = thisObject->d_func();
+ if (d->m_connect)
+ visitor.append(&d->m_connect);
+ if (d->m_disconnect)
+ visitor.append(&d->m_disconnect);
+}
+
+EncodedJSValue QtRuntimeMetaMethod::call(ExecState* exec)
+{
+ QtRuntimeMetaMethodData* d = static_cast<QtRuntimeMetaMethod *>(exec->callee())->d_func();
+
+ // We're limited to 10 args
+ if (exec->argumentCount() > 10)
+ return JSValue::encode(jsUndefined());
+
+ // We have to pick a method that matches..
+ JSLock lock(SilenceAssertionsOnly);
+
+ QObject *obj = d->m_instance->getObject();
+ if (obj) {
+ QVarLengthArray<QVariant, 10> vargs;
+ void *qargs[11];
+
+ int methodIndex;
+ JSObject* errorObj = 0;
+ if ((methodIndex = findMethodIndex(exec, obj->metaObject(), d->m_signature, d->m_allowPrivate, vargs, (void **)qargs, &errorObj)) != -1) {
+ if (QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, methodIndex, qargs) >= 0)
+ return JSValue::encode(jsUndefined());
+
+ if (vargs[0].isValid())
+ return JSValue::encode(convertQVariantToValue(exec, d->m_instance->rootObject(), vargs[0]));
+ }
+
+ if (errorObj)
+ return JSValue::encode(errorObj);
+ } else {
+ return throwVMError(exec, createError(exec, "cannot call function of deleted QObject"));
+ }
+
+ // void functions return undefined
+ return JSValue::encode(jsUndefined());
+}
+
+CallType QtRuntimeMetaMethod::getCallData(JSCell*, CallData& callData)
+{
+ callData.native.function = call;
+ return CallTypeHost;
+}
+
+bool QtRuntimeMetaMethod::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
+{
+ QtRuntimeMetaMethod* thisObject = jsCast<QtRuntimeMetaMethod*>(cell);
+ if (propertyName == "connect") {
+ slot.setCustom(thisObject, thisObject->connectGetter);
+ return true;
+ } else if (propertyName == "disconnect") {
+ slot.setCustom(thisObject, thisObject->disconnectGetter);
+ return true;
+ } else if (propertyName == exec->propertyNames().length) {
+ slot.setCustom(thisObject, thisObject->lengthGetter);
+ return true;
+ }
+
+ return QtRuntimeMethod::getOwnPropertySlot(thisObject, exec, propertyName, slot);
+}
+
+bool QtRuntimeMetaMethod::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
+{
+ QtRuntimeMetaMethod* thisObject = jsCast<QtRuntimeMetaMethod*>(object);
+ if (propertyName == "connect") {
+ PropertySlot slot;
+ slot.setCustom(thisObject, connectGetter);
+ descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum);
+ return true;
+ }
+
+ if (propertyName == "disconnect") {
+ PropertySlot slot;
+ slot.setCustom(thisObject, disconnectGetter);
+ descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum);
+ return true;
+ }
+
+ if (propertyName == exec->propertyNames().length) {
+ PropertySlot slot;
+ slot.setCustom(thisObject, lengthGetter);
+ descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum);
+ return true;
+ }
+
+ return QtRuntimeMethod::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
+}
+
+void QtRuntimeMetaMethod::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+{
+ if (mode == IncludeDontEnumProperties) {
+ propertyNames.add(Identifier(exec, "connect"));
+ propertyNames.add(Identifier(exec, "disconnect"));
+ propertyNames.add(exec->propertyNames().length);
+ }
+
+ QtRuntimeMethod::getOwnPropertyNames(object, exec, propertyNames, mode);
+}
+
+JSValue QtRuntimeMetaMethod::lengthGetter(ExecState*, JSValue, const Identifier&)
+{
+ // QtScript always returns 0
+ return jsNumber(0);
+}
+
+JSValue QtRuntimeMetaMethod::connectGetter(ExecState* exec, JSValue slotBase, const Identifier& ident)
+{
+ QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(asObject(slotBase));
+ QW_DS(QtRuntimeMetaMethod, thisObj);
+
+ if (!d->m_connect)
+ d->m_connect.set(exec->globalData(), thisObj, QtRuntimeConnectionMethod::create(exec, ident, true, d->m_instance, d->m_index, d->m_signature));
+ return d->m_connect.get();
+}
+
+JSValue QtRuntimeMetaMethod::disconnectGetter(ExecState* exec, JSValue slotBase, const Identifier& ident)
+{
+ QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(asObject(slotBase));
+ QW_DS(QtRuntimeMetaMethod, thisObj);
+
+ if (!d->m_disconnect)
+ d->m_disconnect.set(exec->globalData(), thisObj, QtRuntimeConnectionMethod::create(exec, ident, false, d->m_instance, d->m_index, d->m_signature));
+ return d->m_disconnect.get();
+}
+
+// ===============
+
+QMultiMap<QObject*, QtConnectionObject*> QtRuntimeConnectionMethod::connections;
+
+const ClassInfo QtRuntimeConnectionMethod::s_info = { "QtRuntimeMethod", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(QtRuntimeConnectionMethod) };
+
+QtRuntimeConnectionMethod::QtRuntimeConnectionMethod(ExecState* exec, Structure* structure, const Identifier& identifier)
+ : QtRuntimeMethod (new QtRuntimeConnectionMethodData(), exec, structure, identifier)
+{
+}
+
+void QtRuntimeConnectionMethod::finishCreation(ExecState* exec, const Identifier& identifier, bool isConnect, PassRefPtr<QtInstance> instance, int index, const QByteArray& signature)
+{
+ Base::finishCreation(exec, identifier, instance);
+ QW_D(QtRuntimeConnectionMethod);
+
+ d->m_signature = signature;
+ d->m_index = index;
+ d->m_isConnect = isConnect;
+}
+
+EncodedJSValue QtRuntimeConnectionMethod::call(ExecState* exec)
+{
+ QtRuntimeConnectionMethodData* d = static_cast<QtRuntimeConnectionMethod *>(exec->callee())->d_func();
+
+ JSLock lock(SilenceAssertionsOnly);
+
+ QObject* sender = d->m_instance->getObject();
+
+ if (sender) {
+
+ JSObject* thisObject = exec->lexicalGlobalObject()->methodTable()->toThisObject(exec->lexicalGlobalObject(), exec);
+ JSObject* funcObject = 0;
+
+ // QtScript checks signalness first, arguments second
+ int signalIndex = -1;
+
+ // Make sure the initial index is a signal
+ QMetaMethod m = sender->metaObject()->method(d->m_index);
+ if (m.methodType() == QMetaMethod::Signal)
+ signalIndex = findSignalIndex(sender->metaObject(), d->m_index, d->m_signature);
+
+ if (signalIndex != -1) {
+ if (exec->argumentCount() == 1) {
+ funcObject = exec->argument(0).toObject(exec);
+ CallData callData;
+ if (funcObject->methodTable()->getCallData(funcObject, callData) == CallTypeNone) {
+ if (d->m_isConnect)
+ return throwVMError(exec, createTypeError(exec, "QtMetaMethod.connect: target is not a function"));
+ else
+ return throwVMError(exec, createTypeError(exec, "QtMetaMethod.disconnect: target is not a function"));
+ }
+ } else if (exec->argumentCount() >= 2) {
+ if (exec->argument(0).isObject()) {
+ thisObject = exec->argument(0).toObject(exec);
+
+ // Get the actual function to call
+ JSObject *asObj = exec->argument(1).toObject(exec);
+ CallData callData;
+ if (asObj->methodTable()->getCallData(asObj, callData) != CallTypeNone) {
+ // Function version
+ funcObject = asObj;
+ } else {
+ // Convert it to a string
+ UString funcName = exec->argument(1).toString(exec);
+ Identifier funcIdent(exec, funcName);
+
+ // ### DropAllLocks
+ // This is resolved at this point in QtScript
+ JSValue val = thisObject->get(exec, funcIdent);
+ JSObject* asFuncObj = val.toObject(exec);
+
+ if (asFuncObj->methodTable()->getCallData(asFuncObj, callData) != CallTypeNone) {
+ funcObject = asFuncObj;
+ } else {
+ if (d->m_isConnect)
+ return throwVMError(exec, createTypeError(exec, "QtMetaMethod.connect: target is not a function"));
+ else
+ return throwVMError(exec, createTypeError(exec, "QtMetaMethod.disconnect: target is not a function"));
+ }
+ }
+ } else {
+ if (d->m_isConnect)
+ return throwVMError(exec, createTypeError(exec, "QtMetaMethod.connect: thisObject is not an object"));
+ else
+ return throwVMError(exec, createTypeError(exec, "QtMetaMethod.disconnect: thisObject is not an object"));
+ }
+ } else {
+ if (d->m_isConnect)
+ return throwVMError(exec, createError(exec, "QtMetaMethod.connect: no arguments given"));
+ else
+ return throwVMError(exec, createError(exec, "QtMetaMethod.disconnect: no arguments given"));
+ }
+
+ if (d->m_isConnect) {
+ // to connect, we need:
+ // target object [from ctor]
+ // target signal index etc. [from ctor]
+ // receiver function [from arguments]
+ // receiver this object [from arguments]
+
+ ExecState* globalExec = exec->lexicalGlobalObject()->globalExec();
+ QtConnectionObject* conn = QtConnectionObject::createWithInternalJSC(globalExec, d->m_instance, signalIndex, thisObject, funcObject);
+ 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_signature));
+ return throwVMError(exec, createError(exec, msg.toLatin1().constData()));
+ }
+ else {
+ // Store connection
+ connections.insert(sender, conn);
+ }
+ } else {
+ // Now to find our previous connection object. Hmm.
+ QList<QtConnectionObject*> conns = connections.values(sender);
+ bool ret = false;
+
+ JSContextRef context = ::toRef(exec);
+ JSObjectRef receiver = ::toRef(thisObject);
+ JSObjectRef receiverFunction = ::toRef(funcObject);
+
+ foreach(QtConnectionObject* conn, conns) {
+ // Is this the right connection?
+ if (conn->match(context, sender, signalIndex, receiver, receiverFunction)) {
+ // Yep, disconnect it
+ QMetaObject::disconnect(sender, signalIndex, conn, conn->metaObject()->methodOffset());
+ delete conn; // this will also remove it from the map
+ ret = true;
+ break;
+ }
+ }
+
+ if (!ret) {
+ QString msg = QString(QLatin1String("QtMetaMethod.disconnect: failed to disconnect from %1::%2()"))
+ .arg(QLatin1String(sender->metaObject()->className()))
+ .arg(QLatin1String(d->m_signature));
+ return throwVMError(exec, createError(exec, msg.toLatin1().constData()));
+ }
+ }
+ } else {
+ QString msg = QString(QLatin1String("QtMetaMethod.%1: %2::%3() is not a signal"))
+ .arg(QLatin1String(d->m_isConnect ? "connect": "disconnect"))
+ .arg(QLatin1String(sender->metaObject()->className()))
+ .arg(QLatin1String(d->m_signature));
+ return throwVMError(exec, createTypeError(exec, msg.toLatin1().constData()));
+ }
+ } else {
+ return throwVMError(exec, createError(exec, "cannot call function of deleted QObject"));
+ }
+
+ return JSValue::encode(jsUndefined());
+}
+
+CallType QtRuntimeConnectionMethod::getCallData(JSCell*, CallData& callData)
+{
+ callData.native.function = call;
+ return CallTypeHost;
+}
+
+bool QtRuntimeConnectionMethod::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
+{
+ QtRuntimeConnectionMethod* thisObject = jsCast<QtRuntimeConnectionMethod*>(cell);
+ if (propertyName == exec->propertyNames().length) {
+ slot.setCustom(thisObject, thisObject->lengthGetter);
+ return true;
+ }
+
+ return QtRuntimeMethod::getOwnPropertySlot(thisObject, exec, propertyName, slot);
+}
+
+bool QtRuntimeConnectionMethod::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
+{
+ QtRuntimeConnectionMethod* thisObject = jsCast<QtRuntimeConnectionMethod*>(object);
+ if (propertyName == exec->propertyNames().length) {
+ PropertySlot slot;
+ slot.setCustom(thisObject, lengthGetter);
+ descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly | DontEnum);
+ return true;
+ }
+
+ return QtRuntimeMethod::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
+}
+
+void QtRuntimeConnectionMethod::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+{
+ if (mode == IncludeDontEnumProperties)
+ propertyNames.add(exec->propertyNames().length);
+
+ QtRuntimeMethod::getOwnPropertyNames(object, exec, propertyNames, mode);
+}
+
+JSValue QtRuntimeConnectionMethod::lengthGetter(ExecState*, JSValue, const Identifier&)
+{
+ // we have one formal argument, and one optional
+ return jsNumber(1);
+}
+
+// ===============
+
+QtConnectionObject::QtConnectionObject(JSContextRef context, PassRefPtr<QtInstance> senderInstance, int signalIndex, JSObjectRef receiver, JSObjectRef receiverFunction)
+ : QObject(senderInstance->getObject())
+ , m_context(context)
+ , m_senderInstance(senderInstance)
+ , m_originalSender(m_senderInstance->getObject())
+ , m_signalIndex(signalIndex)
+ , m_receiver(receiver)
+ , m_receiverFunction(receiverFunction)
+{
+ JSValueProtect(m_context, m_receiver);
+ JSValueProtect(m_context, m_receiverFunction);
+}
+
+QtConnectionObject::~QtConnectionObject()
+{
+ // We can safely use m_originalSender because connection object will never outlive the sender,
+ // which is its QObject parent.
+ QtRuntimeConnectionMethod::connections.remove(m_originalSender, this);
+
+ JSValueUnprotect(m_context, m_receiver);
+ JSValueUnprotect(m_context, m_receiverFunction);
+}
+
+static const uint qt_meta_data_QtConnectionObject[] = {
+
+ // content:
+ 1, // revision
+ 0, // classname
+ 0, 0, // classinfo
+ 1, 10, // methods
+ 0, 0, // properties
+ 0, 0, // enums/sets
+
+ // slots: signature, parameters, type, tag, flags
+ 28, 27, 27, 27, 0x0a,
+
+ 0 // eod
+};
+
+static const char qt_meta_stringdata_QtConnectionObject[] = {
+ "JSC::Bindings::QtConnectionObject\0\0execute()\0"
+};
+
+const QMetaObject QtConnectionObject::staticMetaObject = {
+ { &QObject::staticMetaObject, qt_meta_stringdata_QtConnectionObject,
+ qt_meta_data_QtConnectionObject, 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))
+ return static_cast<void*>(const_cast<QtConnectionObject*>(this));
+ return QObject::qt_metacast(_clname);
+}
+
+// This is what moc would generate except by the fact that we pass all arguments to our execute() slot.
+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) {
+ switch (_id) {
+ case 0: execute(_a); break;
+ }
+ _id -= 1;
+ }
+ return _id;
+}
+
+static bool isJavaScriptFunction(JSObjectRef object)
+{
+ CallData callData;
+ JSObject* jsObject = toJS(object);
+ return jsObject->methodTable()->getCallData(jsObject, callData) == CallTypeJS;
+}
+
+void QtConnectionObject::execute(void** argv)
+{
+ QObject* sender = m_senderInstance->getObject();
+ if (!sender) {
+ qWarning() << "sender deleted, cannot deliver signal";
+ return;
+ }
+
+ ASSERT(sender == m_originalSender);
+
+ const QMetaObject* meta = sender->metaObject();
+ const QMetaMethod method = meta->method(m_signalIndex);
+
+ QList<QByteArray> parameterTypes = method.parameterTypes();
+
+ JSValueRef* ignoredException = 0;
+ JSRetainPtr<JSStringRef> lengthProperty(JSStringCreateWithUTF8CString("length"));
+ int receiverLength = int(JSValueToNumber(m_context, JSObjectGetProperty(m_context, m_receiverFunction, lengthProperty.get(), ignoredException), ignoredException));
+ int argc = qMax(parameterTypes.count(), receiverLength);
+ WTF::Vector<JSValueRef> args(argc);
+
+ // TODO: remove once conversion functions use JSC API.
+ ExecState* exec = ::toJS(m_context);
+ RefPtr<RootObject> rootObject = m_senderInstance->rootObject();
+
+ for (int i = 0; i < argc; i++) {
+ int argType = QMetaType::type(parameterTypes.at(i));
+ args[i] = ::toRef(exec, convertQVariantToValue(exec, rootObject, QVariant(argType, argv[i+1])));
+ }
+
+ const bool updateQtSender = isJavaScriptFunction(m_receiverFunction);
+ if (updateQtSender)
+ QtInstance::qtSenderStack()->push(QObject::sender());
+
+ JSObjectCallAsFunction(m_context, m_receiverFunction, m_receiver, argc, args.data(), 0);
+
+ if (updateQtSender)
+ QtInstance::qtSenderStack()->pop();
+}
+
+bool QtConnectionObject::match(JSContextRef context, QObject* sender, int signalIndex, JSObjectRef receiver, JSObjectRef receiverFunction)
+{
+ if (sender != m_originalSender || signalIndex != m_signalIndex)
+ return false;
+ JSValueRef* ignoredException = 0;
+ const bool receiverMatch = (!receiver && !m_receiver) || JSValueIsEqual(context, receiver, m_receiver, ignoredException);
+ return receiverMatch && JSValueIsEqual(context, receiverFunction, m_receiverFunction, ignoredException);
+}
+
+QtConnectionObject* QtConnectionObject::createWithInternalJSC(ExecState* exec, PassRefPtr<QtInstance> senderInstance, int signalIndex, JSObject* receiver, JSObject* receiverFunction)
+{
+ return new QtConnectionObject(::toRef(exec), senderInstance, signalIndex, ::toRef(receiver), ::toRef(receiverFunction));
+}
+
+// ===============
+
+template <typename T> QtArray<T>::QtArray(QList<T> list, QMetaType::Type type, PassRefPtr<RootObject> rootObject)
+ : Array(rootObject)
+ , m_list(list)
+ , m_type(type)
+{
+ m_length = m_list.count();
+}
+
+template <typename T> QtArray<T>::~QtArray ()
+{
+}
+
+template <typename T> RootObject* QtArray<T>::rootObject() const
+{
+ return m_rootObject && m_rootObject->isValid() ? m_rootObject.get() : 0;
+}
+
+template <typename T> void QtArray<T>::setValueAt(ExecState* exec, unsigned index, JSValue aValue) const
+{
+ // QtScript sets the value, but doesn't forward it to the original source
+ // (e.g. if you do 'object.intList[5] = 6', the object is not updated, but the
+ // copy of the list is).
+ int dist = -1;
+ QVariant val = convertValueToQVariant(exec, aValue, m_type, &dist);
+
+ if (dist >= 0) {
+ m_list[index] = val.value<T>();
+ }
+}
+
+
+template <typename T> JSValue QtArray<T>::valueAt(ExecState *exec, unsigned int index) const
+{
+ if (index < m_length) {
+ T val = m_list.at(index);
+ return convertQVariantToValue(exec, rootObject(), QVariant::fromValue(val));
+ }
+
+ return jsUndefined();
+}
+
+// ===============
+
+} }