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