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