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