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