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