1/*
2 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
3 *
4 *  This library is free software; you can redistribute it and/or
5 *  modify it under the terms of the GNU Lesser General Public
6 *  License as published by the Free Software Foundation; either
7 *  version 2 of the License, or (at your option) any later version.
8 *
9 *  This library is distributed in the hope that it will be useful,
10 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 *  Lesser General Public License for more details.
13 *
14 *  You should have received a copy of the GNU Lesser General Public
15 *  License along with this library; if not, write to the Free Software
16 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17 *
18 */
19
20#include "config.h"
21#include "qt_runtime.h"
22
23#include "APICast.h"
24#include "APIShims.h"
25#include "BooleanObject.h"
26#include "DateInstance.h"
27#include "DatePrototype.h"
28#include "FunctionPrototype.h"
29#include "Interpreter.h"
30#include "JSArray.h"
31#include "JSContextRefPrivate.h"
32#include "JSDOMBinding.h"
33#include "JSDOMWindow.h"
34#include "JSDocument.h"
35#include "JSGlobalObject.h"
36#include "JSHTMLElement.h"
37#include "JSLock.h"
38#include "JSObject.h"
39#include "JSRetainPtr.h"
40#include "JSUint8ClampedArray.h"
41#include "ObjectPrototype.h"
42#include "PropertyNameArray.h"
43#include "qdatetime.h"
44#include "qdebug.h"
45#include "qmetaobject.h"
46#include "qmetatype.h"
47#include "qobject.h"
48#include "qstringlist.h"
49#include "qt_instance.h"
50#include "qt_pixmapruntime.h"
51#include "qvarlengtharray.h"
52#include <JSFunction.h>
53
54#include <wtf/DateMath.h>
55
56#include <limits.h>
57#include <runtime/Error.h>
58#include <runtime_array.h>
59#include <runtime_object.h>
60
61// QtScript has these
62Q_DECLARE_METATYPE(QObjectList);
63Q_DECLARE_METATYPE(QList<int>);
64Q_DECLARE_METATYPE(QVariant);
65
66using namespace WebCore;
67
68namespace JSC {
69namespace Bindings {
70
71// Debugging
72//#define QTWK_RUNTIME_CONVERSION_DEBUG
73//#define QTWK_RUNTIME_MATCH_DEBUG
74
75class QWKNoDebug
76{
77public:
78    inline QWKNoDebug(){}
79    inline ~QWKNoDebug(){}
80
81    template<typename T>
82    inline QWKNoDebug &operator<<(const T &) { return *this; }
83};
84
85#ifdef QTWK_RUNTIME_CONVERSION_DEBUG
86#define qConvDebug() qDebug()
87#else
88#define qConvDebug() QWKNoDebug()
89#endif
90
91#ifdef QTWK_RUNTIME_MATCH_DEBUG
92#define qMatchDebug() qDebug()
93#else
94#define qMatchDebug() QWKNoDebug()
95#endif
96
97typedef enum {
98    Variant = 0,
99    Number,
100    Boolean,
101    RTString,
102    Date,
103    Array,
104    QObj,
105    Object,
106    Null,
107    RTUint8Array
108} JSRealType;
109
110#if defined(QTWK_RUNTIME_CONVERSION_DEBUG) || defined(QTWK_RUNTIME_MATCH_DEBUG)
111QDebug operator<<(QDebug dbg, const JSRealType &c)
112{
113     const char *map[] = { "Variant", "Number", "Boolean", "RTString", "Date",
114         "Array", "RTObject", "Object", "Null"};
115
116     dbg.nospace() << "JSType(" << ((int)c) << ", " <<  map[c] << ")";
117
118     return dbg.space();
119}
120#endif
121
122void setException(JSContextRef context, JSValueRef* exception, const QString& text)
123{
124    if (!exception)
125        return;
126
127    JSStringRef errorStr = JSStringCreateWithUTF8CString(text.toUtf8());
128    JSValueRef errorVal[] = { JSValueMakeString(context, errorStr) };
129    *exception = JSObjectMakeError(context, 1, errorVal, 0);
130    JSStringRelease(errorStr);
131}
132
133struct RuntimeConversion {
134    ConvertToJSValueFunction toJSValueFunc;
135    ConvertToVariantFunction toVariantFunc;
136};
137
138typedef QHash<int, RuntimeConversion> RuntimeConversionTable;
139Q_GLOBAL_STATIC(RuntimeConversionTable, customRuntimeConversions)
140
141void registerCustomType(int qtMetaTypeId, ConvertToVariantFunction toVariantFunc, ConvertToJSValueFunction toJSValueFunc)
142{
143    RuntimeConversion conversion;
144    conversion.toJSValueFunc = toJSValueFunc;
145    conversion.toVariantFunc = toVariantFunc;
146    customRuntimeConversions()->insert(qtMetaTypeId, conversion);
147}
148
149static bool isJSUint8Array(JSObjectRef object)
150{
151    return toJS(object)->inherits(&JSUint8Array::s_info);
152}
153
154static bool isJSArray(JSObjectRef object)
155{
156    return toJS(object)->inherits(&JSArray::s_info);
157}
158
159static bool isJSDate(JSObjectRef object)
160{
161    return toJS(object)->inherits(&DateInstance::s_info);
162}
163
164static bool isQtObject(JSObjectRef object)
165{
166    return toJS(object)->inherits(&RuntimeObject::s_info);
167}
168
169static JSRealType valueRealType(JSContextRef context, JSValueRef value, JSValueRef* exception)
170{
171    if (JSValueIsNumber(context, value))
172        return Number;
173    if (JSValueIsString(context, value))
174        return RTString;
175    if (JSValueIsBoolean(context, value))
176        return Boolean;
177    if (JSValueIsNull(context, value))
178        return Null;
179    if (!JSValueIsObject(context, value))
180        return RTString; // I don't know.
181
182    JSObjectRef object = JSValueToObject(context, value, exception);
183
184    if (isJSUint8Array(object))
185        return RTUint8Array;
186    if (isJSArray(object))
187            return Array;
188    if (isJSDate(object))
189            return Date;
190    if (isQtObject(object))
191            return QObj;
192
193    return Object;
194}
195
196static QString toString(JSStringRef stringRef)
197{
198    return QString(reinterpret_cast<const QChar*>(JSStringGetCharactersPtr(stringRef)), JSStringGetLength(stringRef));
199}
200
201static JSValueRef unwrapBoxedPrimitive(JSContextRef context, JSValueRef value, JSObjectRef obj)
202{
203    ExecState* exec = toJS(context);
204    APIEntryShim entryShim(exec);
205    JSObject* object = toJS(obj);
206    if (object->inherits(&NumberObject::s_info))
207        return toRef(exec, jsNumber(object->toNumber(exec)));
208    if (object->inherits(&StringObject::s_info))
209        return toRef(exec, object->toString(exec));
210    if (object->inherits(&BooleanObject::s_info))
211        return toRef(exec, object->toPrimitive(exec));
212    return value;
213}
214
215QVariant convertValueToQVariant(JSContextRef, JSValueRef, QMetaType::Type, int*, HashSet<JSObjectRef>*, int, JSValueRef *exception);
216
217static QVariantMap convertValueToQVariantMap(JSContextRef context, JSObjectRef object, HashSet<JSObjectRef>* visitedObjects, int recursionLimit, JSValueRef* exception)
218{
219    QVariantMap result;
220    JSPropertyNameArrayRef properties = JSObjectCopyPropertyNames(context, object);
221    size_t propertyCount = JSPropertyNameArrayGetCount(properties);
222
223    for (size_t i = 0; i < propertyCount; ++i) {
224        JSStringRef name = JSPropertyNameArrayGetNameAtIndex(properties, i);
225
226        int propertyConversionDistance = 0;
227        JSValueRef property = JSObjectGetProperty(context, object, name, exception);
228        QVariant v = convertValueToQVariant(context, property, QMetaType::Void, &propertyConversionDistance, visitedObjects, recursionLimit, exception);
229        if (exception && *exception)
230            *exception = 0;
231        else if (propertyConversionDistance >= 0) {
232            result.insert(toString(name), v);
233        }
234    }
235    JSPropertyNameArrayRelease(properties);
236    return result;
237}
238
239template <typename ItemType>
240QList<ItemType> convertToList(JSContextRef context, JSRealType type, JSObjectRef object,
241                              JSValueRef value, int* distance, HashSet<JSObjectRef>* visitedObjects, int recursionLimit, JSValueRef* exception,
242                              const QMetaType::Type typeId = static_cast<QMetaType::Type>(qMetaTypeId<ItemType>()))
243{
244    QList<ItemType> list;
245    if (type == Array) {
246        static JSStringRef lengthStr = JSStringCreateWithUTF8CString("length");
247        JSValueRef lengthVal = JSObjectGetProperty(context, object, lengthStr, exception);
248        size_t length = JSValueToNumber(context, lengthVal, exception);
249        list.reserve(length);
250        for (size_t i = 0; i < length; ++i) {
251            JSValueRef value = JSObjectGetPropertyAtIndex(context, object, i, exception);
252            int itemDistance = -1;
253            QVariant variant = convertValueToQVariant(context, value, typeId, &itemDistance, visitedObjects, recursionLimit, exception);
254            if (itemDistance >= 0)
255                list << variant.value<ItemType>();
256            else
257                break;
258        }
259        if (list.count() != length)
260            list.clear();
261        else if (distance)
262            *distance = 5;
263    } else {
264        int itemDistance = -1;
265        QVariant variant = convertValueToQVariant(context, value, typeId, &itemDistance, visitedObjects, recursionLimit, exception);
266        if (itemDistance >= 0) {
267            list << variant.value<ItemType>();
268            if (distance)
269                *distance = 10;
270        }
271    }
272    return list;
273}
274
275static QString toQString(JSContextRef context, JSValueRef value)
276{
277    JSRetainPtr<JSStringRef> string(Adopt, JSValueToStringCopy(context, value, 0));
278    if (!string)
279        return QString();
280    return QString(reinterpret_cast<const QChar*>(JSStringGetCharactersPtr(string.get())), JSStringGetLength(string.get()));
281}
282
283static void getGregorianDateTimeUTC(JSContextRef context, JSRealType type, JSValueRef value, JSObjectRef object, JSValueRef* exception, GregorianDateTime* gdt)
284{
285    ExecState* exec = toJS(context);
286    APIEntryShim entryShim(exec);
287    if (type == Date) {
288        JSObject* jsObject = toJS(object);
289        DateInstance* date = asDateInstance(jsObject);
290        gdt->copyFrom(*date->gregorianDateTimeUTC(exec));
291    } else {
292        double ms = JSValueToNumber(context, value, exception);
293        GregorianDateTime convertedGdt;
294        msToGregorianDateTime(exec, ms, /*utc*/ true, convertedGdt);
295        gdt->copyFrom(convertedGdt);
296    }
297}
298
299static QDateTime toQDateTimeUTC(JSContextRef context, JSRealType type, JSValueRef value, JSObjectRef object, JSValueRef* exception)
300{
301    GregorianDateTime gdt;
302    getGregorianDateTimeUTC(context, type, value, object, exception, &gdt);
303    QDate date(gdt.year(), gdt.month() + 1, gdt.monthDay());
304    QTime time(gdt.hour(), gdt.minute(), gdt.second());
305    return QDateTime(date, time, Qt::UTC);
306}
307
308QVariant convertValueToQVariant(JSContextRef context, JSValueRef value, QMetaType::Type hint, int *distance, HashSet<JSObjectRef>* visitedObjects, int recursionLimit, JSValueRef* exception)
309{
310    --recursionLimit;
311
312    if (!value || !recursionLimit)
313        return QVariant();
314
315    JSObjectRef object = 0;
316    if (JSValueIsObject(context, value)) {
317        object = JSValueToObject(context, value, 0);
318        if (visitedObjects->contains(object))
319            return QVariant();
320
321        visitedObjects->add(object);
322
323        value = unwrapBoxedPrimitive(context, value, object);
324    }
325
326    // check magic pointer values before dereferencing value
327    if (JSValueIsNumber(context, value)
328        && std::isnan(JSValueToNumber(context, value, exception))) {
329        if (distance)
330            *distance = -1;
331        return QVariant();
332    }
333
334    if (JSValueIsUndefined(context, value) && hint != QMetaType::QString && hint != (QMetaType::Type) qMetaTypeId<QVariant>()) {
335        if (distance)
336            *distance = -1;
337        return QVariant();
338    }
339
340    JSRealType type = valueRealType(context, value, exception);
341    if (hint == QMetaType::Void) {
342        switch(type) {
343            case Number:
344                hint = QMetaType::Double;
345                break;
346            case Boolean:
347                hint = QMetaType::Bool;
348                break;
349            case RTString:
350            default:
351                hint = QMetaType::QString;
352                break;
353            case Date:
354                hint = QMetaType::QDateTime;
355                break;
356            case Object:
357                hint = QMetaType::QVariantMap;
358                break;
359            case QObj:
360                hint = QMetaType::QObjectStar;
361                break;
362            case RTUint8Array:
363                hint = QMetaType::QByteArray;
364                break;
365            case Array:
366                hint = QMetaType::QVariantList;
367                break;
368        }
369    }
370
371    qConvDebug() << "convertValueToQVariant: jstype is " << type << ", hint is" << hint;
372
373    if (JSValueIsNull(context, value)
374        && hint != QMetaType::QObjectStar
375        && hint != QMetaType::VoidStar
376        && hint != QMetaType::QString
377        && hint != (QMetaType::Type) qMetaTypeId<QVariant>()) {
378        if (distance)
379            *distance = -1;
380        return QVariant();
381    }
382
383    QVariant ret;
384    int dist = -1;
385    switch (hint) {
386        case QMetaType::Bool:
387            ret = QVariant(JSValueToBoolean(context, value));
388            if (type == Boolean)
389                dist = 0;
390            else
391                dist = 10;
392            break;
393
394        case QMetaType::Int:
395        case QMetaType::UInt:
396        case QMetaType::Long:
397        case QMetaType::ULong:
398        case QMetaType::LongLong:
399        case QMetaType::ULongLong:
400        case QMetaType::Short:
401        case QMetaType::UShort:
402        case QMetaType::Float:
403        case QMetaType::Double:
404            ret = QVariant(JSValueToNumber(context, value, 0));
405            ret.convert((QVariant::Type)hint);
406            if (type == Number) {
407                switch (hint) {
408                case QMetaType::Double:
409                    dist = 0;
410                    break;
411                case QMetaType::Float:
412                    dist = 1;
413                    break;
414                case QMetaType::LongLong:
415                case QMetaType::ULongLong:
416                    dist = 2;
417                    break;
418                case QMetaType::Long:
419                case QMetaType::ULong:
420                    dist = 3;
421                    break;
422                case QMetaType::Int:
423                case QMetaType::UInt:
424                    dist = 4;
425                    break;
426                case QMetaType::Short:
427                case QMetaType::UShort:
428                    dist = 5;
429                    break;
430                    break;
431                default:
432                    dist = 10;
433                    break;
434                }
435            } else {
436                dist = 10;
437            }
438            break;
439
440        case QMetaType::QChar:
441            if (type == Number || type == Boolean) {
442                ret = QVariant(QChar((ushort)JSValueToNumber(context, value, 0)));
443                if (type == Boolean)
444                    dist = 3;
445                else
446                    dist = 6;
447            } else {
448                JSRetainPtr<JSStringRef> str(Adopt, JSValueToStringCopy(context, value, exception));
449                QChar ch;
450                if (str && JSStringGetLength(str.get()) > 0)
451                    ch = *reinterpret_cast<const QChar*>(JSStringGetCharactersPtr(str.get()));
452                ret = QVariant(ch);
453                if (type == RTString)
454                    dist = 3;
455                else
456                    dist = 10;
457            }
458            break;
459
460        case QMetaType::QString: {
461            if (JSValueIsNull(context, value) || JSValueIsUndefined(context, value)) {
462                if (distance)
463                    *distance = 1;
464                return QString();
465            }
466            JSRetainPtr<JSStringRef> str(Adopt, JSValueToStringCopy(context, value, exception));
467            if (str) {
468                QString string(reinterpret_cast<const QChar*>(JSStringGetCharactersPtr(str.get())), JSStringGetLength(str.get()));
469                ret = QVariant(string);
470                if (type == RTString)
471                    dist = 0;
472                else
473                    dist = 10;
474            }
475            break;
476        }
477
478        case QMetaType::QVariantMap:
479            if (type == Object || type == Array) {
480                ret = QVariant(convertValueToQVariantMap(context, object, visitedObjects, recursionLimit, exception));
481                // Those types can still have perfect matches, e.g. 'bool' if value is a Boolean Object.
482                dist = 1;
483            }
484            break;
485
486        case QMetaType::QVariantList:
487            ret = QVariant(convertToList<QVariant>(context, type, object, value, &dist, visitedObjects, recursionLimit, exception, QMetaType::Void));
488            break;
489
490        case QMetaType::QStringList: {
491            ret = QVariant(convertToList<QString>(context, type, object, value, &dist, visitedObjects, recursionLimit, exception));
492            break;
493        }
494
495        case QMetaType::QByteArray: {
496            if (type == RTUint8Array) {
497                WTF::Uint8Array* arr = toUint8Array(toJS(toJS(context), value));
498                ret = QVariant(QByteArray(reinterpret_cast<const char*>(arr->data()), arr->length()));
499                dist = 0;
500            } else {
501                ret = QVariant(toQString(context, value).toLatin1());
502                if (type == RTString)
503                    dist = 5;
504                else
505                    dist = 10;
506            }
507            break;
508        }
509
510        case QMetaType::QDateTime:
511        case QMetaType::QDate:
512        case QMetaType::QTime:
513            if (type == Date || type == Number) {
514                QDateTime dt = toQDateTimeUTC(context, type, value, object, exception);
515                const bool isNumber = (type == Number);
516                if (hint == QMetaType::QDateTime) {
517                    ret = dt;
518                    dist = isNumber ? 6 : 0;
519                } else if (hint == QMetaType::QDate) {
520                    ret = dt.date();
521                    dist = isNumber ? 8 : 1;
522                } else {
523                    ret = dt.time();
524                    dist = isNumber ? 10 : 2;
525                }
526            } else if (type == RTString) {
527                QString qstring = toQString(context, value);
528                if (hint == QMetaType::QDateTime) {
529                    QDateTime dt = QDateTime::fromString(qstring, Qt::ISODate);
530                    if (!dt.isValid())
531                        dt = QDateTime::fromString(qstring, Qt::TextDate);
532                    if (!dt.isValid())
533                        dt = QDateTime::fromString(qstring, Qt::SystemLocaleDate);
534                    if (!dt.isValid())
535                        dt = QDateTime::fromString(qstring, Qt::LocaleDate);
536                    if (dt.isValid()) {
537                        ret = dt;
538                        dist = 2;
539                    }
540                } else if (hint == QMetaType::QDate) {
541                    QDate dt = QDate::fromString(qstring, Qt::ISODate);
542                    if (!dt.isValid())
543                        dt = QDate::fromString(qstring, Qt::TextDate);
544                    if (!dt.isValid())
545                        dt = QDate::fromString(qstring, Qt::SystemLocaleDate);
546                    if (!dt.isValid())
547                        dt = QDate::fromString(qstring, Qt::LocaleDate);
548                    if (dt.isValid()) {
549                        ret = dt;
550                        dist = 3;
551                    }
552                } else {
553                    QTime dt = QTime::fromString(qstring, Qt::ISODate);
554                    if (!dt.isValid())
555                        dt = QTime::fromString(qstring, Qt::TextDate);
556                    if (!dt.isValid())
557                        dt = QTime::fromString(qstring, Qt::SystemLocaleDate);
558                    if (!dt.isValid())
559                        dt = QTime::fromString(qstring, Qt::LocaleDate);
560                    if (dt.isValid()) {
561                        ret = dt;
562                        dist = 3;
563                    }
564                }
565            }
566            break;
567
568        case QMetaType::QObjectStar:
569            if (type == QObj) {
570                QtInstance* qtinst = QtInstance::getInstance(toJS(object));
571                if (qtinst) {
572                    if (qtinst->getObject()) {
573                        qConvDebug() << "found instance, with object:" << (void*) qtinst->getObject();
574                        ret = QVariant::fromValue(qtinst->getObject());
575                        qConvDebug() << ret;
576                        dist = 0;
577                    } else {
578                        qConvDebug() << "can't convert deleted qobject";
579                    }
580                } else {
581                    qConvDebug() << "wasn't a qtinstance";
582                }
583            } else if (type == Null) {
584                QObject* nullobj = 0;
585                ret = QVariant::fromValue(nullobj);
586                dist = 0;
587            } else {
588                qConvDebug() << "previous type was not an object:" << type;
589            }
590            break;
591
592        case QMetaType::VoidStar:
593            if (type == QObj) {
594                QtInstance* qtinst = QtInstance::getInstance(toJS(object));
595                if (qtinst) {
596                    if (qtinst->getObject()) {
597                        qConvDebug() << "found instance, with object:" << (void*) qtinst->getObject();
598                        ret = QVariant::fromValue((void *)qtinst->getObject());
599                        qConvDebug() << ret;
600                        dist = 0;
601                    } else {
602                        qConvDebug() << "can't convert deleted qobject";
603                    }
604                } else {
605                    qConvDebug() << "wasn't a qtinstance";
606                }
607            } else if (type == Null) {
608                ret = QVariant::fromValue((void*)0);
609                dist = 0;
610            } else if (type == Number) {
611                // I don't think that converting a double to a pointer is a wise
612                // move.  Except maybe 0.
613                qConvDebug() << "got number for void * - not converting, seems unsafe:" << JSValueToNumber(context, value, 0);
614            } else {
615                qConvDebug() << "void* - unhandled type" << type;
616            }
617            break;
618
619        default:
620            // Non const type ids
621            if (hint == (QMetaType::Type) qMetaTypeId<QObjectList>()) {
622                ret = QVariant::fromValue(convertToList<QObject*>(context, type, object, value, &dist, visitedObjects, recursionLimit, exception));
623                break;
624            }
625            if (hint == (QMetaType::Type) qMetaTypeId<QList<int> >()) {
626                ret = QVariant::fromValue(convertToList<int>(context, type, object, value, &dist, visitedObjects, recursionLimit, exception));
627                break;
628            }
629            if (QtPixmapRuntime::canHandle(static_cast<QMetaType::Type>(hint))) {
630                ret = QtPixmapRuntime::toQt(context, object, static_cast<QMetaType::Type>(hint), exception);
631            } else if (customRuntimeConversions()->contains(hint)) {
632                ret = customRuntimeConversions()->value(hint).toVariantFunc(toJS(object), &dist, visitedObjects);
633                if (dist == 0)
634                    break;
635            } else if (hint == (QMetaType::Type) qMetaTypeId<QVariant>()) {
636                if (JSValueIsNull(context, value) || JSValueIsUndefined(context, value)) {
637                    if (distance)
638                        *distance = 1;
639                    return QVariant();
640                }
641                if (type == Object) {
642                    // Since we haven't really visited this object yet, we remove it
643                    visitedObjects->remove(object);
644                }
645
646                // And then recurse with the autodetect flag
647                ret = convertValueToQVariant(context, value, QMetaType::Void, distance, visitedObjects, recursionLimit, exception);
648                dist = 10;
649                break;
650            }
651
652            dist = 10;
653            break;
654    }
655
656    if (!ret.isValid())
657        dist = -1;
658    if (distance)
659        *distance = dist;
660
661    return ret;
662}
663
664QVariant convertValueToQVariant(JSContextRef context, JSValueRef value, QMetaType::Type hint, int *distance, JSValueRef *exception)
665{
666    const int recursionLimit = 200;
667    HashSet<JSObjectRef> visitedObjects;
668    return convertValueToQVariant(context, value, hint, distance, &visitedObjects, recursionLimit, exception);
669}
670
671JSValueRef convertQVariantToValue(JSContextRef context, PassRefPtr<RootObject> root, const QVariant& variant, JSValueRef *exception)
672{
673    // Variants with QObject * can be isNull but not a null pointer
674    // An empty QString variant is also null
675    QMetaType::Type type = (QMetaType::Type) variant.userType();
676
677    qConvDebug() << "convertQVariantToValue: metatype:" << type << ", isnull: " << variant.isNull();
678    if (variant.isNull() &&
679        !QMetaType::typeFlags(type).testFlag(QMetaType::PointerToQObject) &&
680        type != QMetaType::VoidStar &&
681        type != QMetaType::QString) {
682        return JSValueMakeNull(context);
683    }
684
685    if (type == QMetaType::Bool)
686        return JSValueMakeBoolean(context, variant.toBool());
687
688    if (type == QMetaType::Int ||
689        type == QMetaType::UInt ||
690        type == QMetaType::Long ||
691        type == QMetaType::ULong ||
692        type == QMetaType::LongLong ||
693        type == QMetaType::ULongLong ||
694        type == QMetaType::Short ||
695        type == QMetaType::UShort ||
696        type == QMetaType::Float ||
697        type == QMetaType::Double)
698        return JSValueMakeNumber(context, variant.toDouble());
699
700    if (type == QMetaType::QDateTime ||
701        type == QMetaType::QDate ||
702        type == QMetaType::QTime) {
703
704        QDate date = QDate::currentDate();
705        QTime time(0,0,0); // midnight
706
707        if (type == QMetaType::QDate)
708            date = variant.value<QDate>();
709        else if (type == QMetaType::QTime)
710            time = variant.value<QTime>();
711        else {
712            QDateTime dt = variant.value<QDateTime>().toLocalTime();
713            date = dt.date();
714            time = dt.time();
715        }
716
717        // Dates specified this way are in local time (we convert DateTimes above)
718        const JSValueRef arguments[] = {
719            JSValueMakeNumber(context, date.year()),
720            JSValueMakeNumber(context, date.month() - 1),
721            JSValueMakeNumber(context, date.day()),
722            JSValueMakeNumber(context, time.hour()),
723            JSValueMakeNumber(context, time.minute()),
724            JSValueMakeNumber(context, time.second()),
725            JSValueMakeNumber(context, time.msec())
726        };
727        return JSObjectMakeDate(context, 7, arguments, exception);
728    }
729
730    if (type == QMetaType::QByteArray) {
731        QByteArray qtByteArray = variant.value<QByteArray>();
732        WTF::RefPtr<WTF::Uint8ClampedArray> wtfByteArray = WTF::Uint8ClampedArray::createUninitialized(qtByteArray.length());
733        memcpy(wtfByteArray->data(), qtByteArray.constData(), qtByteArray.length());
734        ExecState* exec = toJS(context);
735        APIEntryShim entryShim(exec);
736        return toRef(exec, toJS(exec, static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()), wtfByteArray.get()));
737    }
738
739    if (QMetaType::typeFlags(type).testFlag(QMetaType::PointerToQObject)) {
740        QObject* obj = variant.value<QObject*>();
741        if (!obj)
742            return JSValueMakeNull(context);
743        ExecState* exec = toJS(context);
744        APIEntryShim entryShim(exec);
745        return toRef(exec, QtInstance::getQtInstance(obj, root, QtInstance::QtOwnership)->createRuntimeObject(exec));
746    }
747
748    if (QtPixmapRuntime::canHandle(static_cast<QMetaType::Type>(variant.type())))
749        return QtPixmapRuntime::toJS(context, variant, exception);
750
751    if (customRuntimeConversions()->contains(type)) {
752        if (!root->globalObject()->inherits(&JSDOMWindow::s_info))
753            return JSValueMakeUndefined(context);
754
755        Document* document = (static_cast<JSDOMWindow*>(root->globalObject()))->impl()->document();
756        if (!document)
757            return JSValueMakeUndefined(context);
758        ExecState* exec = toJS(context);
759        APIEntryShim entryShim(exec);
760        return toRef(exec, customRuntimeConversions()->value(type).toJSValueFunc(exec, toJSDOMGlobalObject(document, exec), variant));
761    }
762
763    if (type == QMetaType::QVariantMap) {
764        // create a new object, and stuff properties into it
765        JSObjectRef ret = JSObjectMake(context, 0, 0);
766        QVariantMap map = variant.value<QVariantMap>();
767        QVariantMap::const_iterator i = map.constBegin();
768        while (i != map.constEnd()) {
769            QString s = i.key();
770            JSStringRef propertyName = JSStringCreateWithCharacters(reinterpret_cast<const JSChar*>(s.constData()), s.length());
771            JSValueRef propertyValue = convertQVariantToValue(context, root.get(), i.value(), /*ignored exception*/0);
772            if (propertyValue)
773                JSObjectSetProperty(context, ret, propertyName, propertyValue, kJSPropertyAttributeNone, /*ignored exception*/0);
774            JSStringRelease(propertyName);
775            ++i;
776        }
777
778        return ret;
779    }
780
781    // List types
782    if (type == QMetaType::QVariantList) {
783        // ### TODO: Could use special array class that lazily converts.
784        // See https://bugs.webkit.org/show_bug.cgi?id=94691
785        QVariantList vl = variant.toList();
786        JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception);
787        if (exception && *exception)
788            return array;
789        for (int i = 0; i < vl.count(); ++i) {
790            JSValueRef property = convertQVariantToValue(context, root.get(), vl.at(i), /*ignored exception*/0);
791            if (property)
792                JSObjectSetPropertyAtIndex(context, array, i, property, /*ignored exception*/0);
793        }
794        return array;
795    } else if (type == QMetaType::QStringList) {
796        QStringList sl = variant.value<QStringList>();
797        JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception);
798        for (int i = 0; i < sl.count(); ++i) {
799            const QString& s = sl.at(i);
800            JSStringRef jsString = JSStringCreateWithCharacters(reinterpret_cast<const JSChar*>(s.constData()), s.length());
801            JSObjectSetPropertyAtIndex(context, array, i, JSValueMakeString(context, jsString), /*ignored exception*/0);
802            JSStringRelease(jsString);
803        }
804        return array;
805    } else if (type == static_cast<QMetaType::Type>(qMetaTypeId<QObjectList>())) {
806        QObjectList ol = variant.value<QObjectList>();
807        JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception);
808        ExecState* exec = toJS(context);
809        APIEntryShim entryShim(exec);
810        for (int i = 0; i < ol.count(); ++i) {
811            JSValueRef jsObject = toRef(exec, QtInstance::getQtInstance(ol.at(i), root, QtInstance::QtOwnership)->createRuntimeObject(exec));
812            JSObjectSetPropertyAtIndex(context, array, i, jsObject, /*ignored exception*/0);
813        }
814        return array;
815    } else if (type == static_cast<QMetaType::Type>(qMetaTypeId<QList<int> >())) {
816        QList<int> il = variant.value<QList<int> >();
817        JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception);
818        for (int i = 0; i < il.count(); ++i)
819            JSObjectSetPropertyAtIndex(context, array, i, JSValueMakeNumber(context, il.at(i)), /*ignored exception*/0);
820        return array;
821    }
822
823    if (type == (QMetaType::Type)qMetaTypeId<QVariant>()) {
824        QVariant real = variant.value<QVariant>();
825        qConvDebug() << "real variant is:" << real;
826        return convertQVariantToValue(context, root.get(), real, exception);
827    }
828
829    qConvDebug() << "fallback path for" << variant << variant.userType();
830
831    QString string = variant.toString();
832    JSStringRef jsstring = JSStringCreateWithCharacters(reinterpret_cast<const JSChar*>(string.constData()), string.length());
833    JSValueRef value = JSValueMakeString(context, jsstring);
834    JSStringRelease(jsstring);
835    return value;
836}
837
838// Type conversion metadata (from QtScript originally)
839class QtMethodMatchType
840{
841public:
842    enum Kind {
843        Invalid,
844        Variant,
845        MetaType,
846        Unresolved,
847        MetaEnum
848    };
849
850
851    QtMethodMatchType()
852        : m_kind(Invalid) { }
853
854    Kind kind() const
855    { return m_kind; }
856
857    QMetaType::Type typeId() const;
858
859    bool isValid() const
860    { return (m_kind != Invalid); }
861
862    bool isVariant() const
863    { return (m_kind == Variant); }
864
865    bool isMetaType() const
866    { return (m_kind == MetaType); }
867
868    bool isUnresolved() const
869    { return (m_kind == Unresolved); }
870
871    bool isMetaEnum() const
872    { return (m_kind == MetaEnum); }
873
874    QByteArray name() const;
875
876    int enumeratorIndex() const
877    { Q_ASSERT(isMetaEnum()); return m_typeId; }
878
879    static QtMethodMatchType variant()
880    { return QtMethodMatchType(Variant); }
881
882    static QtMethodMatchType metaType(int typeId, const QByteArray &name)
883    { return QtMethodMatchType(MetaType, typeId, name); }
884
885    static QtMethodMatchType metaEnum(int enumIndex, const QByteArray &name)
886    { return QtMethodMatchType(MetaEnum, enumIndex, name); }
887
888    static QtMethodMatchType unresolved(const QByteArray &name)
889    { return QtMethodMatchType(Unresolved, /*typeId=*/0, name); }
890
891private:
892    QtMethodMatchType(Kind kind, int typeId = 0, const QByteArray &name = QByteArray())
893        : m_kind(kind), m_typeId(typeId), m_name(name) { }
894
895    Kind m_kind;
896    int m_typeId;
897    QByteArray m_name;
898};
899
900QMetaType::Type QtMethodMatchType::typeId() const
901{
902    if (isVariant())
903        return (QMetaType::Type) qMetaTypeId<QVariant>();
904    return (QMetaType::Type) (isMetaEnum() ? QMetaType::Int : m_typeId);
905}
906
907QByteArray QtMethodMatchType::name() const
908{
909    if (!m_name.isEmpty())
910        return m_name;
911    else if (m_kind == Variant)
912        return "QVariant";
913    return QByteArray();
914}
915
916struct QtMethodMatchData
917{
918    int matchDistance;
919    int index;
920    QVector<QtMethodMatchType> types;
921    QVarLengthArray<QVariant, 10> args;
922
923    QtMethodMatchData(int dist, int idx, QVector<QtMethodMatchType> typs,
924                                const QVarLengthArray<QVariant, 10> &as)
925        : matchDistance(dist), index(idx), types(typs), args(as) { }
926    QtMethodMatchData()
927        : index(-1) { }
928
929    bool isValid() const
930    { return (index != -1); }
931
932    int firstUnresolvedIndex() const
933    {
934        for (int i=0; i < types.count(); i++) {
935            if (types.at(i).isUnresolved())
936                return i;
937        }
938        return -1;
939    }
940};
941
942static int indexOfMetaEnum(const QMetaObject *meta, const QByteArray &str)
943{
944    QByteArray scope;
945    QByteArray name;
946    int scopeIdx = str.indexOf("::");
947    if (scopeIdx != -1) {
948        scope = str.left(scopeIdx);
949        name = str.mid(scopeIdx + 2);
950    } else {
951        name = str;
952    }
953    for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
954        QMetaEnum m = meta->enumerator(i);
955        if ((m.name() == name)/* && (scope.isEmpty() || (m.scope() == scope))*/)
956            return i;
957    }
958    return -1;
959}
960
961// Helper function for resolving methods
962// Largely based on code in QtScript for compatibility reasons
963static int findMethodIndex(JSContextRef context,
964                           const QMetaObject* meta,
965                           const QByteArray& signature,
966                           int argumentCount,
967                           const JSValueRef arguments[],
968                           bool allowPrivate,
969                           QVarLengthArray<QVariant, 10> &vars,
970                           void** vvars,
971                           JSValueRef* exception)
972{
973    QList<int> matchingIndices;
974
975    bool overloads = !signature.contains('(');
976
977    int count = meta->methodCount();
978    for (int i = count - 1; i >= 0; --i) {
979        const QMetaMethod m = meta->method(i);
980
981        // Don't choose private methods
982        if (m.access() == QMetaMethod::Private && !allowPrivate)
983            continue;
984
985        // try and find all matching named methods
986        if (!overloads && m.methodSignature() == signature)
987            matchingIndices.append(i);
988        else if (overloads && m.name() == signature)
989            matchingIndices.append(i);
990    }
991
992    int chosenIndex = -1;
993    QVector<QtMethodMatchType> chosenTypes;
994
995    QVarLengthArray<QVariant, 10> args;
996    QVector<QtMethodMatchData> candidates;
997    QVector<QtMethodMatchData> unresolved;
998    QVector<int> tooFewArgs;
999    QVector<int> conversionFailed;
1000
1001    foreach(int index, matchingIndices) {
1002        QMetaMethod method = meta->method(index);
1003
1004        QVector<QtMethodMatchType> types;
1005        bool unresolvedTypes = false;
1006
1007        // resolve return type
1008        QByteArray returnTypeName = method.typeName();
1009        int rtype = method.returnType();
1010        if (rtype == QMetaType::UnknownType) {
1011            if (returnTypeName.endsWith('*')) {
1012                types.append(QtMethodMatchType::metaType(QMetaType::VoidStar, returnTypeName));
1013            } else {
1014                int enumIndex = indexOfMetaEnum(meta, returnTypeName);
1015                if (enumIndex != -1)
1016                    types.append(QtMethodMatchType::metaEnum(enumIndex, returnTypeName));
1017                else {
1018                    unresolvedTypes = true;
1019                    types.append(QtMethodMatchType::unresolved(returnTypeName));
1020                }
1021            }
1022        } else {
1023            if (rtype == QMetaType::QVariant)
1024                types.append(QtMethodMatchType::variant());
1025            else
1026                types.append(QtMethodMatchType::metaType(rtype, returnTypeName));
1027        }
1028
1029        // resolve argument types
1030        QList<QByteArray> parameterTypeNames = method.parameterTypes();
1031        for (int i = 0; i < parameterTypeNames.count(); ++i) {
1032            QByteArray argTypeName = parameterTypeNames.at(i);
1033            int atype = method.parameterType(i);
1034            if (atype == QMetaType::UnknownType) {
1035                int enumIndex = indexOfMetaEnum(meta, argTypeName);
1036                if (enumIndex != -1)
1037                    types.append(QtMethodMatchType::metaEnum(enumIndex, argTypeName));
1038                else {
1039                    unresolvedTypes = true;
1040                    types.append(QtMethodMatchType::unresolved(argTypeName));
1041                }
1042            } else {
1043                if (atype == QMetaType::QVariant)
1044                    types.append(QtMethodMatchType::variant());
1045                else
1046                    types.append(QtMethodMatchType::metaType(atype, argTypeName));
1047            }
1048        }
1049
1050        // If the native method requires more arguments than what was passed from JavaScript
1051        if (argumentCount + 1 < static_cast<unsigned>(types.count())) {
1052            qMatchDebug() << "Match:too few args for" << method.methodSignature();
1053            tooFewArgs.append(index);
1054            continue;
1055        }
1056
1057        if (unresolvedTypes) {
1058            qMatchDebug() << "Match:unresolved arg types for" << method.methodSignature();
1059            // remember it so we can give an error message later, if necessary
1060            unresolved.append(QtMethodMatchData(/*matchDistance=*/INT_MAX, index,
1061                                                   types, QVarLengthArray<QVariant, 10>()));
1062            continue;
1063        }
1064
1065        // Now convert arguments
1066        if (args.count() != types.count())
1067            args.resize(types.count());
1068
1069        QtMethodMatchType retType = types[0];
1070        if (retType.typeId() != QMetaType::Void)
1071            args[0] = QVariant(retType.typeId(), (void *)0); // the return value
1072
1073        bool converted = true;
1074        int matchDistance = 0;
1075        for (unsigned i = 0; converted && i + 1 < static_cast<unsigned>(types.count()); ++i) {
1076            JSValueRef arg = i < argumentCount ? arguments[i] : JSValueMakeUndefined(context);
1077
1078            int argdistance = -1;
1079            QVariant v = convertValueToQVariant(context, arg, types.at(i+1).typeId(), &argdistance, exception);
1080            if (argdistance >= 0) {
1081                matchDistance += argdistance;
1082                args[i+1] = v;
1083            } else {
1084                qMatchDebug() << "failed to convert argument " << i << "type" << types.at(i+1).typeId() << QMetaType::typeName(types.at(i+1).typeId());
1085                converted = false;
1086            }
1087        }
1088
1089        qMatchDebug() << "Match: " << method.methodSignature() << (converted ? "converted":"failed to convert") << "distance " << matchDistance;
1090
1091        if (converted) {
1092            if ((argumentCount + 1 == static_cast<unsigned>(types.count()))
1093                && (matchDistance == 0)) {
1094                // perfect match, use this one
1095                chosenIndex = index;
1096                chosenTypes = types;
1097                break;
1098            }
1099            QtMethodMatchData currentMatch(matchDistance, index, types, args);
1100            if (candidates.isEmpty())
1101                candidates.append(currentMatch);
1102            else {
1103                QtMethodMatchData bestMatchSoFar = candidates.at(0);
1104                if ((args.count() > bestMatchSoFar.args.count())
1105                    || ((args.count() == bestMatchSoFar.args.count())
1106                    && (matchDistance <= bestMatchSoFar.matchDistance)))
1107                    candidates.prepend(currentMatch);
1108                else
1109                    candidates.append(currentMatch);
1110            }
1111        } else {
1112            conversionFailed.append(index);
1113        }
1114
1115        if (!overloads)
1116            break;
1117    }
1118
1119    if (chosenIndex == -1 && candidates.count() == 0) {
1120        // No valid functions at all - format an error message
1121        if (!conversionFailed.isEmpty()) {
1122            QString message = QString::fromLatin1("incompatible type of argument(s) in call to %0(); candidates were\n")
1123                              .arg(QString::fromLatin1(signature));
1124            for (int i = 0; i < conversionFailed.size(); ++i) {
1125                if (i > 0)
1126                    message += QLatin1String("\n");
1127                QMetaMethod mtd = meta->method(conversionFailed.at(i));
1128                message += QString::fromLatin1("    %0").arg(QString::fromLatin1(mtd.methodSignature()));
1129            }
1130            setException(context, exception, message);
1131        } else if (!unresolved.isEmpty()) {
1132            QtMethodMatchData argsInstance = unresolved.first();
1133            int unresolvedIndex = argsInstance.firstUnresolvedIndex();
1134            Q_ASSERT(unresolvedIndex != -1);
1135            QtMethodMatchType unresolvedType = argsInstance.types.at(unresolvedIndex);
1136            QString message = QString::fromLatin1("cannot call %0(): unknown type `%1'")
1137                .arg(QString::fromLatin1(signature))
1138                .arg(QLatin1String(unresolvedType.name()));
1139            setException(context, exception, message);
1140        } else {
1141            QString message = QString::fromLatin1("too few arguments in call to %0(); candidates are\n")
1142                              .arg(QString::fromLatin1(signature));
1143            for (int i = 0; i < tooFewArgs.size(); ++i) {
1144                if (i > 0)
1145                    message += QLatin1String("\n");
1146                QMetaMethod mtd = meta->method(tooFewArgs.at(i));
1147                message += QString::fromLatin1("    %0").arg(QString::fromLatin1(mtd.methodSignature()));
1148            }
1149            setException(context, exception, message);
1150        }
1151    }
1152
1153    if (chosenIndex == -1 && candidates.count() > 0) {
1154        QtMethodMatchData bestMatch = candidates.at(0);
1155        if ((candidates.size() > 1)
1156            && (bestMatch.args.count() == candidates.at(1).args.count())
1157            && (bestMatch.matchDistance == candidates.at(1).matchDistance)) {
1158            // ambiguous call
1159            QString message = QString::fromLatin1("ambiguous call of overloaded function %0(); candidates were\n")
1160                                .arg(QLatin1String(signature));
1161            for (int i = 0; i < candidates.size(); ++i) {
1162                // Only candidate for overload if argument count and match distance is same as best match
1163                if (candidates.at(i).args.count() == bestMatch.args.count()
1164                    || candidates.at(i).matchDistance == bestMatch.matchDistance) {
1165                    if (i > 0)
1166                        message += QLatin1String("\n");
1167                    QMetaMethod mtd = meta->method(candidates.at(i).index);
1168                    message += QString::fromLatin1("    %0").arg(QString::fromLatin1(mtd.methodSignature()));
1169                }
1170            }
1171            setException(context, exception, message);
1172        } else {
1173            chosenIndex = bestMatch.index;
1174            chosenTypes = bestMatch.types;
1175            args = bestMatch.args;
1176        }
1177    }
1178
1179    if (chosenIndex != -1) {
1180        /* Copy the stuff over */
1181        int i;
1182        vars.resize(args.count());
1183        for (i=0; i < args.count(); i++) {
1184            vars[i] = args[i];
1185            if (chosenTypes[i].isVariant())
1186                vvars[i] = &vars[i];
1187            else
1188                vvars[i] = vars[i].data();
1189        }
1190    }
1191
1192    return chosenIndex;
1193}
1194
1195// Signals are not fuzzy matched as much as methods
1196static int findSignalIndex(const QMetaObject* meta, int initialIndex, QByteArray signature)
1197{
1198    int index = initialIndex;
1199    QMetaMethod method = meta->method(index);
1200    bool overloads = !signature.contains('(');
1201    if (overloads && (method.attributes() & QMetaMethod::Cloned)) {
1202        // find the most general method
1203        do {
1204            method = meta->method(--index);
1205        } while (method.attributes() & QMetaMethod::Cloned);
1206    }
1207    return index;
1208}
1209
1210static JSClassRef prototypeForSignalsAndSlots()
1211{
1212    static JSClassDefinition classDef = {
1213        0, kJSClassAttributeNoAutomaticPrototype, 0, 0, 0, 0,
1214        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1215    };
1216    static JSClassRef cls = JSClassCreate(&classDef);
1217    return cls;
1218}
1219
1220QtRuntimeMethod::QtRuntimeMethod(JSContextRef ctx, QObject* object, const QByteArray& identifier, int index, int flags, QtInstance* instance)
1221    : m_object(object)
1222    , m_identifier(identifier)
1223    , m_index(index)
1224    , m_flags(flags)
1225    , m_instance(instance)
1226{
1227}
1228
1229QtRuntimeMethod::~QtRuntimeMethod()
1230{
1231}
1232
1233JSValueRef QtRuntimeMethod::call(JSContextRef context, JSObjectRef function, JSObjectRef /*thisObject*/, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
1234{
1235    QtRuntimeMethod* d = toRuntimeMethod(context, function);
1236    if (!d) {
1237        setException(context, exception, QStringLiteral("cannot call function of deleted runtime method"));
1238        return JSValueMakeUndefined(context);
1239    }
1240    QObject* obj = d->m_object;
1241
1242    if (!obj) {
1243        setException(context, exception, QStringLiteral("cannot call function of deleted QObject"));
1244        return JSValueMakeUndefined(context);
1245    }
1246
1247    // Allow for maximum of 10 arguments and size stack arrays accordingly.
1248    if (argumentCount > 10)
1249        return JSValueMakeUndefined(context);
1250
1251    QVarLengthArray<QVariant, 10> vargs;
1252    void* qargs[11];
1253    const QMetaObject* metaObject = obj->metaObject();
1254
1255    int methodIndex = findMethodIndex(context, metaObject, d->m_identifier,  argumentCount, arguments,
1256                                      (d->m_flags & AllowPrivate), vargs, (void **)qargs, exception);
1257
1258    if (QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, methodIndex, qargs) >= 0)
1259        return JSValueMakeUndefined(context);
1260
1261    if (vargs.size() > 0 && metaObject->method(methodIndex).returnType() != QMetaType::Void)
1262        return convertQVariantToValue(context, d->m_instance->rootObject(), vargs[0], exception);
1263
1264    return JSValueMakeUndefined(context);
1265}
1266
1267JSValueRef QtRuntimeMethod::connect(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
1268{
1269    return connectOrDisconnect(context, function, thisObject, argumentCount, arguments, exception, true);
1270}
1271
1272JSValueRef QtRuntimeMethod::disconnect(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
1273{
1274    return connectOrDisconnect(context, function, thisObject, argumentCount, arguments, exception, false);
1275}
1276
1277JSObjectRef QtRuntimeMethod::jsObjectRef(JSContextRef context, JSValueRef* exception)
1278{
1279    if (m_jsObject)
1280        return toRef(m_jsObject.get());
1281
1282    static JSStringRef connectStr = JSStringCreateWithUTF8CString("connect");
1283    static JSStringRef disconnectStr = JSStringCreateWithUTF8CString("disconnect");
1284    JSRetainPtr<JSStringRef> actualNameStr(Adopt, JSStringCreateWithUTF8CString(m_identifier.constData()));
1285
1286    JSObjectRef object = JSObjectMakeFunctionWithCallback(context, actualNameStr.get(), call);
1287
1288    JSObjectRef generalFunctionProto = JSValueToObject(context, JSObjectGetPrototype(context, object), 0);
1289    JSObjectRef runtimeMethodProto = JSObjectMake(context, prototypeForSignalsAndSlots(), this);
1290    JSObjectSetPrototype(context, runtimeMethodProto, generalFunctionProto);
1291
1292    JSObjectSetPrototype(context, object, runtimeMethodProto);
1293
1294    JSObjectRef connectFunction = JSObjectMakeFunctionWithCallback(context, connectStr, connect);
1295    JSObjectSetPrototype(context, connectFunction, runtimeMethodProto);
1296
1297    JSObjectRef disconnectFunction = JSObjectMakeFunctionWithCallback(context, disconnectStr, disconnect);
1298    JSObjectSetPrototype(context, disconnectFunction, runtimeMethodProto);
1299
1300    const JSPropertyAttributes attributes = kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete;
1301    JSObjectSetProperty(context, object, connectStr, connectFunction, attributes, exception);
1302    JSObjectSetProperty(context, object, disconnectStr, disconnectFunction, attributes, exception);
1303
1304    m_jsObject = PassWeak<JSObject>(toJS(object));
1305
1306    return object;
1307}
1308
1309QtRuntimeMethod* QtRuntimeMethod::toRuntimeMethod(JSContextRef context, JSObjectRef object)
1310{
1311    JSObjectRef proto = JSValueToObject(context, JSObjectGetPrototype(context, object), 0);
1312    if (!proto)
1313        return 0;
1314    if (!JSValueIsObjectOfClass(context, proto, prototypeForSignalsAndSlots()))
1315        return 0;
1316    return static_cast<QtRuntimeMethod*>(JSObjectGetPrivate(proto));
1317}
1318
1319JSValueRef QtRuntimeMethod::connectOrDisconnect(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception, bool connect)
1320{
1321    QtRuntimeMethod* d = toRuntimeMethod(context, thisObject);
1322    if (!d)
1323        d = toRuntimeMethod(context, function);
1324    if (!d) {
1325        QString errorStr = QStringLiteral("QtMetaMethod.%1: Cannot connect to/from deleted QObject").arg(connect ?  QStringLiteral("connect") : QStringLiteral("disconnect"));
1326        setException(context, exception, errorStr);
1327        return JSValueMakeUndefined(context);
1328    }
1329
1330    QString functionName = connect ? QStringLiteral("connect") : QStringLiteral("disconnect");
1331
1332    if (!argumentCount) {
1333        QString errorStr = QStringLiteral("QtMetaMethod.%1: no arguments given").arg(connect ?  QStringLiteral("connect") : QStringLiteral("disconnect"));
1334        setException(context, exception, errorStr);
1335        return JSValueMakeUndefined(context);
1336    }
1337
1338    if ((!(d->m_flags & QtRuntimeMethod::MethodIsSignal))) {
1339        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));
1340        return JSValueMakeUndefined(context);
1341    }
1342
1343    QObject* sender = d->m_object.data();
1344
1345    if (!sender) {
1346        setException(context, exception, QStringLiteral("cannot call function of deleted QObject"));
1347        return JSValueMakeUndefined(context);
1348    }
1349
1350    int signalIndex = findSignalIndex(sender->metaObject(), d->m_index, d->m_identifier);
1351
1352    JSObjectRef targetObject = 0;
1353    JSObjectRef targetFunction = 0;
1354
1355    if (argumentCount == 1) {
1356        if (!JSValueIsObject(context, arguments[0])) {
1357            setException(context, exception, QStringLiteral("QtMetaMethod.%1: target is not a function").arg(functionName));
1358            return JSValueMakeUndefined(context);
1359        }
1360        targetFunction = JSValueToObject(context, arguments[0], exception);
1361
1362        // object.signal.connect(someFunction);
1363        if (JSObjectIsFunction(context, targetFunction)) {
1364            // object.signal.connect(otherObject.slot);
1365            if (QtRuntimeMethod* targetMethod = toRuntimeMethod(context, targetFunction))
1366                targetObject = toRef(QtInstance::getQtInstance(targetMethod->m_object.data(), d->m_instance->rootObject(), QtInstance::QtOwnership)->createRuntimeObject(toJS(context)));
1367        } else
1368            targetFunction = 0;
1369    } else {
1370        // object.signal.connect(object, someFunction);
1371        targetObject = JSValueToObject(context, arguments[0], exception);
1372        if (JSValueIsObject(context, arguments[1])) {
1373            JSObjectRef obj = JSValueToObject(context, arguments[1], exception);
1374            if (JSObjectIsFunction(context, obj))
1375                targetFunction = obj;
1376        }
1377        if (!targetFunction) {
1378            // Maybe the second argument is a string
1379            JSValueRef conversionException = 0;
1380            JSRetainPtr<JSStringRef> functionName(Adopt, JSValueToStringCopy(context, arguments[1], &conversionException));
1381            if (functionName && !conversionException) {
1382                JSValueRef functionProperty = JSObjectGetProperty(context, targetObject, functionName.get(), &conversionException);
1383                if (!conversionException && functionProperty && JSValueIsObject(context, functionProperty)) {
1384                    targetFunction = JSValueToObject(context, functionProperty, 0);
1385                    if (!JSObjectIsFunction(context, targetFunction))
1386                        targetFunction = 0;
1387                }
1388            }
1389        }
1390    }
1391
1392    // object.signal.connect(someObject);
1393    if (!targetFunction) {
1394        QString message = QStringLiteral("QtMetaMethod.%1: target is not a function");
1395        if (connect)
1396            message = message.arg(QStringLiteral("connect"));
1397        else
1398            message = message.arg(QStringLiteral("disconnect"));
1399        setException(context, exception, message);
1400        return JSValueMakeUndefined(context);
1401    }
1402
1403    if (connect) {
1404        // to connect, we need:
1405        //  target object [from ctor]
1406        //  target signal index etc. [from ctor]
1407        //  receiver function [from arguments]
1408        //  receiver this object [from arguments]
1409
1410        QtConnectionObject* conn = new QtConnectionObject(context, QtInstance::getQtInstance(sender, d->m_instance->rootObject(), QtInstance::QtOwnership), signalIndex, targetObject, targetFunction);
1411        bool ok = QMetaObject::connect(sender, signalIndex, conn, conn->metaObject()->methodOffset());
1412        if (!ok) {
1413            delete conn;
1414            QString msg = QString(QLatin1String("QtMetaMethod.connect: failed to connect to %1::%2()"))
1415                    .arg(QLatin1String(sender->metaObject()->className()))
1416                    .arg(QLatin1String(d->m_identifier));
1417            setException(context, exception, msg);
1418            return JSValueMakeUndefined(context);
1419        }
1420
1421        // Store connection
1422        QtConnectionObject::connections.insert(sender, conn);
1423
1424        return JSValueMakeUndefined(context);
1425    }
1426
1427    // Now to find our previous connection object.
1428    QList<QtConnectionObject*> conns = QtConnectionObject::connections.values(sender);
1429
1430    foreach (QtConnectionObject* conn, conns) {
1431        // Is this the right connection?
1432        if (!conn->match(context, sender, signalIndex, targetObject, targetFunction))
1433            continue;
1434
1435        // Yep, disconnect it
1436        QMetaObject::disconnect(sender, signalIndex, conn, conn->metaObject()->methodOffset());
1437        delete conn; // this will also remove it from the map
1438        return JSValueMakeUndefined(context);
1439    }
1440
1441    QString msg = QStringLiteral("QtMetaMethod.disconnect: failed to disconnect from %1::%2()")
1442            .arg(QLatin1String(sender->metaObject()->className()))
1443            .arg(QLatin1String(d->m_identifier));
1444
1445    setException(context, exception, msg);
1446    return JSValueMakeUndefined(context);
1447}
1448
1449// ===============
1450
1451QMultiMap<QObject*, QtConnectionObject*> QtConnectionObject::connections;
1452
1453QtConnectionObject::QtConnectionObject(JSContextRef context, PassRefPtr<QtInstance> senderInstance, int signalIndex, JSObjectRef receiver, JSObjectRef receiverFunction)
1454    : QObject(senderInstance->getObject())
1455    , m_context(JSContextGetGlobalContext(context))
1456    , m_rootObject(senderInstance->rootObject())
1457    , m_signalIndex(signalIndex)
1458    , m_receiver(receiver)
1459    , m_receiverFunction(receiverFunction)
1460{
1461    if (m_receiver)
1462        JSValueProtect(m_context, m_receiver);
1463    JSValueProtect(m_context, m_receiverFunction);
1464}
1465
1466QtConnectionObject::~QtConnectionObject()
1467{
1468    connections.remove(parent(), this);
1469
1470    if (m_receiver)
1471        JSValueUnprotect(m_context, m_receiver);
1472    JSValueUnprotect(m_context, m_receiverFunction);
1473}
1474
1475// Begin moc-generated code -- modify with care! Check "HAND EDIT" parts
1476struct qt_meta_stringdata_QtConnectionObject_t {
1477    QByteArrayData data[3];
1478    char stringdata[44];
1479};
1480#define QT_MOC_LITERAL(idx, ofs, len) { \
1481    Q_REFCOUNT_INITIALIZE_STATIC, len, 0, 0, \
1482    offsetof(qt_meta_stringdata_QtConnectionObject_t, stringdata) + ofs \
1483        - idx * sizeof(QByteArrayData) \
1484    }
1485static const qt_meta_stringdata_QtConnectionObject_t qt_meta_stringdata_QtConnectionObject = {
1486    {
1487QT_MOC_LITERAL(0, 0, 33),
1488QT_MOC_LITERAL(1, 34, 7),
1489QT_MOC_LITERAL(2, 42, 0)
1490    },
1491    "JSC::Bindings::QtConnectionObject\0"
1492    "execute\0\0"
1493};
1494#undef QT_MOC_LITERAL
1495
1496static const uint qt_meta_data_QtConnectionObject[] = {
1497
1498 // content:
1499       7,       // revision
1500       0,       // classname
1501       0,    0, // classinfo
1502       1,   14, // methods
1503       0,    0, // properties
1504       0,    0, // enums/sets
1505       0,    0, // constructors
1506       0,       // flags
1507       0,       // signalCount
1508
1509 // slots: name, argc, parameters, tag, flags
1510       1,    0,   19,    2, 0x0a,
1511
1512 // slots: parameters
1513    QMetaType::Void,
1514
1515       0        // eod
1516};
1517
1518void QtConnectionObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
1519{
1520    if (_c == QMetaObject::InvokeMetaMethod) {
1521        Q_ASSERT(staticMetaObject.cast(_o));
1522        QtConnectionObject *_t = static_cast<QtConnectionObject *>(_o);
1523        switch (_id) {
1524        case 0: _t->execute(_a); break; // HAND EDIT: add _a parameter
1525        default: ;
1526        }
1527    }
1528}
1529
1530const QMetaObject QtConnectionObject::staticMetaObject = {
1531    { &QObject::staticMetaObject, qt_meta_stringdata_QtConnectionObject.data,
1532      qt_meta_data_QtConnectionObject, qt_static_metacall, 0, 0 }
1533};
1534
1535const QMetaObject *QtConnectionObject::metaObject() const
1536{
1537    return &staticMetaObject;
1538}
1539
1540void *QtConnectionObject::qt_metacast(const char *_clname)
1541{
1542    if (!_clname) return 0;
1543    if (!strcmp(_clname, qt_meta_stringdata_QtConnectionObject.stringdata))
1544        return static_cast<void*>(const_cast<QtConnectionObject*>(this));
1545    return QObject::qt_metacast(_clname);
1546}
1547
1548int QtConnectionObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
1549{
1550    _id = QObject::qt_metacall(_c, _id, _a);
1551    if (_id < 0)
1552        return _id;
1553    if (_c == QMetaObject::InvokeMetaMethod) {
1554        if (_id < 1)
1555            qt_static_metacall(this, _c, _id, _a);
1556        _id -= 1;
1557    }
1558    return _id;
1559}
1560// End of moc-generated code
1561
1562void QtConnectionObject::execute(void** argv)
1563{
1564    QObject* sender = parent();
1565    const QMetaObject* meta = sender->metaObject();
1566    const QMetaMethod method = meta->method(m_signalIndex);
1567
1568    JSValueRef* ignoredException = 0;
1569    JSRetainPtr<JSStringRef> lengthProperty(Adopt, JSStringCreateWithUTF8CString("length"));
1570    int receiverLength = int(JSValueToNumber(m_context, JSObjectGetProperty(m_context, m_receiverFunction, lengthProperty.get(), ignoredException), ignoredException));
1571    int argc = qMax(method.parameterCount(), receiverLength);
1572    WTF::Vector<JSValueRef> args(argc);
1573
1574    for (int i = 0; i < argc; i++) {
1575        int argType = method.parameterType(i);
1576        args[i] = convertQVariantToValue(m_context, m_rootObject, QVariant(argType, argv[i+1]), ignoredException);
1577    }
1578
1579    JSObjectCallAsFunction(m_context, m_receiverFunction, m_receiver, argc, args.data(), 0);
1580}
1581
1582bool QtConnectionObject::match(JSContextRef context, QObject* sender, int signalIndex, JSObjectRef receiver, JSObjectRef receiverFunction)
1583{
1584    if (sender != parent() || signalIndex != m_signalIndex)
1585        return false;
1586    JSValueRef* ignoredException = 0;
1587    const bool receiverMatch = (!receiver && !m_receiver) || (receiver && m_receiver && JSValueIsEqual(context, receiver, m_receiver, ignoredException));
1588    return receiverMatch && JSValueIsEqual(context, receiverFunction, m_receiverFunction, ignoredException);
1589}
1590
1591} }
1592