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