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