cdb092bdb3ea4041ca00073ef2d5b75beddb321a
[WebKit-https.git] / WebCore / bridge / qt / qt_runtime.cpp
1 /*
2  * Copyright (C) 2006 Trolltech ASA
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 #include "qt_instance.h"
23 #include "object.h"
24 #include "array_instance.h"
25 #include "date_object.h"
26 #include "DateMath.h"
27 #include "regexp_object.h"
28 #include <runtime.h>
29 #include <runtime_object.h>
30 #include <runtime_array.h>
31 #include <function.h>
32 #include "PropertyNameArray.h"
33 #include "qmetatype.h"
34 #include "qmetaobject.h"
35 #include "qobject.h"
36 #include "qstringlist.h"
37 #include "qdebug.h"
38 #include "qvarlengtharray.h"
39 #include "qdatetime.h"
40 #include <limits.h>
41
42 // QtScript has these
43 Q_DECLARE_METATYPE(QObjectList);
44 Q_DECLARE_METATYPE(QList<int>);
45 Q_DECLARE_METATYPE(QVariant);
46
47
48 namespace KJS {
49 namespace Bindings {
50
51 // Debugging
52 //#define QTWK_RUNTIME_CONVERSION_DEBUG
53 //#define QTWK_RUNTIME_MATCH_DEBUG
54
55 class QWKNoDebug
56 {
57 public:
58     inline QWKNoDebug(){}
59     inline ~QWKNoDebug(){}
60
61     template<typename T>
62     inline QWKNoDebug &operator<<(const T &) { return *this; }
63 };
64
65 #ifdef QTWK_RUNTIME_CONVERSION_DEBUG
66 #define qConvDebug() qDebug()
67 #else
68 #define qConvDebug() QWKNoDebug()
69 #endif
70
71 #ifdef QTWK_RUNTIME_MATCH_DEBUG
72 #define qMatchDebug() qDebug()
73 #else
74 #define qMatchDebug() QWKNoDebug()
75 #endif
76
77 typedef enum {
78     Variant = 0,
79     Number,
80     Boolean,
81     String,
82     Date,
83     RegExp,
84     Array,
85     QObj,
86     Object,
87     Null,
88     RTArray
89 } JSRealType;
90
91 #if defined(QTWK_RUNTIME_CONVERSION_DEBUG) || defined(QTWK_RUNTIME_MATCH_DEBUG)
92 QDebug operator<<(QDebug dbg, const JSRealType &c)
93 {
94      const char *map[] = { "Variant", "Number", "Boolean", "String", "Date",
95          "RegExp", "Array", "RTObject", "Object", "Null", "RTArray"};
96
97      dbg.nospace() << "JSType(" << ((int)c) << ", " <<  map[c] << ")";
98
99      return dbg.space();
100 }
101 #endif
102
103 static JSRealType valueRealType(ExecState* exec, JSValue* val)
104 {
105     if (val->isNumber())
106         return Number;
107     else if (val->isString())
108         return String;
109     else if (val->isBoolean())
110         return Boolean;
111     else if (val->isNull())
112         return Null;
113     else if (val->isObject()) {
114         JSObject *object = val->toObject(exec);
115         if (object->inherits(&RuntimeArray::s_info))  // RuntimeArray 'inherits' from Array, but not in C++
116             return RTArray;
117         else if (object->inherits(&ArrayInstance::info))
118             return Array;
119         else if (object->inherits(&DateInstance::info))
120             return Date;
121         else if (object->inherits(&RegExpImp::info))
122             return RegExp;
123         else if (object->inherits(&RuntimeObjectImp::s_info))
124             return QObj;
125         return Object;
126     }
127
128     return String; // I don't know.
129 }
130
131 QVariant convertValueToQVariant(ExecState* exec, JSValue* value, QMetaType::Type hint, int *distance)
132 {
133     // check magic pointer values before dereferencing value
134     if (value == jsNaN() || value == jsUndefined()) {
135         if (distance)
136             *distance = -1;
137         return QVariant();
138     }
139
140     JSLock lock;
141     JSRealType type = valueRealType(exec, value);
142     if (hint == QMetaType::Void) {
143         switch(type) {
144             case Number:
145                 hint = QMetaType::Double;
146                 break;
147             case Boolean:
148                 hint = QMetaType::Bool;
149                 break;
150             case String:
151             default:
152                 hint = QMetaType::QString;
153                 break;
154             case Date:
155                 hint = QMetaType::QDateTime;
156                 break;
157             case RegExp:
158                 hint = QMetaType::QRegExp;
159                 break;
160             case Object:
161                 hint = QMetaType::QVariantMap;
162                 break;
163             case QObj:
164                 hint = QMetaType::QObjectStar;
165                 break;
166             case Array:
167             case RTArray:
168                 hint = QMetaType::QVariantList;
169                 break;
170         }
171     }
172
173     qConvDebug() << "convertValueToQVariant: jstype is " << type << ", hint is" << hint;
174
175     if (value == jsNull()
176         && hint != QMetaType::QObjectStar
177         && hint != QMetaType::VoidStar) {
178         if (distance)
179             *distance = -1;
180         return QVariant();
181     }
182
183     QVariant ret;
184     int dist = -1;
185     switch (hint) {
186         case QMetaType::Bool:
187             ret = QVariant(value->toBoolean(exec));
188             if (type == Boolean)
189                 dist = 0;
190             else
191                 dist = 10;
192             break;
193
194         case QMetaType::Int:
195         case QMetaType::UInt:
196         case QMetaType::Long:
197         case QMetaType::ULong:
198         case QMetaType::LongLong:
199         case QMetaType::ULongLong:
200         case QMetaType::Short:
201         case QMetaType::UShort:
202         case QMetaType::Float:
203         case QMetaType::Double:
204             ret = QVariant(value->toNumber(exec));
205             ret.convert((QVariant::Type)hint);
206             if (type == Number) {
207                 switch (hint) {
208                 case QMetaType::Double:
209                     dist = 0;
210                     break;
211                 case QMetaType::Float:
212                     dist = 1;
213                     break;
214                 case QMetaType::LongLong:
215                 case QMetaType::ULongLong:
216                     dist = 2;
217                     break;
218                 case QMetaType::Long:
219                 case QMetaType::ULong:
220                     dist = 3;
221                     break;
222                 case QMetaType::Int:
223                 case QMetaType::UInt:
224                     dist = 4;
225                     break;
226                 case QMetaType::Short:
227                 case QMetaType::UShort:
228                     dist = 5;
229                     break;
230                     break;
231                 default:
232                     dist = 10;
233                     break;
234                 }
235             } else {
236                 dist = 10;
237             }
238             break;
239
240         case QMetaType::QChar:
241             if (type == Number || type == Boolean) {
242                 ret = QVariant(QChar((ushort)value->toNumber(exec)));
243                 if (type == Boolean)
244                     dist = 3;
245                 else
246                     dist = 6;
247             } else {
248                 UString str = value->toString(exec);
249                 ret = QVariant(QChar(str.size() ? *(const ushort*)str.rep()->data() : 0));
250                 if (type == String)
251                     dist = 3;
252                 else
253                     dist = 10;
254             }
255             break;
256
257         case QMetaType::QString: {
258             UString ustring = value->toString(exec);
259             ret = QVariant(QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()));
260             if (type == String)
261                 dist = 0;
262             else
263                 dist = 10;
264             break;
265         }
266
267         case QMetaType::QVariantMap: 
268             if (type == Object || type == Array || type == RTArray) {
269                 // Enumerate the contents of the object
270                 JSObject* object = value->toObject(exec);
271
272                 PropertyNameArray properties;
273                 object->getPropertyNames(exec, properties);
274                 PropertyNameArray::const_iterator it = properties.begin();
275
276                 QVariantMap result;
277                 int objdist = 0;
278                 while(it != properties.end()) {
279                     if (object->propertyIsEnumerable(exec, *it)) {
280                         JSValue* val = object->get(exec, *it);
281                         QVariant v = convertValueToQVariant(exec, val, QMetaType::Void, &objdist);
282                         if (objdist >= 0) {
283                             UString ustring = (*it).ustring();
284                             QString id = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
285                             result.insert(id, v);
286                         }
287                     }
288                     ++it;
289                 }
290                 dist = 1;
291                 ret = QVariant(result);
292             }
293             break;
294
295         case QMetaType::QVariantList:
296             if (type == RTArray) {
297                 JSObject* object = value->toObject(exec);
298                 RuntimeArray* rtarray = static_cast<RuntimeArray*>(object);
299
300                 QVariantList result;
301                 int len = rtarray->getLength();
302                 int objdist = 0;
303                 qConvDebug() << "converting a " << len << " length Array";
304                 for (int i = 0; i < len; ++i) {
305                     JSValue *val = rtarray->getConcreteArray()->valueAt(exec, i);
306                     result.append(convertValueToQVariant(exec, val, QMetaType::Void, &objdist));
307                     if (objdist == -1) {
308                         qConvDebug() << "Failed converting element at index " << i;
309                         break; // Failed converting a list entry, so fail the array
310                     }
311                 }
312                 if (objdist != -1) {
313                     dist = 5;
314                     ret = QVariant(result);
315                 }
316             } else if (type == Array) {
317                 JSObject* object = value->toObject(exec);
318                 ArrayInstance* array = static_cast<ArrayInstance*>(object);
319
320                 QVariantList result;
321                 int len = array->getLength();
322                 int objdist = 0;
323                 qConvDebug() << "converting a " << len << " length Array";
324                 for (int i = 0; i < len; ++i) {
325                     JSValue *val = array->getItem(i);
326                     result.append(convertValueToQVariant(exec, val, QMetaType::Void, &objdist));
327                     if (objdist == -1) {
328                         qConvDebug() << "Failed converting element at index " << i;
329                         break; // Failed converting a list entry, so fail the array
330                     }
331                 }
332                 if (objdist != -1) {
333                     dist = 5;
334                     ret = QVariant(result);
335                 }
336             } else {
337                 // Make a single length array
338                 int objdist;
339                 qConvDebug() << "making a single length variantlist";
340                 QVariant var = convertValueToQVariant(exec, value, QMetaType::Void, &objdist);
341                 if (objdist != -1) {
342                     QVariantList result;
343                     result << var;
344                     ret = QVariant(result);
345                     dist = 10;
346                 } else {
347                     qConvDebug() << "failed making single length varlist";
348                 }
349             }
350             break;
351
352         case QMetaType::QStringList: {
353             if (type == RTArray) {
354                 JSObject* object = value->toObject(exec);
355                 RuntimeArray* rtarray = static_cast<RuntimeArray*>(object);
356
357                 QStringList result;
358                 int len = rtarray->getLength();
359                 for (int i = 0; i < len; ++i) {
360                     JSValue *val = rtarray->getConcreteArray()->valueAt(exec, i);
361                     UString ustring = val->toString(exec);
362                     QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
363
364                     result.append(qstring);
365                 }
366                 dist = 5;
367                 ret = QVariant(result);
368             } else if (type == Array) {
369                 JSObject* object = value->toObject(exec);
370                 ArrayInstance* array = static_cast<ArrayInstance*>(object);
371
372                 QStringList result;
373                 int len = array->getLength();
374                 for (int i = 0; i < len; ++i) {
375                     JSValue* val = array->getItem(i);
376                     UString ustring = val->toString(exec);
377                     QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
378
379                     result.append(qstring);
380                 }
381                 dist = 5;
382                 ret = QVariant(result);
383             } else {
384                 // Make a single length array
385                 UString ustring = value->toString(exec);
386                 QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
387                 QStringList result;
388                 result.append(qstring);
389                 ret = QVariant(result);
390                 dist = 10;
391             }
392             break;
393         }
394
395         case QMetaType::QByteArray: {
396             UString ustring = value->toString(exec);
397             ret = QVariant(QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size()).toLatin1());
398             if (type == String)
399                 dist = 5;
400             else
401                 dist = 10;
402             break;
403         }
404
405         case QMetaType::QDateTime:
406         case QMetaType::QDate:
407         case QMetaType::QTime:
408             if (type == Date) {
409                 JSObject* object = value->toObject(exec);
410                 DateInstance* date = static_cast<DateInstance*>(object);
411                 GregorianDateTime gdt;
412                 date->getUTCTime(gdt);
413                 if (hint == QMetaType::QDateTime) {
414                     ret = QDateTime(QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay), QTime(gdt.hour, gdt.minute, gdt.second), Qt::UTC);
415                     dist = 0;
416                 } else if (hint == QMetaType::QDate) {
417                     ret = QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay);
418                     dist = 1;
419                 } else {
420                     ret = QTime(gdt.hour + 1900, gdt.minute, gdt.second);
421                     dist = 2;
422                 }
423             } else if (type == Number) {
424                 double b = value->toNumber(exec);
425                 GregorianDateTime gdt;
426                 msToGregorianDateTime(b, true, gdt);
427                 if (hint == QMetaType::QDateTime) {
428                     ret = QDateTime(QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay), QTime(gdt.hour, gdt.minute, gdt.second), Qt::UTC);
429                     dist = 6;
430                 } else if (hint == QMetaType::QDate) {
431                     ret = QDate(gdt.year + 1900, gdt.month + 1, gdt.monthDay);
432                     dist = 8;
433                 } else {
434                     ret = QTime(gdt.hour, gdt.minute, gdt.second);
435                     dist = 10;
436                 }
437             } else if (type == String) {
438                 UString ustring = value->toString(exec);
439                 QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
440
441                 if (hint == QMetaType::QDateTime) {
442                     QDateTime dt = QDateTime::fromString(qstring, Qt::ISODate);
443                     if (!dt.isValid())
444                         dt = QDateTime::fromString(qstring, Qt::TextDate);
445                     if (!dt.isValid())
446                         dt = QDateTime::fromString(qstring, Qt::SystemLocaleDate);
447                     if (!dt.isValid())
448                         dt = QDateTime::fromString(qstring, Qt::LocaleDate);
449                     if (dt.isValid()) {
450                         ret = dt;
451                         dist = 2;
452                     }
453                 } else if (hint == QMetaType::QDate) {
454                     QDate dt = QDate::fromString(qstring, Qt::ISODate);
455                     if (!dt.isValid())
456                         dt = QDate::fromString(qstring, Qt::TextDate);
457                     if (!dt.isValid())
458                         dt = QDate::fromString(qstring, Qt::SystemLocaleDate);
459                     if (!dt.isValid())
460                         dt = QDate::fromString(qstring, Qt::LocaleDate);
461                     if (dt.isValid()) {
462                         ret = dt;
463                         dist = 3;
464                     }
465                 } else {
466                     QTime dt = QTime::fromString(qstring, Qt::ISODate);
467                     if (!dt.isValid())
468                         dt = QTime::fromString(qstring, Qt::TextDate);
469                     if (!dt.isValid())
470                         dt = QTime::fromString(qstring, Qt::SystemLocaleDate);
471                     if (!dt.isValid())
472                         dt = QTime::fromString(qstring, Qt::LocaleDate);
473                     if (dt.isValid()) {
474                         ret = dt;
475                         dist = 3;
476                     }
477                 }
478             }
479             break;
480
481         case QMetaType::QRegExp:
482             if (type == RegExp) {
483 /*                JSObject *object = value->toObject(exec);
484                 RegExpImp *re = static_cast<RegExpImp*>(object);
485 */
486                 // Attempt to convert.. a bit risky
487                 UString ustring = value->toString(exec);
488                 QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
489
490                 // this is of the form '/xxxxxx/i'
491                 int firstSlash = qstring.indexOf('/');
492                 int lastSlash = qstring.lastIndexOf('/');
493                 if (firstSlash >=0 && lastSlash > firstSlash) {
494                     QRegExp realRe;
495
496                     realRe.setPattern(qstring.mid(firstSlash + 1, lastSlash - firstSlash - 1));
497
498                     if (qstring.mid(lastSlash + 1).contains('i'))
499                         realRe.setCaseSensitivity(Qt::CaseInsensitive);
500
501                     ret = qVariantFromValue(realRe);
502                     dist = 0;
503                 } else {
504                     qConvDebug() << "couldn't parse a JS regexp";
505                 }
506             } else if (type == String) {
507                 UString ustring = value->toString(exec);
508                 QString qstring = QString::fromUtf16((const ushort*)ustring.rep()->data(),ustring.size());
509
510                 QRegExp re(qstring);
511                 if (re.isValid()) {
512                     ret = qVariantFromValue(re);
513                     dist = 10;
514                 }
515             }
516             break;
517
518         case QMetaType::QObjectStar:
519             if (type == QObj) {
520                 JSObject* object = value->toObject(exec);
521                 QtInstance* qtinst = static_cast<QtInstance*>(Instance::getInstance(object, Instance::QtLanguage));
522                 if (qtinst) {
523                     if (qtinst->getObject()) {
524                         qConvDebug() << "found instance, with object:" << (void*) qtinst->getObject();
525                         ret = qVariantFromValue(qtinst->getObject());
526                         qConvDebug() << ret;
527                         dist = 0;
528                     } else {
529                         qConvDebug() << "can't convert deleted qobject";
530                     }
531                 } else {
532                     qConvDebug() << "wasn't a qtinstance";
533                 }
534             } else if (type == Null) {
535                 QObject* nullobj = 0;
536                 ret = qVariantFromValue(nullobj);
537                 dist = 0;
538             } else {
539                 qConvDebug() << "previous type was not an object:" << type;
540             }
541             break;
542
543         case QMetaType::VoidStar:
544             if (type == QObj) {
545                 JSObject* object = value->toObject(exec);
546                 QtInstance* qtinst = static_cast<QtInstance*>(Instance::getInstance(object, Instance::QtLanguage));
547                 if (qtinst) {
548                     if (qtinst->getObject()) {
549                         qConvDebug() << "found instance, with object:" << (void*) qtinst->getObject();
550                         ret = qVariantFromValue((void *)qtinst->getObject());
551                         qConvDebug() << ret;
552                         dist = 0;
553                     } else {
554                         qConvDebug() << "can't convert deleted qobject";
555                     }
556                 } else {
557                     qConvDebug() << "wasn't a qtinstance";
558                 }
559             } else if (type == Null) {
560                 ret = qVariantFromValue((void*)0);
561                 dist = 0;
562             } else if (type == Number) {
563                 // I don't think that converting a double to a pointer is a wise
564                 // move.  Except maybe 0.
565                 qConvDebug() << "got number for void * - not converting, seems unsafe:" << value->toNumber(exec);
566             } else {
567                 qConvDebug() << "void* - unhandled type" << type;
568             }
569             break;
570
571         default:
572             // Non const type ids
573             if (hint == (QMetaType::Type) qMetaTypeId<QObjectList>())
574             {
575                 if (type == RTArray) {
576                     JSObject* object = value->toObject(exec);
577                     RuntimeArray* rtarray = static_cast<RuntimeArray*>(object);
578
579                     QObjectList result;
580                     int len = rtarray->getLength();
581                     for (int i = 0; i < len; ++i) {
582                         JSValue *val = rtarray->getConcreteArray()->valueAt(exec, i);
583                         int itemdist = -1;
584                         QVariant item = convertValueToQVariant(exec, val, QMetaType::QObjectStar, &itemdist);
585                         if (itemdist >= 0)
586                             result.append(item.value<QObject*>());
587                         else
588                             break;
589                     }
590                     // If we didn't fail conversion
591                     if (result.count() == len) {
592                         dist = 5;
593                         ret = QVariant::fromValue(result);
594                     }
595                 } else if (type == Array) {
596                     JSObject* object = value->toObject(exec);
597                     ArrayInstance* array = static_cast<ArrayInstance *>(object);
598
599                     QObjectList result;
600                     int len = array->getLength();
601                     for (int i = 0; i < len; ++i) {
602                         JSValue* val = array->getItem(i);
603                         int itemdist = -1;
604                         QVariant item = convertValueToQVariant(exec, val, QMetaType::QObjectStar, &itemdist);
605                         if (itemdist >= 0)
606                             result.append(item.value<QObject*>());
607                         else
608                             break;
609                     }
610                     // If we didn't fail conversion
611                     if (result.count() == len) {
612                         dist = 5;
613                         ret = QVariant::fromValue(result);
614                     }
615                 } else {
616                     // Make a single length array
617                     QObjectList result;
618                     int itemdist = -1;
619                     QVariant item = convertValueToQVariant(exec, value, QMetaType::QObjectStar, &itemdist);
620                     if (itemdist >= 0) {
621                         result.append(item.value<QObject*>());
622                         dist = 10;
623                         ret = QVariant::fromValue(result);
624                     }
625                 }
626                 break;
627             } else if (hint == (QMetaType::Type) qMetaTypeId<QList<int> >()) {
628                 if (type == RTArray) {
629                     JSObject* object = value->toObject(exec);
630                     RuntimeArray* rtarray = static_cast<RuntimeArray*>(object);
631
632                     QList<int> result;
633                     int len = rtarray->getLength();
634                     for (int i = 0; i < len; ++i) {
635                         JSValue *val = rtarray->getConcreteArray()->valueAt(exec, i);
636                         int itemdist = -1;
637                         QVariant item = convertValueToQVariant(exec, val, QMetaType::Int, &itemdist);
638                         if (itemdist >= 0)
639                             result.append(item.value<int>());
640                         else
641                             break;
642                     }
643                     // If we didn't fail conversion
644                     if (result.count() == len) {
645                         dist = 5;
646                         ret = QVariant::fromValue(result);
647                     }
648                 } else if (type == Array) {
649                     JSObject* object = value->toObject(exec);
650                     ArrayInstance* array = static_cast<ArrayInstance *>(object);
651
652                     QList<int> result;
653                     int len = array->getLength();
654                     for (int i = 0; i < len; ++i) {
655                         JSValue* val = array->getItem(i);
656                         int itemdist = -1;
657                         QVariant item = convertValueToQVariant(exec, val, QMetaType::Int, &itemdist);
658                         if (itemdist >= 0)
659                             result.append(item.value<int>());
660                         else
661                             break;
662                     }
663                     // If we didn't fail conversion
664                     if (result.count() == len) {
665                         dist = 5;
666                         ret = QVariant::fromValue(result);
667                     }
668                 } else {
669                     // Make a single length array
670                     QList<int> result;
671                     int itemdist = -1;
672                     QVariant item = convertValueToQVariant(exec, value, QMetaType::Int, &itemdist);
673                     if (itemdist >= 0) {
674                         result.append(item.value<int>());
675                         dist = 10;
676                         ret = QVariant::fromValue(result);
677                     }
678                 }
679                 break;
680             } else if (hint == (QMetaType::Type) qMetaTypeId<QVariant>()) {
681                 // Well.. we can do anything... just recurse with the autodetect flag
682                 ret = convertValueToQVariant(exec, value, QMetaType::Void, distance);
683                 dist = 10;
684                 break;
685             }
686
687             dist = 10;
688             break;
689     }
690
691     if (!ret.isValid())
692         dist = -1;
693     if (distance)
694         *distance = dist;
695
696     return ret;
697 }
698
699 JSValue* convertQVariantToValue(ExecState* exec, PassRefPtr<RootObject> root, const QVariant& variant)
700 {
701     // Variants with QObject * can be isNull but not a null pointer
702     // An empty QString variant is also null
703     QMetaType::Type type = (QMetaType::Type) variant.userType();
704
705     qConvDebug() << "convertQVariantToValue: metatype:" << type << ", isnull: " << variant.isNull();
706     if (variant.isNull() &&
707         type != QMetaType::QObjectStar &&
708         type != QMetaType::VoidStar &&
709         type != QMetaType::QWidgetStar &&
710         type != QMetaType::QString) {
711         return jsNull();
712     }
713
714     JSLock lock;
715
716     if (type == QMetaType::Bool)
717         return jsBoolean(variant.toBool());
718
719     if (type == QMetaType::Int ||
720         type == QMetaType::UInt ||
721         type == QMetaType::Long ||
722         type == QMetaType::ULong ||
723         type == QMetaType::LongLong ||
724         type == QMetaType::ULongLong ||
725         type == QMetaType::Short ||
726         type == QMetaType::UShort ||
727         type == QMetaType::Float ||
728         type == QMetaType::Double)
729         return jsNumber(variant.toDouble());
730
731     if (type == QMetaType::QRegExp) {
732         QRegExp re = variant.value<QRegExp>();
733
734         if (re.isValid()) {
735             RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->lexicalGlobalObject()->regExpConstructor());
736             List args;
737             UString uflags;
738
739             if (re.caseSensitivity() == Qt::CaseInsensitive)
740                 uflags = "i"; // ### Can't do g or m
741             UString ustring((UChar*)re.pattern().utf16(), re.pattern().length());
742             args.append(jsString(ustring));
743             args.append(jsString(uflags));
744             return regExpObj->construct(exec, args);
745         }
746     }
747
748     if (type == QMetaType::QDateTime ||
749         type == QMetaType::QDate ||
750         type == QMetaType::QTime) {
751         DateObjectImp *dateObj = static_cast<DateObjectImp*>(exec->lexicalGlobalObject()->dateConstructor());
752         List args;
753
754         QDate date = QDate::currentDate();
755         QTime time(0,0,0); // midnight
756
757         if (type == QMetaType::QDate)
758             date = variant.value<QDate>();
759         else if (type == QMetaType::QTime)
760             time = variant.value<QTime>();
761         else {
762             QDateTime dt = variant.value<QDateTime>().toLocalTime();
763             date = dt.date();
764             time = dt.time();
765         }
766
767         // Dates specified this way are in local time (we convert DateTimes above)
768         args.append(jsNumber(date.year()));
769         args.append(jsNumber(date.month() - 1));
770         args.append(jsNumber(date.day()));
771         args.append(jsNumber(time.hour()));
772         args.append(jsNumber(time.minute()));
773         args.append(jsNumber(time.second()));
774         args.append(jsNumber(time.msec()));
775         return dateObj->construct(exec, args);
776     }
777
778     if (type == QMetaType::QByteArray) {
779         QByteArray ba = variant.value<QByteArray>();
780         UString ustring(ba.constData());
781         return jsString(ustring);
782     }
783
784     if (type == QMetaType::QObjectStar || type == QMetaType::QWidgetStar) {
785         QObject* obj = variant.value<QObject*>();
786         return Instance::createRuntimeObject(QtInstance::create(obj, root));
787     }
788
789     if (type == QMetaType::QVariantMap) {
790         // create a new object, and stuff properties into it
791         JSObject* ret = new JSObject(exec->lexicalGlobalObject()->objectPrototype());
792         QVariantMap map = variant.value<QVariantMap>();
793         QVariantMap::const_iterator i = map.constBegin();
794         while (i != map.constEnd()) {
795             QString s = i.key();
796             JSValue* val = convertQVariantToValue(exec, root, i.value());
797             if (val)
798                 ret->put(exec, Identifier((const UChar *)s.constData(), s.length()), val);
799             // ### error case?
800             ++i;
801         }
802
803         return ret;
804     }
805
806     // List types
807     if (type == QMetaType::QVariantList) {
808         QVariantList vl = variant.toList();
809         qConvDebug() << "got a " << vl.count() << " length list:" << vl;
810         return new RuntimeArray(exec, new QtArray<QVariant>(vl, QMetaType::Void, root));
811     } else if (type == QMetaType::QStringList) {
812         QStringList sl = variant.value<QStringList>();
813         return new RuntimeArray(exec, new QtArray<QString>(sl, QMetaType::QString, root));
814     } else if (type == (QMetaType::Type) qMetaTypeId<QObjectList>()) {
815         QObjectList ol= variant.value<QObjectList>();
816         return new RuntimeArray(exec, new QtArray<QObject*>(ol, QMetaType::QObjectStar, root));
817     } else if (type == (QMetaType::Type)qMetaTypeId<QList<int> >()) {
818         QList<int> il= variant.value<QList<int> >();
819         return new RuntimeArray(exec, new QtArray<int>(il, QMetaType::Int, root));
820     }
821
822     if (type == (QMetaType::Type)qMetaTypeId<QVariant>()) {
823         QVariant real = variant.value<QVariant>();
824         qConvDebug() << "real variant is:" << real;
825         return convertQVariantToValue(exec, root, real);
826     }
827
828     qConvDebug() << "fallback path for" << variant << variant.userType();
829
830     QString string = variant.toString();
831     UString ustring((UChar*)string.utf16(), string.length());
832     return jsString(ustring);
833 }
834
835 // ===============
836
837 // Qt-like macros
838 #define QW_D(Class) Class##Data* d = d_func()
839 #define QW_DS(Class,Instance) Class##Data* d = Instance->d_func()
840
841 QtRuntimeMethod::QtRuntimeMethod(QtRuntimeMethodData* dd, ExecState *exec, const Identifier &ident, PassRefPtr<QtInstance> inst)
842     : InternalFunctionImp (static_cast<FunctionPrototype*>(exec->lexicalGlobalObject()->functionPrototype()), ident)
843     , d_ptr(dd)
844 {
845     QW_D(QtRuntimeMethod);
846     d->m_instance = inst;
847 }
848
849 QtRuntimeMethod::~QtRuntimeMethod()
850 {
851     delete d_ptr;
852 }
853
854 CodeType QtRuntimeMethod::codeType() const
855 {
856     return FunctionCode;
857 }
858
859 Completion QtRuntimeMethod::execute(ExecState*)
860 {
861     return Completion(Normal, jsUndefined());
862 }
863
864 // ===============
865
866 QtRuntimeMethodData::~QtRuntimeMethodData()
867 {
868 }
869
870 QtRuntimeMetaMethodData::~QtRuntimeMetaMethodData()
871 {
872
873 }
874
875 QtRuntimeConnectionMethodData::~QtRuntimeConnectionMethodData()
876 {
877
878 }
879
880 // ===============
881
882 // Type conversion metadata (from QtScript originally)
883 class QtMethodMatchType
884 {
885 public:
886     enum Kind {
887         Invalid,
888         Variant,
889         MetaType,
890         Unresolved,
891         MetaEnum
892     };
893
894
895     QtMethodMatchType()
896         : m_kind(Invalid) { }
897
898     Kind kind() const
899     { return m_kind; }
900
901     QMetaType::Type typeId() const;
902
903     bool isValid() const
904     { return (m_kind != Invalid); }
905
906     bool isVariant() const
907     { return (m_kind == Variant); }
908
909     bool isMetaType() const
910     { return (m_kind == MetaType); }
911
912     bool isUnresolved() const
913     { return (m_kind == Unresolved); }
914
915     bool isMetaEnum() const
916     { return (m_kind == MetaEnum); }
917
918     QByteArray name() const;
919
920     int enumeratorIndex() const
921     { Q_ASSERT(isMetaEnum()); return m_typeId; }
922
923     static QtMethodMatchType variant()
924     { return QtMethodMatchType(Variant); }
925
926     static QtMethodMatchType metaType(int typeId, const QByteArray &name)
927     { return QtMethodMatchType(MetaType, typeId, name); }
928
929     static QtMethodMatchType metaEnum(int enumIndex, const QByteArray &name)
930     { return QtMethodMatchType(MetaEnum, enumIndex, name); }
931
932     static QtMethodMatchType unresolved(const QByteArray &name)
933     { return QtMethodMatchType(Unresolved, /*typeId=*/0, name); }
934
935 private:
936     QtMethodMatchType(Kind kind, int typeId = 0, const QByteArray &name = QByteArray())
937         : m_kind(kind), m_typeId(typeId), m_name(name) { }
938
939     Kind m_kind;
940     int m_typeId;
941     QByteArray m_name;
942 };
943
944 QMetaType::Type QtMethodMatchType::typeId() const
945 {
946     if (isVariant())
947         return (QMetaType::Type) QMetaType::type("QVariant");
948     return (QMetaType::Type) (isMetaEnum() ? QMetaType::Int : m_typeId);
949 }
950
951 QByteArray QtMethodMatchType::name() const
952 {
953     if (!m_name.isEmpty())
954         return m_name;
955     else if (m_kind == Variant)
956         return "QVariant";
957     return QByteArray();
958 }
959
960 struct QtMethodMatchData
961 {
962     int matchDistance;
963     int index;
964     QVector<QtMethodMatchType> types;
965     QVarLengthArray<QVariant, 10> args;
966
967     QtMethodMatchData(int dist, int idx, QVector<QtMethodMatchType> typs,
968                                 const QVarLengthArray<QVariant, 10> &as)
969         : matchDistance(dist), index(idx), types(typs), args(as) { }
970     QtMethodMatchData()
971         : index(-1) { }
972
973     bool isValid() const
974     { return (index != -1); }
975
976     int firstUnresolvedIndex() const
977     {
978         for (int i=0; i < types.count(); i++) {
979             if (types.at(i).isUnresolved())
980                 return i;
981         }
982         return -1;
983     }
984 };
985
986 static int indexOfMetaEnum(const QMetaObject *meta, const QByteArray &str)
987 {
988     QByteArray scope;
989     QByteArray name;
990     int scopeIdx = str.indexOf("::");
991     if (scopeIdx != -1) {
992         scope = str.left(scopeIdx);
993         name = str.mid(scopeIdx + 2);
994     } else {
995         name = str;
996     }
997     for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
998         QMetaEnum m = meta->enumerator(i);
999         if ((m.name() == name)/* && (scope.isEmpty() || (m.scope() == scope))*/)
1000             return i;
1001     }
1002     return -1;
1003 }
1004
1005 // Helper function for resolving methods
1006 // Largely based on code in QtScript for compatibility reasons
1007 static int findMethodIndex(ExecState* exec,
1008                            const QMetaObject* meta,
1009                            const QByteArray& signature,
1010                            bool allowPrivate,
1011                            const List& jsArgs,
1012                            QVarLengthArray<QVariant, 10> &vars,
1013                            void** vvars,
1014                            JSObject **pError)
1015 {
1016     QList<int> matchingIndices;
1017
1018     bool overloads = !signature.contains('(');
1019
1020     int count = meta->methodCount();
1021     for (int i = count - 1; i >= 0; --i) {
1022         const QMetaMethod m = meta->method(i);
1023
1024         // Don't choose private methods
1025         if (m.access() == QMetaMethod::Private && !allowPrivate)
1026             continue;
1027
1028         // try and find all matching named methods
1029         if (m.signature() == signature)
1030             matchingIndices.append(i);
1031         else if (overloads) {
1032             QByteArray rawsignature = m.signature();
1033             rawsignature.truncate(rawsignature.indexOf('('));
1034             if (rawsignature == signature)
1035                 matchingIndices.append(i);
1036         }
1037     }
1038
1039     int chosenIndex = -1;
1040     *pError = 0;
1041     QVector<QtMethodMatchType> chosenTypes;
1042
1043     QVarLengthArray<QVariant, 10> args;
1044     QVector<QtMethodMatchData> candidates;
1045     QVector<QtMethodMatchData> unresolved;
1046     QVector<int> tooFewArgs;
1047     QVector<int> conversionFailed;
1048
1049     foreach(int index, matchingIndices) {
1050         QMetaMethod method = meta->method(index);
1051
1052         QVector<QtMethodMatchType> types;
1053         bool unresolvedTypes = false;
1054
1055         // resolve return type
1056         QByteArray returnTypeName = method.typeName();
1057         int rtype = QMetaType::type(returnTypeName);
1058         if ((rtype == 0) && !returnTypeName.isEmpty()) {
1059             if (returnTypeName == "QVariant") {
1060                 types.append(QtMethodMatchType::variant());
1061             } else if (returnTypeName.endsWith('*')) {
1062                 types.append(QtMethodMatchType::metaType(QMetaType::VoidStar, returnTypeName));
1063             } else {
1064                 int enumIndex = indexOfMetaEnum(meta, returnTypeName);
1065                 if (enumIndex != -1)
1066                     types.append(QtMethodMatchType::metaEnum(enumIndex, returnTypeName));
1067                 else {
1068                     unresolvedTypes = true;
1069                     types.append(QtMethodMatchType::unresolved(returnTypeName));
1070                 }
1071             }
1072         } else {
1073             if (returnTypeName == "QVariant")
1074                 types.append(QtMethodMatchType::variant());
1075             else
1076                 types.append(QtMethodMatchType::metaType(rtype, returnTypeName));
1077         }
1078
1079         // resolve argument types
1080         QList<QByteArray> parameterTypeNames = method.parameterTypes();
1081         for (int i = 0; i < parameterTypeNames.count(); ++i) {
1082             QByteArray argTypeName = parameterTypeNames.at(i);
1083             int atype = QMetaType::type(argTypeName);
1084             if (atype == 0) {
1085                 if (argTypeName == "QVariant") {
1086                     types.append(QtMethodMatchType::variant());
1087                 } else {
1088                     int enumIndex = indexOfMetaEnum(meta, argTypeName);
1089                     if (enumIndex != -1)
1090                         types.append(QtMethodMatchType::metaEnum(enumIndex, argTypeName));
1091                     else {
1092                         unresolvedTypes = true;
1093                         types.append(QtMethodMatchType::unresolved(argTypeName));
1094                     }
1095                 }
1096             } else {
1097                 if (argTypeName == "QVariant")
1098                     types.append(QtMethodMatchType::variant());
1099                 else
1100                     types.append(QtMethodMatchType::metaType(atype, argTypeName));
1101             }
1102         }
1103
1104         if (jsArgs.size() < (types.count() - 1)) {
1105             qMatchDebug() << "Match:too few args for" << method.signature();
1106             tooFewArgs.append(index);
1107             continue;
1108         }
1109
1110         if (unresolvedTypes) {
1111             qMatchDebug() << "Match:unresolved arg types for" << method.signature();
1112             // remember it so we can give an error message later, if necessary
1113             unresolved.append(QtMethodMatchData(/*matchDistance=*/INT_MAX, index,
1114                                                    types, QVarLengthArray<QVariant, 10>()));
1115             continue;
1116         }
1117
1118         // Now convert arguments
1119         if (args.count() != types.count())
1120             args.resize(types.count());
1121
1122         QtMethodMatchType retType = types[0];
1123         args[0] = QVariant(retType.typeId(), (void *)0); // the return value
1124
1125         bool converted = true;
1126         int matchDistance = 0;
1127         for (int i = 0; converted && i < types.count() - 1; ++i) {
1128             JSValue* arg = i < jsArgs.size() ? jsArgs[i] : jsUndefined();
1129
1130             int argdistance = -1;
1131             QVariant v = convertValueToQVariant(exec, arg, types.at(i+1).typeId(), &argdistance);
1132             if (argdistance >= 0) {
1133                 matchDistance += argdistance;
1134                 args[i+1] = v;
1135             } else {
1136                 qMatchDebug() << "failed to convert argument " << i << "type" << types.at(i+1).typeId() << QMetaType::typeName(types.at(i+1).typeId());
1137                 converted = false;
1138             }
1139         }
1140
1141         qMatchDebug() << "Match: " << method.signature() << (converted ? "converted":"failed to convert") << "distance " << matchDistance;
1142
1143         if (converted) {
1144             if ((jsArgs.size() == types.count() - 1)
1145                 && (matchDistance == 0)) {
1146                 // perfect match, use this one
1147                 chosenIndex = index;
1148                 break;
1149             } else {
1150                 QtMethodMatchData metaArgs(matchDistance, index, types, args);
1151                 if (candidates.isEmpty()) {
1152                     candidates.append(metaArgs);
1153                 } else {
1154                     QtMethodMatchData otherArgs = candidates.at(0);
1155                     if ((args.count() > otherArgs.args.count())
1156                         || ((args.count() == otherArgs.args.count())
1157                             && (matchDistance <= otherArgs.matchDistance))) {
1158                         candidates.prepend(metaArgs);
1159                     } else {
1160                         candidates.append(metaArgs);
1161                     }
1162                 }
1163             }
1164         } else {
1165             conversionFailed.append(index);
1166         }
1167
1168         if (!overloads)
1169             break;
1170     }
1171
1172     if (chosenIndex == -1 && candidates.count() == 0) {
1173         // No valid functions at all - format an error message
1174         if (!conversionFailed.isEmpty()) {
1175             QString message = QString::fromLatin1("incompatible type of argument(s) in call to %0(); candidates were\n")
1176                               .arg(QLatin1String(signature));
1177             for (int i = 0; i < conversionFailed.size(); ++i) {
1178                 if (i > 0)
1179                     message += QLatin1String("\n");
1180                 QMetaMethod mtd = meta->method(conversionFailed.at(i));
1181                 message += QString::fromLatin1("    %0").arg(QString::fromLatin1(mtd.signature()));
1182             }
1183             *pError = throwError(exec, TypeError, message.toLatin1().constData());
1184         } else if (!unresolved.isEmpty()) {
1185             QtMethodMatchData argsInstance = unresolved.first();
1186             int unresolvedIndex = argsInstance.firstUnresolvedIndex();
1187             Q_ASSERT(unresolvedIndex != -1);
1188             QtMethodMatchType unresolvedType = argsInstance.types.at(unresolvedIndex);
1189             QString message = QString::fromLatin1("cannot call %0(): unknown type `%1'")
1190                 .arg(QString::fromLatin1(signature))
1191                 .arg(QLatin1String(unresolvedType.name()));
1192             *pError = throwError(exec, TypeError, message.toLatin1().constData());
1193         } else {
1194             QString message = QString::fromLatin1("too few arguments in call to %0(); candidates are\n")
1195                               .arg(QLatin1String(signature));
1196             for (int i = 0; i < tooFewArgs.size(); ++i) {
1197                 if (i > 0)
1198                     message += QLatin1String("\n");
1199                 QMetaMethod mtd = meta->method(tooFewArgs.at(i));
1200                 message += QString::fromLatin1("    %0").arg(QString::fromLatin1(mtd.signature()));
1201             }
1202             *pError = throwError(exec, SyntaxError, message.toLatin1().constData());
1203         }
1204     }
1205
1206     if (chosenIndex == -1 && candidates.count() > 0) {
1207         QtMethodMatchData metaArgs = candidates.at(0);
1208         if ((candidates.size() > 1)
1209             && (metaArgs.args.count() == candidates.at(1).args.count())
1210             && (metaArgs.matchDistance == candidates.at(1).matchDistance)) {
1211             // ambiguous call
1212             QString message = QString::fromLatin1("ambiguous call of overloaded function %0(); candidates were\n")
1213                                 .arg(QLatin1String(signature));
1214             for (int i = 0; i < candidates.size(); ++i) {
1215                 if (i > 0)
1216                     message += QLatin1String("\n");
1217                 QMetaMethod mtd = meta->method(candidates.at(i).index);
1218                 message += QString::fromLatin1("    %0").arg(QString::fromLatin1(mtd.signature()));
1219             }
1220             *pError = throwError(exec, TypeError, message.toLatin1().constData());
1221         } else {
1222             chosenIndex = metaArgs.index;
1223             args = metaArgs.args;
1224         }
1225     }
1226
1227     if (chosenIndex != -1) {
1228         /* Copy the stuff over */
1229         int i;
1230         vars.resize(args.count());
1231         for (i=0; i < args.count(); i++) {
1232             vars[i] = args[i];
1233             vvars[i] = vars[i].data();
1234         }
1235     }
1236
1237     return chosenIndex;
1238 }
1239
1240 // Signals are not fuzzy matched as much as methods
1241 static int findSignalIndex(const QMetaObject* meta, int initialIndex, QByteArray signature)
1242 {
1243     int index = initialIndex;
1244     QMetaMethod method = meta->method(index);
1245     bool overloads = !signature.contains('(');
1246     if (overloads && (method.attributes() & QMetaMethod::Cloned)) {
1247         // find the most general method
1248         do {
1249             method = meta->method(--index);
1250         } while (method.attributes() & QMetaMethod::Cloned);
1251     }
1252     return index;
1253 }
1254
1255 QtRuntimeMetaMethod::QtRuntimeMetaMethod(ExecState* exec, const Identifier& ident, PassRefPtr<QtInstance> inst, int index, const QByteArray& signature, bool allowPrivate)
1256     : QtRuntimeMethod (new QtRuntimeMetaMethodData(), exec, ident, inst)
1257 {
1258     QW_D(QtRuntimeMetaMethod);
1259     d->m_signature = signature;
1260     d->m_index = index;
1261     d->m_connect = 0;
1262     d->m_disconnect = 0;
1263     d->m_allowPrivate = allowPrivate;
1264 }
1265
1266 void QtRuntimeMetaMethod::mark()
1267 {
1268     QtRuntimeMethod::mark();
1269     QW_D(QtRuntimeMetaMethod);
1270     if (d->m_connect)
1271         d->m_connect->mark();
1272     if (d->m_disconnect)
1273         d->m_disconnect->mark();
1274 }
1275
1276 JSValue* QtRuntimeMetaMethod::callAsFunction(ExecState* exec, JSObject*, const List& args)
1277 {
1278     QW_D(QtRuntimeMetaMethod);
1279
1280     // We're limited to 10 args
1281     if (args.size() > 10)
1282         return jsUndefined();
1283
1284     // We have to pick a method that matches..
1285     JSLock lock;
1286
1287     QObject *obj = d->m_instance->getObject();
1288     if (obj) {
1289         QVarLengthArray<QVariant, 10> vargs;
1290         void *qargs[11];
1291
1292         int methodIndex;
1293         JSObject* errorObj = 0;
1294         if ((methodIndex = findMethodIndex(exec, obj->metaObject(), d->m_signature, d->m_allowPrivate, args, vargs, (void **)qargs, &errorObj)) != -1) {
1295             if (obj->qt_metacall(QMetaObject::InvokeMetaMethod, methodIndex, qargs) >= 0)
1296                 return jsUndefined();
1297
1298             if (vargs[0].isValid())
1299                 return convertQVariantToValue(exec, d->m_instance->rootObject(), vargs[0]);
1300         }
1301
1302         if (errorObj)
1303             return errorObj;
1304     } else {
1305         return throwError(exec, GeneralError, "cannot call function of deleted QObject");
1306     }
1307
1308     // void functions return undefined
1309     return jsUndefined();
1310 }
1311
1312 bool QtRuntimeMetaMethod::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
1313 {
1314     if (propertyName == "connect") {
1315         slot.setCustom(this, connectGetter);
1316         return true;
1317     } else if (propertyName == "disconnect") {
1318         slot.setCustom(this, disconnectGetter);
1319         return true;
1320     } else if (propertyName == exec->propertyNames().length) {
1321         slot.setCustom(this, lengthGetter);
1322         return true;
1323     }
1324
1325     return QtRuntimeMethod::getOwnPropertySlot(exec, propertyName, slot);
1326 }
1327
1328 JSValue *QtRuntimeMetaMethod::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&)
1329 {
1330     // QtScript always returns 0
1331     return jsNumber(0);
1332 }
1333
1334 JSValue *QtRuntimeMetaMethod::connectGetter(ExecState* exec, JSObject*, const Identifier& ident, const PropertySlot& slot)
1335 {
1336     QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(slot.slotBase());
1337     QW_DS(QtRuntimeMetaMethod, thisObj);
1338
1339     if (!d->m_connect)
1340         d->m_connect = new QtRuntimeConnectionMethod(exec, ident, true, d->m_instance, d->m_index, d->m_signature);
1341     return d->m_connect;
1342 }
1343
1344 JSValue* QtRuntimeMetaMethod::disconnectGetter(ExecState* exec, JSObject*, const Identifier& ident, const PropertySlot& slot)
1345 {
1346     QtRuntimeMetaMethod* thisObj = static_cast<QtRuntimeMetaMethod*>(slot.slotBase());
1347     QW_DS(QtRuntimeMetaMethod, thisObj);
1348
1349     if (!d->m_disconnect)
1350         d->m_disconnect = new QtRuntimeConnectionMethod(exec, ident, false, d->m_instance, d->m_index, d->m_signature);
1351     return d->m_disconnect;
1352 }
1353
1354 // ===============
1355
1356 QMultiMap<QObject*, QtConnectionObject*> QtRuntimeConnectionMethod::connections;
1357
1358 QtRuntimeConnectionMethod::QtRuntimeConnectionMethod(ExecState* exec, const Identifier& ident, bool isConnect, PassRefPtr<QtInstance> inst, int index, const QByteArray& signature)
1359     : QtRuntimeMethod (new QtRuntimeConnectionMethodData(), exec, ident, inst)
1360 {
1361     QW_D(QtRuntimeConnectionMethod);
1362
1363     d->m_signature = signature;
1364     d->m_index = index;
1365     d->m_isConnect = isConnect;
1366 }
1367
1368 JSValue *QtRuntimeConnectionMethod::callAsFunction(ExecState* exec, JSObject*, const List& args)
1369 {
1370     QW_D(QtRuntimeConnectionMethod);
1371
1372     JSLock lock;
1373
1374     QObject* sender = d->m_instance->getObject();
1375
1376     if (sender) {
1377
1378         JSObject* thisObject = exec->lexicalGlobalObject();
1379         JSObject* funcObject = 0;
1380
1381         // QtScript checks signalness first, arguments second
1382         int signalIndex = -1;
1383
1384         // Make sure the initial index is a signal
1385         QMetaMethod m = sender->metaObject()->method(d->m_index);
1386         if (m.methodType() == QMetaMethod::Signal)
1387             signalIndex = findSignalIndex(sender->metaObject(), d->m_index, d->m_signature);
1388
1389         if (signalIndex != -1) {
1390             if (args.size() == 1) {
1391                 funcObject = args[0]->toObject(exec);
1392                 if (!funcObject->implementsCall()) {
1393                     if (d->m_isConnect)
1394                         return throwError(exec, TypeError, "QtMetaMethod.connect: target is not a function");
1395                     else
1396                         return throwError(exec, TypeError, "QtMetaMethod.disconnect: target is not a function");
1397                 }
1398             } else if (args.size() >= 2) {
1399                 if (args[0]->type() == ObjectType) {
1400                     thisObject = args[0]->toObject(exec);
1401
1402                     // Get the actual function to call
1403                     JSObject *asObj = args[1]->toObject(exec);
1404                     if (asObj->implementsCall()) {
1405                         // Function version
1406                         funcObject = asObj;
1407                     } else {
1408                         // Convert it to a string
1409                         UString funcName = args[1]->toString(exec);
1410                         Identifier funcIdent(funcName);
1411
1412                         // ### DropAllLocks
1413                         // This is resolved at this point in QtScript
1414                         JSValue* val = thisObject->get(exec, funcIdent);
1415                         JSObject* asFuncObj = val->toObject(exec);
1416
1417                         if (asFuncObj->implementsCall()) {
1418                             funcObject = asFuncObj;
1419                         } else {
1420                             if (d->m_isConnect)
1421                                 return throwError(exec, TypeError, "QtMetaMethod.connect: target is not a function");
1422                             else
1423                                 return throwError(exec, TypeError, "QtMetaMethod.disconnect: target is not a function");
1424                         }
1425                     }
1426                 } else {
1427                     if (d->m_isConnect)
1428                         return throwError(exec, TypeError, "QtMetaMethod.connect: thisObject is not an object");
1429                     else
1430                         return throwError(exec, TypeError, "QtMetaMethod.disconnect: thisObject is not an object");
1431                 }
1432             } else {
1433                 if (d->m_isConnect)
1434                     return throwError(exec, GeneralError, "QtMetaMethod.connect: no arguments given");
1435                 else
1436                     return throwError(exec, GeneralError, "QtMetaMethod.disconnect: no arguments given");
1437             }
1438
1439             if (d->m_isConnect) {
1440                 // to connect, we need:
1441                 //  target object [from ctor]
1442                 //  target signal index etc. [from ctor]
1443                 //  receiver function [from arguments]
1444                 //  receiver this object [from arguments]
1445
1446                 QtConnectionObject* conn = new QtConnectionObject(d->m_instance, signalIndex, thisObject, funcObject);
1447                 bool ok = QMetaObject::connect(sender, signalIndex, conn, conn->metaObject()->methodOffset());
1448                 if (!ok) {
1449                     delete conn;
1450                     QString msg = QString("QtMetaMethod.connect: failed to connect to %1::%2()")
1451                             .arg(sender->metaObject()->className())
1452                             .arg(QLatin1String(d->m_signature));
1453                     return throwError(exec, GeneralError, msg.toLatin1().constData());
1454                 }
1455                 else {
1456                     // Store connection
1457                     connections.insert(sender, conn);
1458                 }
1459             } else {
1460                 // Now to find our previous connection object. Hmm.
1461                 QList<QtConnectionObject*> conns = connections.values(sender);
1462                 bool ret = false;
1463
1464                 foreach(QtConnectionObject* conn, conns) {
1465                     // Is this the right connection?
1466                     if (conn->match(sender, signalIndex, thisObject, funcObject)) {
1467                         // Yep, disconnect it
1468                         QMetaObject::disconnect(sender, signalIndex, conn, conn->metaObject()->methodOffset());
1469                         delete conn; // this will also remove it from the map
1470                         ret = true;
1471                         break;
1472                     }
1473                 }
1474
1475                 if (!ret) {
1476                     QString msg = QString("QtMetaMethod.disconnect: failed to disconnect from %1::%2()")
1477                             .arg(sender->metaObject()->className())
1478                             .arg(QLatin1String(d->m_signature));
1479                     return throwError(exec, GeneralError, msg.toLatin1().constData());
1480                 }
1481             }
1482         } else {
1483             QString msg = QString("QtMetaMethod.%1: %2::%3() is not a signal")
1484                     .arg(d->m_isConnect ? "connect": "disconnect")
1485                     .arg(sender->metaObject()->className())
1486                     .arg(QLatin1String(d->m_signature));
1487             return throwError(exec, TypeError, msg.toLatin1().constData());
1488         }
1489     } else {
1490         return throwError(exec, GeneralError, "cannot call function of deleted QObject");
1491     }
1492
1493     return jsUndefined();
1494 }
1495
1496 bool QtRuntimeConnectionMethod::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
1497 {
1498     if (propertyName == exec->propertyNames().length) {
1499         slot.setCustom(this, lengthGetter);
1500         return true;
1501     }
1502
1503     return QtRuntimeMethod::getOwnPropertySlot(exec, propertyName, slot);
1504 }
1505
1506 JSValue *QtRuntimeConnectionMethod::lengthGetter(ExecState*, JSObject*, const Identifier&, const PropertySlot&)
1507 {
1508     // we have one formal argument, and one optional
1509     return jsNumber(1);
1510 }
1511
1512 // ===============
1513
1514 QtConnectionObject::QtConnectionObject(PassRefPtr<QtInstance> instance, int signalIndex, JSObject* thisObject, JSObject* funcObject)
1515     : m_instance(instance)
1516     , m_signalIndex(signalIndex)
1517     , m_originalObject(m_instance->getObject())
1518     , m_thisObject(thisObject)
1519     , m_funcObject(funcObject)
1520 {
1521     setParent(m_originalObject);
1522     ASSERT(JSLock::currentThreadIsHoldingLock()); // so our ProtectedPtrs are safe
1523 }
1524
1525 QtConnectionObject::~QtConnectionObject()
1526 {
1527     // Remove us from the map of active connections
1528     QtRuntimeConnectionMethod::connections.remove(m_originalObject, this);
1529 }
1530
1531 static const uint qt_meta_data_QtConnectionObject[] = {
1532
1533  // content:
1534        1,       // revision
1535        0,       // classname
1536        0,    0, // classinfo
1537        1,   10, // methods
1538        0,    0, // properties
1539        0,    0, // enums/sets
1540
1541  // slots: signature, parameters, type, tag, flags
1542       28,   27,   27,   27, 0x0a,
1543
1544        0        // eod
1545 };
1546
1547 static const char qt_meta_stringdata_QtConnectionObject[] = {
1548     "KJS::Bindings::QtConnectionObject\0\0execute()\0"
1549 };
1550
1551 const QMetaObject QtConnectionObject::staticMetaObject = {
1552     { &QObject::staticMetaObject, qt_meta_stringdata_QtConnectionObject,
1553       qt_meta_data_QtConnectionObject, 0 }
1554 };
1555
1556 const QMetaObject *QtConnectionObject::metaObject() const
1557 {
1558     return &staticMetaObject;
1559 }
1560
1561 void *QtConnectionObject::qt_metacast(const char *_clname)
1562 {
1563     if (!_clname) return 0;
1564     if (!strcmp(_clname, qt_meta_stringdata_QtConnectionObject))
1565         return static_cast<void*>(const_cast<QtConnectionObject*>(this));
1566     return QObject::qt_metacast(_clname);
1567 }
1568
1569 int QtConnectionObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
1570 {
1571     _id = QObject::qt_metacall(_c, _id, _a);
1572     if (_id < 0)
1573         return _id;
1574     if (_c == QMetaObject::InvokeMetaMethod) {
1575         switch (_id) {
1576         case 0: execute(_a); break;
1577         }
1578         _id -= 1;
1579     }
1580     return _id;
1581 }
1582
1583 void QtConnectionObject::execute(void **argv)
1584 {
1585     QObject* obj = m_instance->getObject();
1586     if (obj) {
1587         const QMetaObject* meta = obj->metaObject();
1588         const QMetaMethod method = meta->method(m_signalIndex);
1589
1590         QList<QByteArray> parameterTypes = method.parameterTypes();
1591
1592         int argc = parameterTypes.count();
1593
1594         JSLock lock;
1595
1596         // ### Should the Interpreter/ExecState come from somewhere else?
1597         RefPtr<RootObject> ro = m_instance->rootObject();
1598         if (ro) {
1599             JSGlobalObject* globalobj = ro->globalObject();
1600             if (globalobj) {
1601                 ExecState* exec = globalobj->globalExec();
1602                 if (exec) {
1603                     // Build the argument list (up to the formal argument length of the slot)
1604                     List l;
1605                     // ### DropAllLocks?
1606                     int funcArgC = m_funcObject->get(exec, exec->propertyNames().length)->toInt32(exec);
1607                     int argTotal = qMax(funcArgC, argc);
1608                     for(int i=0; i < argTotal; i++) {
1609                         if (i < argc) {
1610                             int argType = QMetaType::type(parameterTypes.at(i));
1611                             l.append(convertQVariantToValue(exec, ro, QVariant(argType, argv[i+1])));
1612                         } else {
1613                             l.append(jsUndefined());
1614                         }
1615                     }
1616                     // Stuff in the __qt_sender property, if we can
1617                     if (m_funcObject->inherits(&FunctionImp::info)) {
1618                         FunctionImp* fimp = static_cast<FunctionImp*>(m_funcObject.get());
1619
1620                         JSObject* qt_sender = Instance::createRuntimeObject(QtInstance::create(sender(), ro));
1621                         JSObject* wrapper = new JSObject();
1622                         wrapper->put(exec, "__qt_sender__", qt_sender);
1623                         ScopeChain oldsc = fimp->scope();
1624                         ScopeChain sc = oldsc;
1625                         sc.push(wrapper);
1626                         fimp->setScope(sc);
1627                         fimp->call(exec, m_thisObject, l);
1628                         fimp->setScope(oldsc);
1629                     } else
1630                         m_funcObject->call(exec, m_thisObject, l);
1631                 }
1632             }
1633         }
1634     } else {
1635         // A strange place to be - a deleted object emitted a signal here.
1636         qWarning() << "sender deleted, cannot deliver signal";
1637     }
1638 }
1639
1640 bool QtConnectionObject::match(QObject* sender, int signalIndex, JSObject* thisObject, JSObject *funcObject)
1641 {
1642     if (m_originalObject == sender && m_signalIndex == signalIndex
1643         && thisObject == (JSObject*)m_thisObject && funcObject == (JSObject*)m_funcObject)
1644         return true;
1645     return false;
1646 }
1647
1648 // ===============
1649
1650 template <typename T> QtArray<T>::QtArray(QList<T> list, QMetaType::Type type, PassRefPtr<RootObject> rootObject)
1651     : Array(rootObject)
1652     , m_list(list)
1653     , m_type(type)
1654 {
1655     m_length = m_list.count();
1656 }
1657
1658 template <typename T> QtArray<T>::~QtArray ()
1659 {
1660 }
1661
1662 template <typename T> RootObject* QtArray<T>::rootObject() const
1663 {
1664     return _rootObject && _rootObject->isValid() ? _rootObject.get() : 0;
1665 }
1666
1667 template <typename T> void QtArray<T>::setValueAt(ExecState *exec, unsigned int index, JSValue *aValue) const
1668 {
1669     // QtScript sets the value, but doesn't forward it to the original source
1670     // (e.g. if you do 'object.intList[5] = 6', the object is not updated, but the
1671     // copy of the list is).
1672     int dist = -1;
1673     QVariant val = convertValueToQVariant(exec, aValue, m_type, &dist);
1674
1675     if (dist >= 0) {
1676         m_list[index] = val.value<T>();
1677     }
1678 }
1679
1680
1681 template <typename T> JSValue* QtArray<T>::valueAt(ExecState *exec, unsigned int index) const
1682 {
1683     if (index < m_length) {
1684         T val = m_list.at(index);
1685         return convertQVariantToValue(exec, rootObject(), QVariant::fromValue(val));
1686     }
1687
1688     return jsUndefined();
1689 }
1690
1691 // ===============
1692
1693 } }