[Qt] Port meta method/signal/slot handling in run-time bridge to use 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 JSValue convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& variant)
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         type != QMetaType::QObjectStar &&
787         type != QMetaType::VoidStar &&
788         type != QMetaType::QWidgetStar &&
789         type != QMetaType::QString) {
790         return jsNull();
791     }
792
793     JSLockHolder lock(exec);
794
795     if (type == QMetaType::Bool)
796         return jsBoolean(variant.toBool());
797
798     if (type == QMetaType::Int ||
799         type == QMetaType::UInt ||
800         type == QMetaType::Long ||
801         type == QMetaType::ULong ||
802         type == QMetaType::LongLong ||
803         type == QMetaType::ULongLong ||
804         type == QMetaType::Short ||
805         type == QMetaType::UShort ||
806         type == QMetaType::Float ||
807         type == QMetaType::Double)
808         return jsNumber(variant.toDouble());
809
810     if (type == QMetaType::QDateTime ||
811         type == QMetaType::QDate ||
812         type == QMetaType::QTime) {
813
814         QDate date = QDate::currentDate();
815         QTime time(0,0,0); // midnight
816
817         if (type == QMetaType::QDate)
818             date = variant.value<QDate>();
819         else if (type == QMetaType::QTime)
820             time = variant.value<QTime>();
821         else {
822             QDateTime dt = variant.value<QDateTime>().toLocalTime();
823             date = dt.date();
824             time = dt.time();
825         }
826
827         // Dates specified this way are in local time (we convert DateTimes above)
828         GregorianDateTime dt;
829         dt.setYear(date.year());
830         dt.setMonth(date.month() - 1);
831         dt.setMonthDay(date.day());
832         dt.setHour(time.hour());
833         dt.setMinute(time.minute());
834         dt.setSecond(time.second());
835         dt.setIsDST(-1);
836         double ms = gregorianDateTimeToMS(exec, dt, time.msec(), /*inputIsUTC*/ false);
837
838         return DateInstance::create(exec, exec->lexicalGlobalObject()->dateStructure(), trunc(ms));
839     }
840
841     if (type == QMetaType::QByteArray) {
842         QByteArray qtByteArray = variant.value<QByteArray>();
843         WTF::RefPtr<WTF::Uint8ClampedArray> wtfByteArray = WTF::Uint8ClampedArray::createUninitialized(qtByteArray.length());
844         memcpy(wtfByteArray->data(), qtByteArray.constData(), qtByteArray.length());
845         return toJS(exec, static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()), wtfByteArray.get());
846     }
847
848     if (type == QMetaType::QObjectStar || type == QMetaType::QWidgetStar) {
849         QObject* obj = variant.value<QObject*>();
850         if (!obj)
851             return jsNull();
852         return QtInstance::getQtInstance(obj, root, QtInstance::QtOwnership)->createRuntimeObject(exec);
853     }
854
855     if (QtPixmapInstance::canHandle(static_cast<QMetaType::Type>(variant.type())))
856         return QtPixmapInstance::createPixmapRuntimeObject(exec, root, variant);
857
858     if (customRuntimeConversions()->contains(type)) {
859         if (!root->globalObject()->inherits(&JSDOMWindow::s_info))
860             return jsUndefined();
861
862         Document* document = (static_cast<JSDOMWindow*>(root->globalObject()))->impl()->document();
863         if (!document)
864             return jsUndefined();
865         return customRuntimeConversions()->value(type).toJSValueFunc(exec, toJSDOMGlobalObject(document, exec), variant);
866     }
867
868     if (type == QMetaType::QVariantMap) {
869         // create a new object, and stuff properties into it
870         JSObject* ret = constructEmptyObject(exec);
871         QVariantMap map = variant.value<QVariantMap>();
872         QVariantMap::const_iterator i = map.constBegin();
873         while (i != map.constEnd()) {
874             QString s = i.key();
875             JSValue val = convertQVariantToValue(exec, root.get(), i.value());
876             if (val) {
877                 PutPropertySlot slot;
878                 ret->methodTable()->put(ret, exec, Identifier(&exec->globalData(), reinterpret_cast_ptr<const UChar *>(s.constData()), s.length()), val, slot);
879                 // ### error case?
880             }
881             ++i;
882         }
883
884         return ret;
885     }
886
887     // List types
888     if (type == QMetaType::QVariantList) {
889         QVariantList vl = variant.toList();
890         qConvDebug() << "got a " << vl.count() << " length list:" << vl;
891         return RuntimeArray::create(exec, new QtArray<QVariant>(vl, QMetaType::Void, root));
892     } else if (type == QMetaType::QStringList) {
893         QStringList sl = variant.value<QStringList>();
894         return RuntimeArray::create(exec, new QtArray<QString>(sl, QMetaType::QString, root));
895     } else if (type == (QMetaType::Type) qMetaTypeId<QObjectList>()) {
896         QObjectList ol= variant.value<QObjectList>();
897         return RuntimeArray::create(exec, new QtArray<QObject*>(ol, QMetaType::QObjectStar, root));
898     } else if (type == (QMetaType::Type)qMetaTypeId<QList<int> >()) {
899         QList<int> il= variant.value<QList<int> >();
900         return RuntimeArray::create(exec, new QtArray<int>(il, QMetaType::Int, root));
901     }
902
903     if (type == (QMetaType::Type)qMetaTypeId<QVariant>()) {
904         QVariant real = variant.value<QVariant>();
905         qConvDebug() << "real variant is:" << real;
906         return convertQVariantToValue(exec, root, real);
907     }
908
909     qConvDebug() << "fallback path for" << variant << variant.userType();
910
911     QString string = variant.toString();
912     UString ustring((UChar*)string.utf16(), string.length());
913     return jsString(exec, ustring);
914 }
915
916 // Type conversion metadata (from QtScript originally)
917 class QtMethodMatchType
918 {
919 public:
920     enum Kind {
921         Invalid,
922         Variant,
923         MetaType,
924         Unresolved,
925         MetaEnum
926     };
927
928
929     QtMethodMatchType()
930         : m_kind(Invalid) { }
931
932     Kind kind() const
933     { return m_kind; }
934
935     QMetaType::Type typeId() const;
936
937     bool isValid() const
938     { return (m_kind != Invalid); }
939
940     bool isVariant() const
941     { return (m_kind == Variant); }
942
943     bool isMetaType() const
944     { return (m_kind == MetaType); }
945
946     bool isUnresolved() const
947     { return (m_kind == Unresolved); }
948
949     bool isMetaEnum() const
950     { return (m_kind == MetaEnum); }
951
952     QByteArray name() const;
953
954     int enumeratorIndex() const
955     { Q_ASSERT(isMetaEnum()); return m_typeId; }
956
957     static QtMethodMatchType variant()
958     { return QtMethodMatchType(Variant); }
959
960     static QtMethodMatchType metaType(int typeId, const QByteArray &name)
961     { return QtMethodMatchType(MetaType, typeId, name); }
962
963     static QtMethodMatchType metaEnum(int enumIndex, const QByteArray &name)
964     { return QtMethodMatchType(MetaEnum, enumIndex, name); }
965
966     static QtMethodMatchType unresolved(const QByteArray &name)
967     { return QtMethodMatchType(Unresolved, /*typeId=*/0, name); }
968
969 private:
970     QtMethodMatchType(Kind kind, int typeId = 0, const QByteArray &name = QByteArray())
971         : m_kind(kind), m_typeId(typeId), m_name(name) { }
972
973     Kind m_kind;
974     int m_typeId;
975     QByteArray m_name;
976 };
977
978 QMetaType::Type QtMethodMatchType::typeId() const
979 {
980     if (isVariant())
981         return (QMetaType::Type) QMetaType::type("QVariant");
982     return (QMetaType::Type) (isMetaEnum() ? QMetaType::Int : m_typeId);
983 }
984
985 QByteArray QtMethodMatchType::name() const
986 {
987     if (!m_name.isEmpty())
988         return m_name;
989     else if (m_kind == Variant)
990         return "QVariant";
991     return QByteArray();
992 }
993
994 struct QtMethodMatchData
995 {
996     int matchDistance;
997     int index;
998     QVector<QtMethodMatchType> types;
999     QVarLengthArray<QVariant, 10> args;
1000
1001     QtMethodMatchData(int dist, int idx, QVector<QtMethodMatchType> typs,
1002                                 const QVarLengthArray<QVariant, 10> &as)
1003         : matchDistance(dist), index(idx), types(typs), args(as) { }
1004     QtMethodMatchData()
1005         : index(-1) { }
1006
1007     bool isValid() const
1008     { return (index != -1); }
1009
1010     int firstUnresolvedIndex() const
1011     {
1012         for (int i=0; i < types.count(); i++) {
1013             if (types.at(i).isUnresolved())
1014                 return i;
1015         }
1016         return -1;
1017     }
1018 };
1019
1020 static int indexOfMetaEnum(const QMetaObject *meta, const QByteArray &str)
1021 {
1022     QByteArray scope;
1023     QByteArray name;
1024     int scopeIdx = str.indexOf("::");
1025     if (scopeIdx != -1) {
1026         scope = str.left(scopeIdx);
1027         name = str.mid(scopeIdx + 2);
1028     } else {
1029         name = str;
1030     }
1031     for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
1032         QMetaEnum m = meta->enumerator(i);
1033         if ((m.name() == name)/* && (scope.isEmpty() || (m.scope() == scope))*/)
1034             return i;
1035     }
1036     return -1;
1037 }
1038
1039 // Helper function for resolving methods
1040 // Largely based on code in QtScript for compatibility reasons
1041 static int findMethodIndex(JSContextRef context,
1042                            const QMetaObject* meta,
1043                            const QByteArray& signature,
1044                            int argumentCount,
1045                            const JSValueRef arguments[],
1046                            bool allowPrivate,
1047                            QVarLengthArray<QVariant, 10> &vars,
1048                            void** vvars,
1049                            JSValueRef* exception)
1050 {
1051     QList<int> matchingIndices;
1052
1053     bool overloads = !signature.contains('(');
1054
1055     int count = meta->methodCount();
1056     for (int i = count - 1; i >= 0; --i) {
1057         const QMetaMethod m = meta->method(i);
1058
1059         // Don't choose private methods
1060         if (m.access() == QMetaMethod::Private && !allowPrivate)
1061             continue;
1062
1063         // try and find all matching named methods
1064         if (!overloads && m.methodSignature() == signature)
1065             matchingIndices.append(i);
1066         else if (overloads && m.name() == signature)
1067             matchingIndices.append(i);
1068     }
1069
1070     int chosenIndex = -1;
1071     QVector<QtMethodMatchType> chosenTypes;
1072
1073     QVarLengthArray<QVariant, 10> args;
1074     QVector<QtMethodMatchData> candidates;
1075     QVector<QtMethodMatchData> unresolved;
1076     QVector<int> tooFewArgs;
1077     QVector<int> conversionFailed;
1078
1079     foreach(int index, matchingIndices) {
1080         QMetaMethod method = meta->method(index);
1081
1082         QVector<QtMethodMatchType> types;
1083         bool unresolvedTypes = false;
1084
1085         // resolve return type
1086         QByteArray returnTypeName = method.typeName();
1087         int rtype = method.returnType();
1088         if (rtype == QMetaType::UnknownType) {
1089             if (returnTypeName.endsWith('*')) {
1090                 types.append(QtMethodMatchType::metaType(QMetaType::VoidStar, returnTypeName));
1091             } else {
1092                 int enumIndex = indexOfMetaEnum(meta, returnTypeName);
1093                 if (enumIndex != -1)
1094                     types.append(QtMethodMatchType::metaEnum(enumIndex, returnTypeName));
1095                 else {
1096                     unresolvedTypes = true;
1097                     types.append(QtMethodMatchType::unresolved(returnTypeName));
1098                 }
1099             }
1100         } else {
1101             if (rtype == QMetaType::QVariant)
1102                 types.append(QtMethodMatchType::variant());
1103             else
1104                 types.append(QtMethodMatchType::metaType(rtype, returnTypeName));
1105         }
1106
1107         // resolve argument types
1108         QList<QByteArray> parameterTypeNames = method.parameterTypes();
1109         for (int i = 0; i < parameterTypeNames.count(); ++i) {
1110             QByteArray argTypeName = parameterTypeNames.at(i);
1111             int atype = method.parameterType(i);
1112             if (atype == QMetaType::UnknownType) {
1113                 int enumIndex = indexOfMetaEnum(meta, argTypeName);
1114                 if (enumIndex != -1)
1115                     types.append(QtMethodMatchType::metaEnum(enumIndex, argTypeName));
1116                 else {
1117                     unresolvedTypes = true;
1118                     types.append(QtMethodMatchType::unresolved(argTypeName));
1119                 }
1120             } else {
1121                 if (atype == QMetaType::QVariant)
1122                     types.append(QtMethodMatchType::variant());
1123                 else
1124                     types.append(QtMethodMatchType::metaType(atype, argTypeName));
1125             }
1126         }
1127
1128         // If the native method requires more arguments than what was passed from JavaScript
1129         if (argumentCount + 1 < static_cast<unsigned>(types.count())) {
1130             qMatchDebug() << "Match:too few args for" << method.methodSignature();
1131             tooFewArgs.append(index);
1132             continue;
1133         }
1134
1135         if (unresolvedTypes) {
1136             qMatchDebug() << "Match:unresolved arg types for" << method.methodSignature();
1137             // remember it so we can give an error message later, if necessary
1138             unresolved.append(QtMethodMatchData(/*matchDistance=*/INT_MAX, index,
1139                                                    types, QVarLengthArray<QVariant, 10>()));
1140             continue;
1141         }
1142
1143         // Now convert arguments
1144         if (args.count() != types.count())
1145             args.resize(types.count());
1146
1147         QtMethodMatchType retType = types[0];
1148         if (retType.typeId() != QMetaType::Void)
1149             args[0] = QVariant(retType.typeId(), (void *)0); // the return value
1150
1151         bool converted = true;
1152         int matchDistance = 0;
1153         for (unsigned i = 0; converted && i + 1 < static_cast<unsigned>(types.count()); ++i) {
1154             JSValueRef arg = i < argumentCount ? arguments[i] : JSValueMakeUndefined(context);
1155
1156             int argdistance = -1;
1157             QVariant v = convertValueToQVariant(toJS(context), toJS(toJS(context), arg), types.at(i+1).typeId(), &argdistance);
1158             if (argdistance >= 0) {
1159                 matchDistance += argdistance;
1160                 args[i+1] = v;
1161             } else {
1162                 qMatchDebug() << "failed to convert argument " << i << "type" << types.at(i+1).typeId() << QMetaType::typeName(types.at(i+1).typeId());
1163                 converted = false;
1164             }
1165         }
1166
1167         qMatchDebug() << "Match: " << method.methodSignature() << (converted ? "converted":"failed to convert") << "distance " << matchDistance;
1168
1169         if (converted) {
1170             if ((argumentCount + 1 == static_cast<unsigned>(types.count()))
1171                 && (matchDistance == 0)) {
1172                 // perfect match, use this one
1173                 chosenIndex = index;
1174                 break;
1175             }
1176             QtMethodMatchData currentMatch(matchDistance, index, types, args);
1177             if (candidates.isEmpty())
1178                 candidates.append(currentMatch);
1179             else {
1180                 QtMethodMatchData bestMatchSoFar = candidates.at(0);
1181                 if ((args.count() > bestMatchSoFar.args.count())
1182                     || ((args.count() == bestMatchSoFar.args.count())
1183                     && (matchDistance <= bestMatchSoFar.matchDistance)))
1184                     candidates.prepend(currentMatch);
1185                 else
1186                     candidates.append(currentMatch);
1187             }
1188         } else {
1189             conversionFailed.append(index);
1190         }
1191
1192         if (!overloads)
1193             break;
1194     }
1195
1196     if (chosenIndex == -1 && candidates.count() == 0) {
1197         // No valid functions at all - format an error message
1198         if (!conversionFailed.isEmpty()) {
1199             QString message = QString::fromLatin1("incompatible type of argument(s) in call to %0(); candidates were\n")
1200                               .arg(QString::fromLatin1(signature));
1201             for (int i = 0; i < conversionFailed.size(); ++i) {
1202                 if (i > 0)
1203                     message += QLatin1String("\n");
1204                 QMetaMethod mtd = meta->method(conversionFailed.at(i));
1205                 message += QString::fromLatin1("    %0").arg(QString::fromLatin1(mtd.methodSignature()));
1206             }
1207             setException(context, exception, message);
1208         } else if (!unresolved.isEmpty()) {
1209             QtMethodMatchData argsInstance = unresolved.first();
1210             int unresolvedIndex = argsInstance.firstUnresolvedIndex();
1211             Q_ASSERT(unresolvedIndex != -1);
1212             QtMethodMatchType unresolvedType = argsInstance.types.at(unresolvedIndex);
1213             QString message = QString::fromLatin1("cannot call %0(): unknown type `%1'")
1214                 .arg(QString::fromLatin1(signature))
1215                 .arg(QLatin1String(unresolvedType.name()));
1216             setException(context, exception, message);
1217         } else {
1218             QString message = QString::fromLatin1("too few arguments in call to %0(); candidates are\n")
1219                               .arg(QString::fromLatin1(signature));
1220             for (int i = 0; i < tooFewArgs.size(); ++i) {
1221                 if (i > 0)
1222                     message += QLatin1String("\n");
1223                 QMetaMethod mtd = meta->method(tooFewArgs.at(i));
1224                 message += QString::fromLatin1("    %0").arg(QString::fromLatin1(mtd.methodSignature()));
1225             }
1226             setException(context, exception, message);
1227         }
1228     }
1229
1230     if (chosenIndex == -1 && candidates.count() > 0) {
1231         QtMethodMatchData bestMatch = candidates.at(0);
1232         if ((candidates.size() > 1)
1233             && (bestMatch.args.count() == candidates.at(1).args.count())
1234             && (bestMatch.matchDistance == candidates.at(1).matchDistance)) {
1235             // ambiguous call
1236             QString message = QString::fromLatin1("ambiguous call of overloaded function %0(); candidates were\n")
1237                                 .arg(QLatin1String(signature));
1238             for (int i = 0; i < candidates.size(); ++i) {
1239                 // Only candidate for overload if argument count and match distance is same as best match
1240                 if (candidates.at(i).args.count() == bestMatch.args.count()
1241                     || candidates.at(i).matchDistance == bestMatch.matchDistance) {
1242                     if (i > 0)
1243                         message += QLatin1String("\n");
1244                     QMetaMethod mtd = meta->method(candidates.at(i).index);
1245                     message += QString::fromLatin1("    %0").arg(QString::fromLatin1(mtd.methodSignature()));
1246                 }
1247             }
1248             setException(context, exception, message);
1249         } else {
1250             chosenIndex = bestMatch.index;
1251             args = bestMatch.args;
1252         }
1253     }
1254
1255     if (chosenIndex != -1) {
1256         /* Copy the stuff over */
1257         int i;
1258         vars.resize(args.count());
1259         for (i=0; i < args.count(); i++) {
1260             vars[i] = args[i];
1261             vvars[i] = vars[i].data();
1262         }
1263     }
1264
1265     return chosenIndex;
1266 }
1267
1268 // Signals are not fuzzy matched as much as methods
1269 static int findSignalIndex(const QMetaObject* meta, int initialIndex, QByteArray signature)
1270 {
1271     int index = initialIndex;
1272     QMetaMethod method = meta->method(index);
1273     bool overloads = !signature.contains('(');
1274     if (overloads && (method.attributes() & QMetaMethod::Cloned)) {
1275         // find the most general method
1276         do {
1277             method = meta->method(--index);
1278         } while (method.attributes() & QMetaMethod::Cloned);
1279     }
1280     return index;
1281 }
1282
1283 static JSClassRef prototypeForSignalsAndSlots()
1284 {
1285     static JSClassDefinition classDef = {
1286         0, 0, 0, 0, 0, 0,
1287         0, 0, 0, 0, 0, 0, 0, QtRuntimeMethod::call, 0, 0, 0
1288     };
1289     static JSClassRef cls = JSClassCreate(&classDef);
1290     return cls;
1291 }
1292
1293 QtRuntimeMethod::QtRuntimeMethod(JSContextRef ctx, JSValueRef* exception, QObject* object, const QByteArray& identifier, int index, int flags, QtInstance* instance)
1294     : m_object(object)
1295     , m_identifier(identifier)
1296     , m_index(index)
1297     , m_flags(flags)
1298     , m_instance(instance)
1299 {
1300 }
1301
1302 QtRuntimeMethod::~QtRuntimeMethod()
1303 {
1304     if (m_jsObject)
1305         JSObjectSetPrivate(toRef(m_jsObject.get()), 0);
1306 }
1307
1308 JSValueRef QtRuntimeMethod::call(JSContextRef context, JSObjectRef function, JSObjectRef /*thisObject*/, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
1309 {
1310     QtRuntimeMethod* d = reinterpret_cast<QtRuntimeMethod*>(JSObjectGetPrivate(function));
1311     if (!d) {
1312         setException(context, exception, QStringLiteral("cannot call function of deleted runtime method"));
1313         return JSValueMakeUndefined(context);
1314     }
1315     QObject* obj = d->m_object;
1316
1317     if (!obj) {
1318         setException(context, exception, QStringLiteral("cannot call function of deleted QObject"));
1319         return JSValueMakeUndefined(context);
1320     }
1321
1322     // Allow for maximum of 10 arguments and size stack arrays accordingly.
1323     if (argumentCount > 10)
1324         return JSValueMakeUndefined(context);
1325
1326     QVarLengthArray<QVariant, 10> vargs;
1327     void* qargs[11];
1328
1329     int methodIndex = findMethodIndex(context, obj->metaObject(), d->m_identifier,  argumentCount, arguments,
1330                                       (d->m_flags & AllowPrivate), vargs, (void **)qargs, exception);
1331
1332     if (QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, methodIndex, qargs) >= 0)
1333         return JSValueMakeUndefined(context);
1334
1335     if (vargs.size() > 0 && vargs[0].isValid())
1336         return toRef(toJS(context), convertQVariantToValue(toJS(context), d->m_instance->rootObject(), vargs[0]));
1337
1338     return JSValueMakeUndefined(context);
1339 }
1340
1341 JSValueRef QtRuntimeMethod::connect(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
1342 {
1343     return connectOrDisconnect(context, function, thisObject, argumentCount, arguments, exception, true);
1344 }
1345
1346 JSValueRef QtRuntimeMethod::disconnect(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
1347 {
1348     return connectOrDisconnect(context, function, thisObject, argumentCount, arguments, exception, false);
1349 }
1350
1351 JSObjectRef QtRuntimeMethod::jsObjectRef(JSContextRef context, JSValueRef* exception)
1352 {
1353     if (m_jsObject)
1354         return toRef(m_jsObject.get());
1355
1356     static const JSClassDefinition classDefForConnect = {
1357         0, 0, "connect", 0, 0, 0,
1358         0, 0, 0, 0, 0, 0, 0, connect, 0, 0, 0
1359     };
1360
1361     static const JSClassDefinition classDefForDisconnect = {
1362         0, 0, "disconnect", 0, 0, 0,
1363         0, 0, 0, 0, 0, 0, 0, disconnect, 0, 0, 0
1364     };
1365
1366     static JSClassRef classRefConnect = JSClassCreate(&classDefForConnect);
1367     static JSClassRef classRefDisconnect = JSClassCreate(&classDefForDisconnect);
1368     bool isSignal = m_flags & MethodIsSignal;
1369     JSObjectRef object = JSObjectMake(context, prototypeForSignalsAndSlots(), this);
1370     JSObjectRef connectFunction = JSObjectMake(context, classRefConnect, this);
1371     JSObjectRef disconnectFunction = JSObjectMake(context, classRefDisconnect, this);
1372     JSPropertyAttributes attributes = kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete;
1373
1374     static JSStringRef connectStr = JSStringCreateWithUTF8CString("connect");
1375     static JSStringRef disconnectStr = JSStringCreateWithUTF8CString("disconnect");
1376     static JSStringRef lengthStr = JSStringCreateWithUTF8CString("length");
1377     static JSStringRef nameStr = JSStringCreateWithUTF8CString("name");
1378     JSRetainPtr<JSStringRef> actualNameStr(Adopt, JSStringCreateWithUTF8CString(m_identifier.constData()));
1379
1380     JSObjectSetProperty(context, connectFunction, lengthStr, JSValueMakeNumber(context, isSignal ? 1 : 0), attributes, exception);
1381     JSObjectSetProperty(context, connectFunction, nameStr, JSValueMakeString(context, connectStr), attributes, exception);
1382     JSObjectSetProperty(context, disconnectFunction, lengthStr, JSValueMakeNumber(context, isSignal ? 1 : 0), attributes, exception);
1383     JSObjectSetProperty(context, disconnectFunction, nameStr, JSValueMakeString(context, disconnectStr), attributes, exception);
1384
1385     JSObjectSetProperty(context, object, connectStr, connectFunction, attributes, exception);
1386     JSObjectSetProperty(context, object, disconnectStr, disconnectFunction, attributes, exception);
1387     JSObjectSetProperty(context, object, lengthStr, JSValueMakeNumber(context, 0), attributes, exception);
1388     JSObjectSetProperty(context, object, nameStr, JSValueMakeString(context, actualNameStr.get()), attributes, exception);
1389
1390     m_jsObject = PassWeak<JSObject>(toJS(object));
1391     return object;
1392 }
1393
1394 JSValueRef QtRuntimeMethod::connectOrDisconnect(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception, bool connect)
1395 {
1396     QtRuntimeMethod* d = static_cast<QtRuntimeMethod*>(JSObjectGetPrivate(thisObject));
1397     if (!d)
1398         d = static_cast<QtRuntimeMethod*>(JSObjectGetPrivate(function));
1399
1400     QString functionName = connect ? QStringLiteral("connect") : QStringLiteral("disconnect");
1401
1402     if (!argumentCount) {
1403         QString errorStr = QStringLiteral("QtMetaMethod.%1: no arguments given").arg(connect ?  QStringLiteral("connect") : QStringLiteral("disconnect"));
1404         setException(context, exception, errorStr);
1405         return JSValueMakeUndefined(context);
1406     }
1407
1408     if ((!(d->m_flags & QtRuntimeMethod::MethodIsSignal))) {
1409         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));
1410         return JSValueMakeUndefined(context);
1411     }
1412
1413     QObject* sender = d->m_object.data();
1414
1415     if (!sender) {
1416         setException(context, exception, QStringLiteral("cannot call function of deleted QObject"));
1417         return JSValueMakeUndefined(context);
1418     }
1419
1420     int signalIndex = findSignalIndex(sender->metaObject(), d->m_index, d->m_identifier);
1421
1422     JSObjectRef targetObject = 0;
1423     JSObjectRef targetFunction = 0;
1424
1425     if (argumentCount == 1) {
1426         if (!JSValueIsObject(context, arguments[0])) {
1427             setException(context, exception, QStringLiteral("QtMetaMethod.%1: target is not a function").arg(functionName));
1428             return JSValueMakeUndefined(context);
1429         }
1430         targetFunction = JSValueToObject(context, arguments[0], exception);
1431
1432         // object.signal.connect(someFunction);
1433         if (JSObjectIsFunction(context, targetFunction)) {
1434             if (JSValueIsObjectOfClass(context, targetFunction, prototypeForSignalsAndSlots())) {
1435                 // object.signal.connect(otherObject.slot);
1436                 if (QtRuntimeMethod* targetMethod = static_cast<QtRuntimeMethod*>(JSObjectGetPrivate(targetFunction)))
1437                     targetObject = toRef(QtInstance::getQtInstance(targetMethod->m_object.data(), d->m_instance->rootObject(), QtInstance::QtOwnership)->createRuntimeObject(toJS(context)));
1438             }
1439         } else
1440             targetFunction = 0;
1441     } else {
1442         // object.signal.connect(object, someFunction);
1443         targetObject = JSValueToObject(context, arguments[0], exception);
1444         if (JSValueIsObject(context, arguments[1])) {
1445             JSObjectRef obj = JSValueToObject(context, arguments[1], exception);
1446             if (JSObjectIsFunction(context, obj))
1447                 targetFunction = obj;
1448         }
1449         if (!targetFunction) {
1450             // Maybe the second argument is a string
1451             JSValueRef conversionException = 0;
1452             JSRetainPtr<JSStringRef> functionName(Adopt, JSValueToStringCopy(context, arguments[1], &conversionException));
1453             if (functionName && !conversionException) {
1454                 JSValueRef functionProperty = JSObjectGetProperty(context, targetObject, functionName.get(), &conversionException);
1455                 if (!conversionException && functionProperty && JSValueIsObject(context, functionProperty)) {
1456                     targetFunction = JSValueToObject(context, functionProperty, 0);
1457                     if (!JSObjectIsFunction(context, targetFunction))
1458                         targetFunction = 0;
1459                 }
1460             }
1461         }
1462     }
1463
1464     // object.signal.connect(someObject);
1465     if (!targetFunction) {
1466         QString message = QStringLiteral("QtMetaMethod.%1: target is not a function");
1467         if (connect)
1468             message = message.arg(QStringLiteral("connect"));
1469         else
1470             message = message.arg(QStringLiteral("disconnect"));
1471         setException(context, exception, message);
1472         return JSValueMakeUndefined(context);
1473     }
1474
1475     if (connect) {
1476         // to connect, we need:
1477         //  target object [from ctor]
1478         //  target signal index etc. [from ctor]
1479         //  receiver function [from arguments]
1480         //  receiver this object [from arguments]
1481
1482         QtConnectionObject* conn = new QtConnectionObject(context, QtInstance::getQtInstance(sender, d->m_instance->rootObject(), QtInstance::QtOwnership), signalIndex, targetObject, targetFunction);
1483         bool ok = QMetaObject::connect(sender, signalIndex, conn, conn->metaObject()->methodOffset());
1484         if (!ok) {
1485             delete conn;
1486             QString msg = QString(QLatin1String("QtMetaMethod.connect: failed to connect to %1::%2()"))
1487                     .arg(QLatin1String(sender->metaObject()->className()))
1488                     .arg(QLatin1String(d->m_identifier));
1489             setException(context, exception, msg);
1490             return JSValueMakeUndefined(context);
1491         }
1492
1493         // Store connection
1494         QtConnectionObject::connections.insert(sender, conn);
1495
1496         return JSValueMakeUndefined(context);
1497     }
1498
1499     // Now to find our previous connection object.
1500     QList<QtConnectionObject*> conns = QtConnectionObject::connections.values(sender);
1501
1502     foreach (QtConnectionObject* conn, conns) {
1503         // Is this the right connection?
1504         if (!conn->match(context, sender, signalIndex, targetObject, targetFunction))
1505             continue;
1506
1507         // Yep, disconnect it
1508         QMetaObject::disconnect(sender, signalIndex, conn, conn->metaObject()->methodOffset());
1509         delete conn; // this will also remove it from the map
1510         return JSValueMakeUndefined(context);
1511     }
1512
1513     QString msg = QStringLiteral("QtMetaMethod.disconnect: failed to disconnect from %1::%2()")
1514             .arg(QLatin1String(sender->metaObject()->className()))
1515             .arg(QLatin1String(d->m_identifier));
1516
1517     setException(context, exception, msg);
1518     return JSValueMakeUndefined(context);
1519 }
1520
1521 // ===============
1522
1523 QMultiMap<QObject*, QtConnectionObject*> QtConnectionObject::connections;
1524
1525 QtConnectionObject::QtConnectionObject(JSContextRef context, PassRefPtr<QtInstance> senderInstance, int signalIndex, JSObjectRef receiver, JSObjectRef receiverFunction)
1526     : QObject(senderInstance->getObject())
1527     , m_context(JSContextGetGlobalContext(context))
1528     , m_senderInstance(senderInstance)
1529     , m_originalSender(m_senderInstance->getObject())
1530     , m_signalIndex(signalIndex)
1531     , m_receiver(receiver)
1532     , m_receiverFunction(receiverFunction)
1533 {
1534     if (m_receiver)
1535         JSValueProtect(m_context, m_receiver);
1536     JSValueProtect(m_context, m_receiverFunction);
1537 }
1538
1539 QtConnectionObject::~QtConnectionObject()
1540 {
1541     // We can safely use m_originalSender because connection object will never outlive the sender,
1542     // which is its QObject parent.
1543     connections.remove(m_originalSender, this);
1544
1545     if (m_receiver)
1546         JSValueUnprotect(m_context, m_receiver);
1547     JSValueUnprotect(m_context, m_receiverFunction);
1548 }
1549
1550 // Begin moc-generated code -- modify with care! Check "HAND EDIT" parts
1551 struct qt_meta_stringdata_QtConnectionObject_t {
1552     QByteArrayData data[3];
1553     char stringdata[44];
1554 };
1555 #define QT_MOC_LITERAL(idx, ofs, len) { \
1556     Q_REFCOUNT_INITIALIZE_STATIC, len, 0, 0, \
1557     offsetof(qt_meta_stringdata_QtConnectionObject_t, stringdata) + ofs \
1558         - idx * sizeof(QByteArrayData) \
1559     }
1560 static const qt_meta_stringdata_QtConnectionObject_t qt_meta_stringdata_QtConnectionObject = {
1561     {
1562 QT_MOC_LITERAL(0, 0, 33),
1563 QT_MOC_LITERAL(1, 34, 7),
1564 QT_MOC_LITERAL(2, 42, 0)
1565     },
1566     "JSC::Bindings::QtConnectionObject\0"
1567     "execute\0\0"
1568 };
1569 #undef QT_MOC_LITERAL
1570
1571 static const uint qt_meta_data_QtConnectionObject[] = {
1572
1573  // content:
1574        7,       // revision
1575        0,       // classname
1576        0,    0, // classinfo
1577        1,   14, // methods
1578        0,    0, // properties
1579        0,    0, // enums/sets
1580        0,    0, // constructors
1581        0,       // flags
1582        0,       // signalCount
1583
1584  // slots: name, argc, parameters, tag, flags
1585        1,    0,   19,    2, 0x0a,
1586
1587  // slots: parameters
1588     QMetaType::Void,
1589
1590        0        // eod
1591 };
1592
1593 void QtConnectionObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
1594 {
1595     if (_c == QMetaObject::InvokeMetaMethod) {
1596         Q_ASSERT(staticMetaObject.cast(_o));
1597         QtConnectionObject *_t = static_cast<QtConnectionObject *>(_o);
1598         switch (_id) {
1599         case 0: _t->execute(_a); break; // HAND EDIT: add _a parameter
1600         default: ;
1601         }
1602     }
1603 }
1604
1605 const QMetaObject QtConnectionObject::staticMetaObject = {
1606     { &QObject::staticMetaObject, qt_meta_stringdata_QtConnectionObject.data,
1607       qt_meta_data_QtConnectionObject, qt_static_metacall, 0, 0 }
1608 };
1609
1610 const QMetaObject *QtConnectionObject::metaObject() const
1611 {
1612     return &staticMetaObject;
1613 }
1614
1615 void *QtConnectionObject::qt_metacast(const char *_clname)
1616 {
1617     if (!_clname) return 0;
1618     if (!strcmp(_clname, qt_meta_stringdata_QtConnectionObject.stringdata))
1619         return static_cast<void*>(const_cast<QtConnectionObject*>(this));
1620     return QObject::qt_metacast(_clname);
1621 }
1622
1623 int QtConnectionObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
1624 {
1625     _id = QObject::qt_metacall(_c, _id, _a);
1626     if (_id < 0)
1627         return _id;
1628     if (_c == QMetaObject::InvokeMetaMethod) {
1629         if (_id < 1)
1630             qt_static_metacall(this, _c, _id, _a);
1631         _id -= 1;
1632     }
1633     return _id;
1634 }
1635 // End of moc-generated code
1636
1637 void QtConnectionObject::execute(void** argv)
1638 {
1639     QObject* sender = m_senderInstance->getObject();
1640     if (!sender) {
1641         qWarning() << "sender deleted, cannot deliver signal";
1642         return;
1643     }
1644
1645     ASSERT(sender == m_originalSender);
1646
1647     const QMetaObject* meta = sender->metaObject();
1648     const QMetaMethod method = meta->method(m_signalIndex);
1649
1650     JSValueRef* ignoredException = 0;
1651     JSRetainPtr<JSStringRef> lengthProperty(JSStringCreateWithUTF8CString("length"));
1652     int receiverLength = int(JSValueToNumber(m_context, JSObjectGetProperty(m_context, m_receiverFunction, lengthProperty.get(), ignoredException), ignoredException));
1653     int argc = qMax(method.parameterCount(), receiverLength);
1654     WTF::Vector<JSValueRef> args(argc);
1655
1656     // TODO: remove once conversion functions use JSC API.
1657     ExecState* exec = ::toJS(m_context);
1658     RefPtr<RootObject> rootObject = m_senderInstance->rootObject();
1659
1660     for (int i = 0; i < argc; i++) {
1661         int argType = method.parameterType(i);
1662         args[i] = ::toRef(exec, convertQVariantToValue(exec, rootObject, QVariant(argType, argv[i+1])));
1663     }
1664
1665     JSObjectCallAsFunction(m_context, m_receiverFunction, m_receiver, argc, args.data(), 0);
1666 }
1667
1668 bool QtConnectionObject::match(JSContextRef context, QObject* sender, int signalIndex, JSObjectRef receiver, JSObjectRef receiverFunction)
1669 {
1670     if (sender != m_originalSender || signalIndex != m_signalIndex)
1671         return false;
1672     JSValueRef* ignoredException = 0;
1673     const bool receiverMatch = (!receiver && !m_receiver) || (receiver && m_receiver && JSValueIsEqual(context, receiver, m_receiver, ignoredException));
1674     return receiverMatch && JSValueIsEqual(context, receiverFunction, m_receiverFunction, ignoredException);
1675 }
1676
1677 // ===============
1678
1679 template <typename T> QtArray<T>::QtArray(QList<T> list, QMetaType::Type type, PassRefPtr<RootObject> rootObject)
1680     : Array(rootObject)
1681     , m_list(list)
1682     , m_type(type)
1683 {
1684     m_length = m_list.count();
1685 }
1686
1687 template <typename T> QtArray<T>::~QtArray ()
1688 {
1689 }
1690
1691 template <typename T> RootObject* QtArray<T>::rootObject() const
1692 {
1693     return m_rootObject && m_rootObject->isValid() ? m_rootObject.get() : 0;
1694 }
1695
1696 template <typename T> void QtArray<T>::setValueAt(ExecState* exec, unsigned index, JSValue aValue) const
1697 {
1698     // QtScript sets the value, but doesn't forward it to the original source
1699     // (e.g. if you do 'object.intList[5] = 6', the object is not updated, but the
1700     // copy of the list is).
1701     int dist = -1;
1702     QVariant val = convertValueToQVariant(exec, aValue, m_type, &dist);
1703
1704     if (dist >= 0) {
1705         m_list[index] = val.value<T>();
1706     }
1707 }
1708
1709
1710 template <typename T> JSValue QtArray<T>::valueAt(ExecState *exec, unsigned int index) const
1711 {
1712     if (index < m_length) {
1713         T val = m_list.at(index);
1714         return convertQVariantToValue(exec, rootObject(), QVariant::fromValue(val));
1715     }
1716
1717     return jsUndefined();
1718 }
1719
1720 // ===============
1721
1722 } }