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