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