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 "APICast.h" 24#include "APIShims.h" 25#include "BooleanObject.h" 26#include "DateInstance.h" 27#include "DatePrototype.h" 28#include "FunctionPrototype.h" 29#include "Interpreter.h" 30#include "JSArray.h" 31#include "JSContextRefPrivate.h" 32#include "JSDOMBinding.h" 33#include "JSDOMWindow.h" 34#include "JSDocument.h" 35#include "JSGlobalObject.h" 36#include "JSHTMLElement.h" 37#include "JSLock.h" 38#include "JSObject.h" 39#include "JSRetainPtr.h" 40#include "JSUint8ClampedArray.h" 41#include "ObjectPrototype.h" 42#include "PropertyNameArray.h" 43#include "qdatetime.h" 44#include "qdebug.h" 45#include "qmetaobject.h" 46#include "qmetatype.h" 47#include "qobject.h" 48#include "qstringlist.h" 49#include "qt_instance.h" 50#include "qt_pixmapruntime.h" 51#include "qvarlengtharray.h" 52#include <JSFunction.h> 53 54#include <wtf/DateMath.h> 55 56#include <limits.h> 57#include <runtime/Error.h> 58#include <runtime_array.h> 59#include <runtime_object.h> 60 61// QtScript has these 62Q_DECLARE_METATYPE(QObjectList); 63Q_DECLARE_METATYPE(QList<int>); 64Q_DECLARE_METATYPE(QVariant); 65 66using namespace WebCore; 67 68namespace JSC { 69namespace Bindings { 70 71// Debugging 72//#define QTWK_RUNTIME_CONVERSION_DEBUG 73//#define QTWK_RUNTIME_MATCH_DEBUG 74 75class QWKNoDebug 76{ 77public: 78 inline QWKNoDebug(){} 79 inline ~QWKNoDebug(){} 80 81 template<typename T> 82 inline QWKNoDebug &operator<<(const T &) { return *this; } 83}; 84 85#ifdef QTWK_RUNTIME_CONVERSION_DEBUG 86#define qConvDebug() qDebug() 87#else 88#define qConvDebug() QWKNoDebug() 89#endif 90 91#ifdef QTWK_RUNTIME_MATCH_DEBUG 92#define qMatchDebug() qDebug() 93#else 94#define qMatchDebug() QWKNoDebug() 95#endif 96 97typedef enum { 98 Variant = 0, 99 Number, 100 Boolean, 101 RTString, 102 Date, 103 Array, 104 QObj, 105 Object, 106 Null, 107 RTUint8Array 108} JSRealType; 109 110#if defined(QTWK_RUNTIME_CONVERSION_DEBUG) || defined(QTWK_RUNTIME_MATCH_DEBUG) 111QDebug operator<<(QDebug dbg, const JSRealType &c) 112{ 113 const char *map[] = { "Variant", "Number", "Boolean", "RTString", "Date", 114 "Array", "RTObject", "Object", "Null"}; 115 116 dbg.nospace() << "JSType(" << ((int)c) << ", " << map[c] << ")"; 117 118 return dbg.space(); 119} 120#endif 121 122void setException(JSContextRef context, JSValueRef* exception, const QString& text) 123{ 124 if (!exception) 125 return; 126 127 JSStringRef errorStr = JSStringCreateWithUTF8CString(text.toUtf8()); 128 JSValueRef errorVal[] = { JSValueMakeString(context, errorStr) }; 129 *exception = JSObjectMakeError(context, 1, errorVal, 0); 130 JSStringRelease(errorStr); 131} 132 133struct RuntimeConversion { 134 ConvertToJSValueFunction toJSValueFunc; 135 ConvertToVariantFunction toVariantFunc; 136}; 137 138typedef QHash<int, RuntimeConversion> RuntimeConversionTable; 139Q_GLOBAL_STATIC(RuntimeConversionTable, customRuntimeConversions) 140 141void registerCustomType(int qtMetaTypeId, ConvertToVariantFunction toVariantFunc, ConvertToJSValueFunction toJSValueFunc) 142{ 143 RuntimeConversion conversion; 144 conversion.toJSValueFunc = toJSValueFunc; 145 conversion.toVariantFunc = toVariantFunc; 146 customRuntimeConversions()->insert(qtMetaTypeId, conversion); 147} 148 149static bool isJSUint8Array(JSObjectRef object) 150{ 151 return toJS(object)->inherits(&JSUint8Array::s_info); 152} 153 154static bool isJSArray(JSObjectRef object) 155{ 156 return toJS(object)->inherits(&JSArray::s_info); 157} 158 159static bool isJSDate(JSObjectRef object) 160{ 161 return toJS(object)->inherits(&DateInstance::s_info); 162} 163 164static bool isQtObject(JSObjectRef object) 165{ 166 return toJS(object)->inherits(&RuntimeObject::s_info); 167} 168 169static JSRealType valueRealType(JSContextRef context, JSValueRef value, JSValueRef* exception) 170{ 171 if (JSValueIsNumber(context, value)) 172 return Number; 173 if (JSValueIsString(context, value)) 174 return RTString; 175 if (JSValueIsBoolean(context, value)) 176 return Boolean; 177 if (JSValueIsNull(context, value)) 178 return Null; 179 if (!JSValueIsObject(context, value)) 180 return RTString; // I don't know. 181 182 JSObjectRef object = JSValueToObject(context, value, exception); 183 184 if (isJSUint8Array(object)) 185 return RTUint8Array; 186 if (isJSArray(object)) 187 return Array; 188 if (isJSDate(object)) 189 return Date; 190 if (isQtObject(object)) 191 return QObj; 192 193 return Object; 194} 195 196static QString toString(JSStringRef stringRef) 197{ 198 return QString(reinterpret_cast<const QChar*>(JSStringGetCharactersPtr(stringRef)), JSStringGetLength(stringRef)); 199} 200 201static JSValueRef unwrapBoxedPrimitive(JSContextRef context, JSValueRef value, JSObjectRef obj) 202{ 203 ExecState* exec = toJS(context); 204 APIEntryShim entryShim(exec); 205 JSObject* object = toJS(obj); 206 if (object->inherits(&NumberObject::s_info)) 207 return toRef(exec, jsNumber(object->toNumber(exec))); 208 if (object->inherits(&StringObject::s_info)) 209 return toRef(exec, object->toString(exec)); 210 if (object->inherits(&BooleanObject::s_info)) 211 return toRef(exec, object->toPrimitive(exec)); 212 return value; 213} 214 215QVariant convertValueToQVariant(JSContextRef, JSValueRef, QMetaType::Type, int*, HashSet<JSObjectRef>*, int, JSValueRef *exception); 216 217static QVariantMap convertValueToQVariantMap(JSContextRef context, JSObjectRef object, HashSet<JSObjectRef>* visitedObjects, int recursionLimit, JSValueRef* exception) 218{ 219 QVariantMap result; 220 JSPropertyNameArrayRef properties = JSObjectCopyPropertyNames(context, object); 221 size_t propertyCount = JSPropertyNameArrayGetCount(properties); 222 223 for (size_t i = 0; i < propertyCount; ++i) { 224 JSStringRef name = JSPropertyNameArrayGetNameAtIndex(properties, i); 225 226 int propertyConversionDistance = 0; 227 JSValueRef property = JSObjectGetProperty(context, object, name, exception); 228 QVariant v = convertValueToQVariant(context, property, QMetaType::Void, &propertyConversionDistance, visitedObjects, recursionLimit, exception); 229 if (exception && *exception) 230 *exception = 0; 231 else if (propertyConversionDistance >= 0) { 232 result.insert(toString(name), v); 233 } 234 } 235 JSPropertyNameArrayRelease(properties); 236 return result; 237} 238 239template <typename ItemType> 240QList<ItemType> convertToList(JSContextRef context, JSRealType type, JSObjectRef object, 241 JSValueRef value, int* distance, HashSet<JSObjectRef>* visitedObjects, int recursionLimit, JSValueRef* exception, 242 const QMetaType::Type typeId = static_cast<QMetaType::Type>(qMetaTypeId<ItemType>())) 243{ 244 QList<ItemType> list; 245 if (type == Array) { 246 static JSStringRef lengthStr = JSStringCreateWithUTF8CString("length"); 247 JSValueRef lengthVal = JSObjectGetProperty(context, object, lengthStr, exception); 248 size_t length = JSValueToNumber(context, lengthVal, exception); 249 list.reserve(length); 250 for (size_t i = 0; i < length; ++i) { 251 JSValueRef value = JSObjectGetPropertyAtIndex(context, object, i, exception); 252 int itemDistance = -1; 253 QVariant variant = convertValueToQVariant(context, value, typeId, &itemDistance, visitedObjects, recursionLimit, exception); 254 if (itemDistance >= 0) 255 list << variant.value<ItemType>(); 256 else 257 break; 258 } 259 if (list.count() != length) 260 list.clear(); 261 else if (distance) 262 *distance = 5; 263 } else { 264 int itemDistance = -1; 265 QVariant variant = convertValueToQVariant(context, value, typeId, &itemDistance, visitedObjects, recursionLimit, exception); 266 if (itemDistance >= 0) { 267 list << variant.value<ItemType>(); 268 if (distance) 269 *distance = 10; 270 } 271 } 272 return list; 273} 274 275static QString toQString(JSContextRef context, JSValueRef value) 276{ 277 JSRetainPtr<JSStringRef> string(Adopt, JSValueToStringCopy(context, value, 0)); 278 if (!string) 279 return QString(); 280 return QString(reinterpret_cast<const QChar*>(JSStringGetCharactersPtr(string.get())), JSStringGetLength(string.get())); 281} 282 283static void getGregorianDateTimeUTC(JSContextRef context, JSRealType type, JSValueRef value, JSObjectRef object, JSValueRef* exception, GregorianDateTime* gdt) 284{ 285 ExecState* exec = toJS(context); 286 APIEntryShim entryShim(exec); 287 if (type == Date) { 288 JSObject* jsObject = toJS(object); 289 DateInstance* date = asDateInstance(jsObject); 290 gdt->copyFrom(*date->gregorianDateTimeUTC(exec)); 291 } else { 292 double ms = JSValueToNumber(context, value, exception); 293 GregorianDateTime convertedGdt; 294 msToGregorianDateTime(exec, ms, /*utc*/ true, convertedGdt); 295 gdt->copyFrom(convertedGdt); 296 } 297} 298 299static QDateTime toQDateTimeUTC(JSContextRef context, JSRealType type, JSValueRef value, JSObjectRef object, JSValueRef* exception) 300{ 301 GregorianDateTime gdt; 302 getGregorianDateTimeUTC(context, type, value, object, exception, &gdt); 303 QDate date(gdt.year(), gdt.month() + 1, gdt.monthDay()); 304 QTime time(gdt.hour(), gdt.minute(), gdt.second()); 305 return QDateTime(date, time, Qt::UTC); 306} 307 308QVariant convertValueToQVariant(JSContextRef context, JSValueRef value, QMetaType::Type hint, int *distance, HashSet<JSObjectRef>* visitedObjects, int recursionLimit, JSValueRef* exception) 309{ 310 --recursionLimit; 311 312 if (!value || !recursionLimit) 313 return QVariant(); 314 315 JSObjectRef object = 0; 316 if (JSValueIsObject(context, value)) { 317 object = JSValueToObject(context, value, 0); 318 if (visitedObjects->contains(object)) 319 return QVariant(); 320 321 visitedObjects->add(object); 322 323 value = unwrapBoxedPrimitive(context, value, object); 324 } 325 326 // check magic pointer values before dereferencing value 327 if (JSValueIsNumber(context, value) 328 && std::isnan(JSValueToNumber(context, value, exception))) { 329 if (distance) 330 *distance = -1; 331 return QVariant(); 332 } 333 334 if (JSValueIsUndefined(context, value) && hint != QMetaType::QString && hint != (QMetaType::Type) qMetaTypeId<QVariant>()) { 335 if (distance) 336 *distance = -1; 337 return QVariant(); 338 } 339 340 JSRealType type = valueRealType(context, value, exception); 341 if (hint == QMetaType::Void) { 342 switch(type) { 343 case Number: 344 hint = QMetaType::Double; 345 break; 346 case Boolean: 347 hint = QMetaType::Bool; 348 break; 349 case RTString: 350 default: 351 hint = QMetaType::QString; 352 break; 353 case Date: 354 hint = QMetaType::QDateTime; 355 break; 356 case Object: 357 hint = QMetaType::QVariantMap; 358 break; 359 case QObj: 360 hint = QMetaType::QObjectStar; 361 break; 362 case RTUint8Array: 363 hint = QMetaType::QByteArray; 364 break; 365 case Array: 366 hint = QMetaType::QVariantList; 367 break; 368 } 369 } 370 371 qConvDebug() << "convertValueToQVariant: jstype is " << type << ", hint is" << hint; 372 373 if (JSValueIsNull(context, value) 374 && hint != QMetaType::QObjectStar 375 && hint != QMetaType::VoidStar 376 && hint != QMetaType::QString 377 && hint != (QMetaType::Type) qMetaTypeId<QVariant>()) { 378 if (distance) 379 *distance = -1; 380 return QVariant(); 381 } 382 383 QVariant ret; 384 int dist = -1; 385 switch (hint) { 386 case QMetaType::Bool: 387 ret = QVariant(JSValueToBoolean(context, value)); 388 if (type == Boolean) 389 dist = 0; 390 else 391 dist = 10; 392 break; 393 394 case QMetaType::Int: 395 case QMetaType::UInt: 396 case QMetaType::Long: 397 case QMetaType::ULong: 398 case QMetaType::LongLong: 399 case QMetaType::ULongLong: 400 case QMetaType::Short: 401 case QMetaType::UShort: 402 case QMetaType::Float: 403 case QMetaType::Double: 404 ret = QVariant(JSValueToNumber(context, value, 0)); 405 ret.convert((QVariant::Type)hint); 406 if (type == Number) { 407 switch (hint) { 408 case QMetaType::Double: 409 dist = 0; 410 break; 411 case QMetaType::Float: 412 dist = 1; 413 break; 414 case QMetaType::LongLong: 415 case QMetaType::ULongLong: 416 dist = 2; 417 break; 418 case QMetaType::Long: 419 case QMetaType::ULong: 420 dist = 3; 421 break; 422 case QMetaType::Int: 423 case QMetaType::UInt: 424 dist = 4; 425 break; 426 case QMetaType::Short: 427 case QMetaType::UShort: 428 dist = 5; 429 break; 430 break; 431 default: 432 dist = 10; 433 break; 434 } 435 } else { 436 dist = 10; 437 } 438 break; 439 440 case QMetaType::QChar: 441 if (type == Number || type == Boolean) { 442 ret = QVariant(QChar((ushort)JSValueToNumber(context, value, 0))); 443 if (type == Boolean) 444 dist = 3; 445 else 446 dist = 6; 447 } else { 448 JSRetainPtr<JSStringRef> str(Adopt, JSValueToStringCopy(context, value, exception)); 449 QChar ch; 450 if (str && JSStringGetLength(str.get()) > 0) 451 ch = *reinterpret_cast<const QChar*>(JSStringGetCharactersPtr(str.get())); 452 ret = QVariant(ch); 453 if (type == RTString) 454 dist = 3; 455 else 456 dist = 10; 457 } 458 break; 459 460 case QMetaType::QString: { 461 if (JSValueIsNull(context, value) || JSValueIsUndefined(context, value)) { 462 if (distance) 463 *distance = 1; 464 return QString(); 465 } 466 JSRetainPtr<JSStringRef> str(Adopt, JSValueToStringCopy(context, value, exception)); 467 if (str) { 468 QString string(reinterpret_cast<const QChar*>(JSStringGetCharactersPtr(str.get())), JSStringGetLength(str.get())); 469 ret = QVariant(string); 470 if (type == RTString) 471 dist = 0; 472 else 473 dist = 10; 474 } 475 break; 476 } 477 478 case QMetaType::QVariantMap: 479 if (type == Object || type == Array) { 480 ret = QVariant(convertValueToQVariantMap(context, object, visitedObjects, recursionLimit, exception)); 481 // Those types can still have perfect matches, e.g. 'bool' if value is a Boolean Object. 482 dist = 1; 483 } 484 break; 485 486 case QMetaType::QVariantList: 487 ret = QVariant(convertToList<QVariant>(context, type, object, value, &dist, visitedObjects, recursionLimit, exception, QMetaType::Void)); 488 break; 489 490 case QMetaType::QStringList: { 491 ret = QVariant(convertToList<QString>(context, type, object, value, &dist, visitedObjects, recursionLimit, exception)); 492 break; 493 } 494 495 case QMetaType::QByteArray: { 496 if (type == RTUint8Array) { 497 WTF::Uint8Array* arr = toUint8Array(toJS(toJS(context), value)); 498 ret = QVariant(QByteArray(reinterpret_cast<const char*>(arr->data()), arr->length())); 499 dist = 0; 500 } else { 501 ret = QVariant(toQString(context, value).toLatin1()); 502 if (type == RTString) 503 dist = 5; 504 else 505 dist = 10; 506 } 507 break; 508 } 509 510 case QMetaType::QDateTime: 511 case QMetaType::QDate: 512 case QMetaType::QTime: 513 if (type == Date || type == Number) { 514 QDateTime dt = toQDateTimeUTC(context, type, value, object, exception); 515 const bool isNumber = (type == Number); 516 if (hint == QMetaType::QDateTime) { 517 ret = dt; 518 dist = isNumber ? 6 : 0; 519 } else if (hint == QMetaType::QDate) { 520 ret = dt.date(); 521 dist = isNumber ? 8 : 1; 522 } else { 523 ret = dt.time(); 524 dist = isNumber ? 10 : 2; 525 } 526 } else if (type == RTString) { 527 QString qstring = toQString(context, value); 528 if (hint == QMetaType::QDateTime) { 529 QDateTime dt = QDateTime::fromString(qstring, Qt::ISODate); 530 if (!dt.isValid()) 531 dt = QDateTime::fromString(qstring, Qt::TextDate); 532 if (!dt.isValid()) 533 dt = QDateTime::fromString(qstring, Qt::SystemLocaleDate); 534 if (!dt.isValid()) 535 dt = QDateTime::fromString(qstring, Qt::LocaleDate); 536 if (dt.isValid()) { 537 ret = dt; 538 dist = 2; 539 } 540 } else if (hint == QMetaType::QDate) { 541 QDate dt = QDate::fromString(qstring, Qt::ISODate); 542 if (!dt.isValid()) 543 dt = QDate::fromString(qstring, Qt::TextDate); 544 if (!dt.isValid()) 545 dt = QDate::fromString(qstring, Qt::SystemLocaleDate); 546 if (!dt.isValid()) 547 dt = QDate::fromString(qstring, Qt::LocaleDate); 548 if (dt.isValid()) { 549 ret = dt; 550 dist = 3; 551 } 552 } else { 553 QTime dt = QTime::fromString(qstring, Qt::ISODate); 554 if (!dt.isValid()) 555 dt = QTime::fromString(qstring, Qt::TextDate); 556 if (!dt.isValid()) 557 dt = QTime::fromString(qstring, Qt::SystemLocaleDate); 558 if (!dt.isValid()) 559 dt = QTime::fromString(qstring, Qt::LocaleDate); 560 if (dt.isValid()) { 561 ret = dt; 562 dist = 3; 563 } 564 } 565 } 566 break; 567 568 case QMetaType::QObjectStar: 569 if (type == QObj) { 570 QtInstance* qtinst = QtInstance::getInstance(toJS(object)); 571 if (qtinst) { 572 if (qtinst->getObject()) { 573 qConvDebug() << "found instance, with object:" << (void*) qtinst->getObject(); 574 ret = QVariant::fromValue(qtinst->getObject()); 575 qConvDebug() << ret; 576 dist = 0; 577 } else { 578 qConvDebug() << "can't convert deleted qobject"; 579 } 580 } else { 581 qConvDebug() << "wasn't a qtinstance"; 582 } 583 } else if (type == Null) { 584 QObject* nullobj = 0; 585 ret = QVariant::fromValue(nullobj); 586 dist = 0; 587 } else { 588 qConvDebug() << "previous type was not an object:" << type; 589 } 590 break; 591 592 case QMetaType::VoidStar: 593 if (type == QObj) { 594 QtInstance* qtinst = QtInstance::getInstance(toJS(object)); 595 if (qtinst) { 596 if (qtinst->getObject()) { 597 qConvDebug() << "found instance, with object:" << (void*) qtinst->getObject(); 598 ret = QVariant::fromValue((void *)qtinst->getObject()); 599 qConvDebug() << ret; 600 dist = 0; 601 } else { 602 qConvDebug() << "can't convert deleted qobject"; 603 } 604 } else { 605 qConvDebug() << "wasn't a qtinstance"; 606 } 607 } else if (type == Null) { 608 ret = QVariant::fromValue((void*)0); 609 dist = 0; 610 } else if (type == Number) { 611 // I don't think that converting a double to a pointer is a wise 612 // move. Except maybe 0. 613 qConvDebug() << "got number for void * - not converting, seems unsafe:" << JSValueToNumber(context, value, 0); 614 } else { 615 qConvDebug() << "void* - unhandled type" << type; 616 } 617 break; 618 619 default: 620 // Non const type ids 621 if (hint == (QMetaType::Type) qMetaTypeId<QObjectList>()) { 622 ret = QVariant::fromValue(convertToList<QObject*>(context, type, object, value, &dist, visitedObjects, recursionLimit, exception)); 623 break; 624 } 625 if (hint == (QMetaType::Type) qMetaTypeId<QList<int> >()) { 626 ret = QVariant::fromValue(convertToList<int>(context, type, object, value, &dist, visitedObjects, recursionLimit, exception)); 627 break; 628 } 629 if (QtPixmapRuntime::canHandle(static_cast<QMetaType::Type>(hint))) { 630 ret = QtPixmapRuntime::toQt(context, object, static_cast<QMetaType::Type>(hint), exception); 631 } else if (customRuntimeConversions()->contains(hint)) { 632 ret = customRuntimeConversions()->value(hint).toVariantFunc(toJS(object), &dist, visitedObjects); 633 if (dist == 0) 634 break; 635 } else if (hint == (QMetaType::Type) qMetaTypeId<QVariant>()) { 636 if (JSValueIsNull(context, value) || JSValueIsUndefined(context, value)) { 637 if (distance) 638 *distance = 1; 639 return QVariant(); 640 } 641 if (type == Object) { 642 // Since we haven't really visited this object yet, we remove it 643 visitedObjects->remove(object); 644 } 645 646 // And then recurse with the autodetect flag 647 ret = convertValueToQVariant(context, value, QMetaType::Void, distance, visitedObjects, recursionLimit, exception); 648 dist = 10; 649 break; 650 } 651 652 dist = 10; 653 break; 654 } 655 656 if (!ret.isValid()) 657 dist = -1; 658 if (distance) 659 *distance = dist; 660 661 return ret; 662} 663 664QVariant convertValueToQVariant(JSContextRef context, JSValueRef value, QMetaType::Type hint, int *distance, JSValueRef *exception) 665{ 666 const int recursionLimit = 200; 667 HashSet<JSObjectRef> visitedObjects; 668 return convertValueToQVariant(context, value, hint, distance, &visitedObjects, recursionLimit, exception); 669} 670 671JSValueRef convertQVariantToValue(JSContextRef context, PassRefPtr<RootObject> root, const QVariant& variant, JSValueRef *exception) 672{ 673 // Variants with QObject * can be isNull but not a null pointer 674 // An empty QString variant is also null 675 QMetaType::Type type = (QMetaType::Type) variant.userType(); 676 677 qConvDebug() << "convertQVariantToValue: metatype:" << type << ", isnull: " << variant.isNull(); 678 if (variant.isNull() && 679 !QMetaType::typeFlags(type).testFlag(QMetaType::PointerToQObject) && 680 type != QMetaType::VoidStar && 681 type != QMetaType::QString) { 682 return JSValueMakeNull(context); 683 } 684 685 if (type == QMetaType::Bool) 686 return JSValueMakeBoolean(context, variant.toBool()); 687 688 if (type == QMetaType::Int || 689 type == QMetaType::UInt || 690 type == QMetaType::Long || 691 type == QMetaType::ULong || 692 type == QMetaType::LongLong || 693 type == QMetaType::ULongLong || 694 type == QMetaType::Short || 695 type == QMetaType::UShort || 696 type == QMetaType::Float || 697 type == QMetaType::Double) 698 return JSValueMakeNumber(context, variant.toDouble()); 699 700 if (type == QMetaType::QDateTime || 701 type == QMetaType::QDate || 702 type == QMetaType::QTime) { 703 704 QDate date = QDate::currentDate(); 705 QTime time(0,0,0); // midnight 706 707 if (type == QMetaType::QDate) 708 date = variant.value<QDate>(); 709 else if (type == QMetaType::QTime) 710 time = variant.value<QTime>(); 711 else { 712 QDateTime dt = variant.value<QDateTime>().toLocalTime(); 713 date = dt.date(); 714 time = dt.time(); 715 } 716 717 // Dates specified this way are in local time (we convert DateTimes above) 718 const JSValueRef arguments[] = { 719 JSValueMakeNumber(context, date.year()), 720 JSValueMakeNumber(context, date.month() - 1), 721 JSValueMakeNumber(context, date.day()), 722 JSValueMakeNumber(context, time.hour()), 723 JSValueMakeNumber(context, time.minute()), 724 JSValueMakeNumber(context, time.second()), 725 JSValueMakeNumber(context, time.msec()) 726 }; 727 return JSObjectMakeDate(context, 7, arguments, exception); 728 } 729 730 if (type == QMetaType::QByteArray) { 731 QByteArray qtByteArray = variant.value<QByteArray>(); 732 WTF::RefPtr<WTF::Uint8ClampedArray> wtfByteArray = WTF::Uint8ClampedArray::createUninitialized(qtByteArray.length()); 733 memcpy(wtfByteArray->data(), qtByteArray.constData(), qtByteArray.length()); 734 ExecState* exec = toJS(context); 735 APIEntryShim entryShim(exec); 736 return toRef(exec, toJS(exec, static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()), wtfByteArray.get())); 737 } 738 739 if (QMetaType::typeFlags(type).testFlag(QMetaType::PointerToQObject)) { 740 QObject* obj = variant.value<QObject*>(); 741 if (!obj) 742 return JSValueMakeNull(context); 743 ExecState* exec = toJS(context); 744 APIEntryShim entryShim(exec); 745 return toRef(exec, QtInstance::getQtInstance(obj, root, QtInstance::QtOwnership)->createRuntimeObject(exec)); 746 } 747 748 if (QtPixmapRuntime::canHandle(static_cast<QMetaType::Type>(variant.type()))) 749 return QtPixmapRuntime::toJS(context, variant, exception); 750 751 if (customRuntimeConversions()->contains(type)) { 752 if (!root->globalObject()->inherits(&JSDOMWindow::s_info)) 753 return JSValueMakeUndefined(context); 754 755 Document* document = (static_cast<JSDOMWindow*>(root->globalObject()))->impl()->document(); 756 if (!document) 757 return JSValueMakeUndefined(context); 758 ExecState* exec = toJS(context); 759 APIEntryShim entryShim(exec); 760 return toRef(exec, customRuntimeConversions()->value(type).toJSValueFunc(exec, toJSDOMGlobalObject(document, exec), variant)); 761 } 762 763 if (type == QMetaType::QVariantMap) { 764 // create a new object, and stuff properties into it 765 JSObjectRef ret = JSObjectMake(context, 0, 0); 766 QVariantMap map = variant.value<QVariantMap>(); 767 QVariantMap::const_iterator i = map.constBegin(); 768 while (i != map.constEnd()) { 769 QString s = i.key(); 770 JSStringRef propertyName = JSStringCreateWithCharacters(reinterpret_cast<const JSChar*>(s.constData()), s.length()); 771 JSValueRef propertyValue = convertQVariantToValue(context, root.get(), i.value(), /*ignored exception*/0); 772 if (propertyValue) 773 JSObjectSetProperty(context, ret, propertyName, propertyValue, kJSPropertyAttributeNone, /*ignored exception*/0); 774 JSStringRelease(propertyName); 775 ++i; 776 } 777 778 return ret; 779 } 780 781 // List types 782 if (type == QMetaType::QVariantList) { 783 // ### TODO: Could use special array class that lazily converts. 784 // See https://bugs.webkit.org/show_bug.cgi?id=94691 785 QVariantList vl = variant.toList(); 786 JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception); 787 if (exception && *exception) 788 return array; 789 for (int i = 0; i < vl.count(); ++i) { 790 JSValueRef property = convertQVariantToValue(context, root.get(), vl.at(i), /*ignored exception*/0); 791 if (property) 792 JSObjectSetPropertyAtIndex(context, array, i, property, /*ignored exception*/0); 793 } 794 return array; 795 } else if (type == QMetaType::QStringList) { 796 QStringList sl = variant.value<QStringList>(); 797 JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception); 798 for (int i = 0; i < sl.count(); ++i) { 799 const QString& s = sl.at(i); 800 JSStringRef jsString = JSStringCreateWithCharacters(reinterpret_cast<const JSChar*>(s.constData()), s.length()); 801 JSObjectSetPropertyAtIndex(context, array, i, JSValueMakeString(context, jsString), /*ignored exception*/0); 802 JSStringRelease(jsString); 803 } 804 return array; 805 } else if (type == static_cast<QMetaType::Type>(qMetaTypeId<QObjectList>())) { 806 QObjectList ol = variant.value<QObjectList>(); 807 JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception); 808 ExecState* exec = toJS(context); 809 APIEntryShim entryShim(exec); 810 for (int i = 0; i < ol.count(); ++i) { 811 JSValueRef jsObject = toRef(exec, QtInstance::getQtInstance(ol.at(i), root, QtInstance::QtOwnership)->createRuntimeObject(exec)); 812 JSObjectSetPropertyAtIndex(context, array, i, jsObject, /*ignored exception*/0); 813 } 814 return array; 815 } else if (type == static_cast<QMetaType::Type>(qMetaTypeId<QList<int> >())) { 816 QList<int> il = variant.value<QList<int> >(); 817 JSObjectRef array = JSObjectMakeArray(context, 0, 0, exception); 818 for (int i = 0; i < il.count(); ++i) 819 JSObjectSetPropertyAtIndex(context, array, i, JSValueMakeNumber(context, il.at(i)), /*ignored exception*/0); 820 return array; 821 } 822 823 if (type == (QMetaType::Type)qMetaTypeId<QVariant>()) { 824 QVariant real = variant.value<QVariant>(); 825 qConvDebug() << "real variant is:" << real; 826 return convertQVariantToValue(context, root.get(), real, exception); 827 } 828 829 qConvDebug() << "fallback path for" << variant << variant.userType(); 830 831 QString string = variant.toString(); 832 JSStringRef jsstring = JSStringCreateWithCharacters(reinterpret_cast<const JSChar*>(string.constData()), string.length()); 833 JSValueRef value = JSValueMakeString(context, jsstring); 834 JSStringRelease(jsstring); 835 return value; 836} 837 838// Type conversion metadata (from QtScript originally) 839class QtMethodMatchType 840{ 841public: 842 enum Kind { 843 Invalid, 844 Variant, 845 MetaType, 846 Unresolved, 847 MetaEnum 848 }; 849 850 851 QtMethodMatchType() 852 : m_kind(Invalid) { } 853 854 Kind kind() const 855 { return m_kind; } 856 857 QMetaType::Type typeId() const; 858 859 bool isValid() const 860 { return (m_kind != Invalid); } 861 862 bool isVariant() const 863 { return (m_kind == Variant); } 864 865 bool isMetaType() const 866 { return (m_kind == MetaType); } 867 868 bool isUnresolved() const 869 { return (m_kind == Unresolved); } 870 871 bool isMetaEnum() const 872 { return (m_kind == MetaEnum); } 873 874 QByteArray name() const; 875 876 int enumeratorIndex() const 877 { Q_ASSERT(isMetaEnum()); return m_typeId; } 878 879 static QtMethodMatchType variant() 880 { return QtMethodMatchType(Variant); } 881 882 static QtMethodMatchType metaType(int typeId, const QByteArray &name) 883 { return QtMethodMatchType(MetaType, typeId, name); } 884 885 static QtMethodMatchType metaEnum(int enumIndex, const QByteArray &name) 886 { return QtMethodMatchType(MetaEnum, enumIndex, name); } 887 888 static QtMethodMatchType unresolved(const QByteArray &name) 889 { return QtMethodMatchType(Unresolved, /*typeId=*/0, name); } 890 891private: 892 QtMethodMatchType(Kind kind, int typeId = 0, const QByteArray &name = QByteArray()) 893 : m_kind(kind), m_typeId(typeId), m_name(name) { } 894 895 Kind m_kind; 896 int m_typeId; 897 QByteArray m_name; 898}; 899 900QMetaType::Type QtMethodMatchType::typeId() const 901{ 902 if (isVariant()) 903 return (QMetaType::Type) qMetaTypeId<QVariant>(); 904 return (QMetaType::Type) (isMetaEnum() ? QMetaType::Int : m_typeId); 905} 906 907QByteArray QtMethodMatchType::name() const 908{ 909 if (!m_name.isEmpty()) 910 return m_name; 911 else if (m_kind == Variant) 912 return "QVariant"; 913 return QByteArray(); 914} 915 916struct QtMethodMatchData 917{ 918 int matchDistance; 919 int index; 920 QVector<QtMethodMatchType> types; 921 QVarLengthArray<QVariant, 10> args; 922 923 QtMethodMatchData(int dist, int idx, QVector<QtMethodMatchType> typs, 924 const QVarLengthArray<QVariant, 10> &as) 925 : matchDistance(dist), index(idx), types(typs), args(as) { } 926 QtMethodMatchData() 927 : index(-1) { } 928 929 bool isValid() const 930 { return (index != -1); } 931 932 int firstUnresolvedIndex() const 933 { 934 for (int i=0; i < types.count(); i++) { 935 if (types.at(i).isUnresolved()) 936 return i; 937 } 938 return -1; 939 } 940}; 941 942static int indexOfMetaEnum(const QMetaObject *meta, const QByteArray &str) 943{ 944 QByteArray scope; 945 QByteArray name; 946 int scopeIdx = str.indexOf("::"); 947 if (scopeIdx != -1) { 948 scope = str.left(scopeIdx); 949 name = str.mid(scopeIdx + 2); 950 } else { 951 name = str; 952 } 953 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) { 954 QMetaEnum m = meta->enumerator(i); 955 if ((m.name() == name)/* && (scope.isEmpty() || (m.scope() == scope))*/) 956 return i; 957 } 958 return -1; 959} 960 961// Helper function for resolving methods 962// Largely based on code in QtScript for compatibility reasons 963static int findMethodIndex(JSContextRef context, 964 const QMetaObject* meta, 965 const QByteArray& signature, 966 int argumentCount, 967 const JSValueRef arguments[], 968 bool allowPrivate, 969 QVarLengthArray<QVariant, 10> &vars, 970 void** vvars, 971 JSValueRef* exception) 972{ 973 QList<int> matchingIndices; 974 975 bool overloads = !signature.contains('('); 976 977 int count = meta->methodCount(); 978 for (int i = count - 1; i >= 0; --i) { 979 const QMetaMethod m = meta->method(i); 980 981 // Don't choose private methods 982 if (m.access() == QMetaMethod::Private && !allowPrivate) 983 continue; 984 985 // try and find all matching named methods 986 if (!overloads && m.methodSignature() == signature) 987 matchingIndices.append(i); 988 else if (overloads && m.name() == signature) 989 matchingIndices.append(i); 990 } 991 992 int chosenIndex = -1; 993 QVector<QtMethodMatchType> chosenTypes; 994 995 QVarLengthArray<QVariant, 10> args; 996 QVector<QtMethodMatchData> candidates; 997 QVector<QtMethodMatchData> unresolved; 998 QVector<int> tooFewArgs; 999 QVector<int> conversionFailed; 1000 1001 foreach(int index, matchingIndices) { 1002 QMetaMethod method = meta->method(index); 1003 1004 QVector<QtMethodMatchType> types; 1005 bool unresolvedTypes = false; 1006 1007 // resolve return type 1008 QByteArray returnTypeName = method.typeName(); 1009 int rtype = method.returnType(); 1010 if (rtype == QMetaType::UnknownType) { 1011 if (returnTypeName.endsWith('*')) { 1012 types.append(QtMethodMatchType::metaType(QMetaType::VoidStar, returnTypeName)); 1013 } else { 1014 int enumIndex = indexOfMetaEnum(meta, returnTypeName); 1015 if (enumIndex != -1) 1016 types.append(QtMethodMatchType::metaEnum(enumIndex, returnTypeName)); 1017 else { 1018 unresolvedTypes = true; 1019 types.append(QtMethodMatchType::unresolved(returnTypeName)); 1020 } 1021 } 1022 } else { 1023 if (rtype == QMetaType::QVariant) 1024 types.append(QtMethodMatchType::variant()); 1025 else 1026 types.append(QtMethodMatchType::metaType(rtype, returnTypeName)); 1027 } 1028 1029 // resolve argument types 1030 QList<QByteArray> parameterTypeNames = method.parameterTypes(); 1031 for (int i = 0; i < parameterTypeNames.count(); ++i) { 1032 QByteArray argTypeName = parameterTypeNames.at(i); 1033 int atype = method.parameterType(i); 1034 if (atype == QMetaType::UnknownType) { 1035 int enumIndex = indexOfMetaEnum(meta, argTypeName); 1036 if (enumIndex != -1) 1037 types.append(QtMethodMatchType::metaEnum(enumIndex, argTypeName)); 1038 else { 1039 unresolvedTypes = true; 1040 types.append(QtMethodMatchType::unresolved(argTypeName)); 1041 } 1042 } else { 1043 if (atype == QMetaType::QVariant) 1044 types.append(QtMethodMatchType::variant()); 1045 else 1046 types.append(QtMethodMatchType::metaType(atype, argTypeName)); 1047 } 1048 } 1049 1050 // If the native method requires more arguments than what was passed from JavaScript 1051 if (argumentCount + 1 < static_cast<unsigned>(types.count())) { 1052 qMatchDebug() << "Match:too few args for" << method.methodSignature(); 1053 tooFewArgs.append(index); 1054 continue; 1055 } 1056 1057 if (unresolvedTypes) { 1058 qMatchDebug() << "Match:unresolved arg types for" << method.methodSignature(); 1059 // remember it so we can give an error message later, if necessary 1060 unresolved.append(QtMethodMatchData(/*matchDistance=*/INT_MAX, index, 1061 types, QVarLengthArray<QVariant, 10>())); 1062 continue; 1063 } 1064 1065 // Now convert arguments 1066 if (args.count() != types.count()) 1067 args.resize(types.count()); 1068 1069 QtMethodMatchType retType = types[0]; 1070 if (retType.typeId() != QMetaType::Void) 1071 args[0] = QVariant(retType.typeId(), (void *)0); // the return value 1072 1073 bool converted = true; 1074 int matchDistance = 0; 1075 for (unsigned i = 0; converted && i + 1 < static_cast<unsigned>(types.count()); ++i) { 1076 JSValueRef arg = i < argumentCount ? arguments[i] : JSValueMakeUndefined(context); 1077 1078 int argdistance = -1; 1079 QVariant v = convertValueToQVariant(context, arg, types.at(i+1).typeId(), &argdistance, exception); 1080 if (argdistance >= 0) { 1081 matchDistance += argdistance; 1082 args[i+1] = v; 1083 } else { 1084 qMatchDebug() << "failed to convert argument " << i << "type" << types.at(i+1).typeId() << QMetaType::typeName(types.at(i+1).typeId()); 1085 converted = false; 1086 } 1087 } 1088 1089 qMatchDebug() << "Match: " << method.methodSignature() << (converted ? "converted":"failed to convert") << "distance " << matchDistance; 1090 1091 if (converted) { 1092 if ((argumentCount + 1 == static_cast<unsigned>(types.count())) 1093 && (matchDistance == 0)) { 1094 // perfect match, use this one 1095 chosenIndex = index; 1096 chosenTypes = types; 1097 break; 1098 } 1099 QtMethodMatchData currentMatch(matchDistance, index, types, args); 1100 if (candidates.isEmpty()) 1101 candidates.append(currentMatch); 1102 else { 1103 QtMethodMatchData bestMatchSoFar = candidates.at(0); 1104 if ((args.count() > bestMatchSoFar.args.count()) 1105 || ((args.count() == bestMatchSoFar.args.count()) 1106 && (matchDistance <= bestMatchSoFar.matchDistance))) 1107 candidates.prepend(currentMatch); 1108 else 1109 candidates.append(currentMatch); 1110 } 1111 } else { 1112 conversionFailed.append(index); 1113 } 1114 1115 if (!overloads) 1116 break; 1117 } 1118 1119 if (chosenIndex == -1 && candidates.count() == 0) { 1120 // No valid functions at all - format an error message 1121 if (!conversionFailed.isEmpty()) { 1122 QString message = QString::fromLatin1("incompatible type of argument(s) in call to %0(); candidates were\n") 1123 .arg(QString::fromLatin1(signature)); 1124 for (int i = 0; i < conversionFailed.size(); ++i) { 1125 if (i > 0) 1126 message += QLatin1String("\n"); 1127 QMetaMethod mtd = meta->method(conversionFailed.at(i)); 1128 message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.methodSignature())); 1129 } 1130 setException(context, exception, message); 1131 } else if (!unresolved.isEmpty()) { 1132 QtMethodMatchData argsInstance = unresolved.first(); 1133 int unresolvedIndex = argsInstance.firstUnresolvedIndex(); 1134 Q_ASSERT(unresolvedIndex != -1); 1135 QtMethodMatchType unresolvedType = argsInstance.types.at(unresolvedIndex); 1136 QString message = QString::fromLatin1("cannot call %0(): unknown type `%1'") 1137 .arg(QString::fromLatin1(signature)) 1138 .arg(QLatin1String(unresolvedType.name())); 1139 setException(context, exception, message); 1140 } else { 1141 QString message = QString::fromLatin1("too few arguments in call to %0(); candidates are\n") 1142 .arg(QString::fromLatin1(signature)); 1143 for (int i = 0; i < tooFewArgs.size(); ++i) { 1144 if (i > 0) 1145 message += QLatin1String("\n"); 1146 QMetaMethod mtd = meta->method(tooFewArgs.at(i)); 1147 message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.methodSignature())); 1148 } 1149 setException(context, exception, message); 1150 } 1151 } 1152 1153 if (chosenIndex == -1 && candidates.count() > 0) { 1154 QtMethodMatchData bestMatch = candidates.at(0); 1155 if ((candidates.size() > 1) 1156 && (bestMatch.args.count() == candidates.at(1).args.count()) 1157 && (bestMatch.matchDistance == candidates.at(1).matchDistance)) { 1158 // ambiguous call 1159 QString message = QString::fromLatin1("ambiguous call of overloaded function %0(); candidates were\n") 1160 .arg(QLatin1String(signature)); 1161 for (int i = 0; i < candidates.size(); ++i) { 1162 // Only candidate for overload if argument count and match distance is same as best match 1163 if (candidates.at(i).args.count() == bestMatch.args.count() 1164 || candidates.at(i).matchDistance == bestMatch.matchDistance) { 1165 if (i > 0) 1166 message += QLatin1String("\n"); 1167 QMetaMethod mtd = meta->method(candidates.at(i).index); 1168 message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.methodSignature())); 1169 } 1170 } 1171 setException(context, exception, message); 1172 } else { 1173 chosenIndex = bestMatch.index; 1174 chosenTypes = bestMatch.types; 1175 args = bestMatch.args; 1176 } 1177 } 1178 1179 if (chosenIndex != -1) { 1180 /* Copy the stuff over */ 1181 int i; 1182 vars.resize(args.count()); 1183 for (i=0; i < args.count(); i++) { 1184 vars[i] = args[i]; 1185 if (chosenTypes[i].isVariant()) 1186 vvars[i] = &vars[i]; 1187 else 1188 vvars[i] = vars[i].data(); 1189 } 1190 } 1191 1192 return chosenIndex; 1193} 1194 1195// Signals are not fuzzy matched as much as methods 1196static int findSignalIndex(const QMetaObject* meta, int initialIndex, QByteArray signature) 1197{ 1198 int index = initialIndex; 1199 QMetaMethod method = meta->method(index); 1200 bool overloads = !signature.contains('('); 1201 if (overloads && (method.attributes() & QMetaMethod::Cloned)) { 1202 // find the most general method 1203 do { 1204 method = meta->method(--index); 1205 } while (method.attributes() & QMetaMethod::Cloned); 1206 } 1207 return index; 1208} 1209 1210static JSClassRef prototypeForSignalsAndSlots() 1211{ 1212 static JSClassDefinition classDef = { 1213 0, kJSClassAttributeNoAutomaticPrototype, 0, 0, 0, 0, 1214 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 1215 }; 1216 static JSClassRef cls = JSClassCreate(&classDef); 1217 return cls; 1218} 1219 1220QtRuntimeMethod::QtRuntimeMethod(JSContextRef ctx, QObject* object, const QByteArray& identifier, int index, int flags, QtInstance* instance) 1221 : m_object(object) 1222 , m_identifier(identifier) 1223 , m_index(index) 1224 , m_flags(flags) 1225 , m_instance(instance) 1226{ 1227} 1228 1229QtRuntimeMethod::~QtRuntimeMethod() 1230{ 1231} 1232 1233JSValueRef QtRuntimeMethod::call(JSContextRef context, JSObjectRef function, JSObjectRef /*thisObject*/, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 1234{ 1235 QtRuntimeMethod* d = toRuntimeMethod(context, function); 1236 if (!d) { 1237 setException(context, exception, QStringLiteral("cannot call function of deleted runtime method")); 1238 return JSValueMakeUndefined(context); 1239 } 1240 QObject* obj = d->m_object; 1241 1242 if (!obj) { 1243 setException(context, exception, QStringLiteral("cannot call function of deleted QObject")); 1244 return JSValueMakeUndefined(context); 1245 } 1246 1247 // Allow for maximum of 10 arguments and size stack arrays accordingly. 1248 if (argumentCount > 10) 1249 return JSValueMakeUndefined(context); 1250 1251 QVarLengthArray<QVariant, 10> vargs; 1252 void* qargs[11]; 1253 const QMetaObject* metaObject = obj->metaObject(); 1254 1255 int methodIndex = findMethodIndex(context, metaObject, d->m_identifier, argumentCount, arguments, 1256 (d->m_flags & AllowPrivate), vargs, (void **)qargs, exception); 1257 1258 if (QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, methodIndex, qargs) >= 0) 1259 return JSValueMakeUndefined(context); 1260 1261 if (vargs.size() > 0 && metaObject->method(methodIndex).returnType() != QMetaType::Void) 1262 return convertQVariantToValue(context, d->m_instance->rootObject(), vargs[0], exception); 1263 1264 return JSValueMakeUndefined(context); 1265} 1266 1267JSValueRef QtRuntimeMethod::connect(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 1268{ 1269 return connectOrDisconnect(context, function, thisObject, argumentCount, arguments, exception, true); 1270} 1271 1272JSValueRef QtRuntimeMethod::disconnect(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 1273{ 1274 return connectOrDisconnect(context, function, thisObject, argumentCount, arguments, exception, false); 1275} 1276 1277JSObjectRef QtRuntimeMethod::jsObjectRef(JSContextRef context, JSValueRef* exception) 1278{ 1279 if (m_jsObject) 1280 return toRef(m_jsObject.get()); 1281 1282 static JSStringRef connectStr = JSStringCreateWithUTF8CString("connect"); 1283 static JSStringRef disconnectStr = JSStringCreateWithUTF8CString("disconnect"); 1284 JSRetainPtr<JSStringRef> actualNameStr(Adopt, JSStringCreateWithUTF8CString(m_identifier.constData())); 1285 1286 JSObjectRef object = JSObjectMakeFunctionWithCallback(context, actualNameStr.get(), call); 1287 1288 JSObjectRef generalFunctionProto = JSValueToObject(context, JSObjectGetPrototype(context, object), 0); 1289 JSObjectRef runtimeMethodProto = JSObjectMake(context, prototypeForSignalsAndSlots(), this); 1290 JSObjectSetPrototype(context, runtimeMethodProto, generalFunctionProto); 1291 1292 JSObjectSetPrototype(context, object, runtimeMethodProto); 1293 1294 JSObjectRef connectFunction = JSObjectMakeFunctionWithCallback(context, connectStr, connect); 1295 JSObjectSetPrototype(context, connectFunction, runtimeMethodProto); 1296 1297 JSObjectRef disconnectFunction = JSObjectMakeFunctionWithCallback(context, disconnectStr, disconnect); 1298 JSObjectSetPrototype(context, disconnectFunction, runtimeMethodProto); 1299 1300 const JSPropertyAttributes attributes = kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete; 1301 JSObjectSetProperty(context, object, connectStr, connectFunction, attributes, exception); 1302 JSObjectSetProperty(context, object, disconnectStr, disconnectFunction, attributes, exception); 1303 1304 m_jsObject = PassWeak<JSObject>(toJS(object)); 1305 1306 return object; 1307} 1308 1309QtRuntimeMethod* QtRuntimeMethod::toRuntimeMethod(JSContextRef context, JSObjectRef object) 1310{ 1311 JSObjectRef proto = JSValueToObject(context, JSObjectGetPrototype(context, object), 0); 1312 if (!proto) 1313 return 0; 1314 if (!JSValueIsObjectOfClass(context, proto, prototypeForSignalsAndSlots())) 1315 return 0; 1316 return static_cast<QtRuntimeMethod*>(JSObjectGetPrivate(proto)); 1317} 1318 1319JSValueRef QtRuntimeMethod::connectOrDisconnect(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception, bool connect) 1320{ 1321 QtRuntimeMethod* d = toRuntimeMethod(context, thisObject); 1322 if (!d) 1323 d = toRuntimeMethod(context, function); 1324 if (!d) { 1325 QString errorStr = QStringLiteral("QtMetaMethod.%1: Cannot connect to/from deleted QObject").arg(connect ? QStringLiteral("connect") : QStringLiteral("disconnect")); 1326 setException(context, exception, errorStr); 1327 return JSValueMakeUndefined(context); 1328 } 1329 1330 QString functionName = connect ? QStringLiteral("connect") : QStringLiteral("disconnect"); 1331 1332 if (!argumentCount) { 1333 QString errorStr = QStringLiteral("QtMetaMethod.%1: no arguments given").arg(connect ? QStringLiteral("connect") : QStringLiteral("disconnect")); 1334 setException(context, exception, errorStr); 1335 return JSValueMakeUndefined(context); 1336 } 1337 1338 if ((!(d->m_flags & QtRuntimeMethod::MethodIsSignal))) { 1339 setException(context, exception, QStringLiteral("QtMetaMethod.%3: %1::%2() is not a signal").arg(QString::fromUtf8(d->m_object.data()->metaObject()->className())).arg(QString::fromLatin1(d->m_identifier)).arg(functionName)); 1340 return JSValueMakeUndefined(context); 1341 } 1342 1343 QObject* sender = d->m_object.data(); 1344 1345 if (!sender) { 1346 setException(context, exception, QStringLiteral("cannot call function of deleted QObject")); 1347 return JSValueMakeUndefined(context); 1348 } 1349 1350 int signalIndex = findSignalIndex(sender->metaObject(), d->m_index, d->m_identifier); 1351 1352 JSObjectRef targetObject = 0; 1353 JSObjectRef targetFunction = 0; 1354 1355 if (argumentCount == 1) { 1356 if (!JSValueIsObject(context, arguments[0])) { 1357 setException(context, exception, QStringLiteral("QtMetaMethod.%1: target is not a function").arg(functionName)); 1358 return JSValueMakeUndefined(context); 1359 } 1360 targetFunction = JSValueToObject(context, arguments[0], exception); 1361 1362 // object.signal.connect(someFunction); 1363 if (JSObjectIsFunction(context, targetFunction)) { 1364 // object.signal.connect(otherObject.slot); 1365 if (QtRuntimeMethod* targetMethod = toRuntimeMethod(context, targetFunction)) 1366 targetObject = toRef(QtInstance::getQtInstance(targetMethod->m_object.data(), d->m_instance->rootObject(), QtInstance::QtOwnership)->createRuntimeObject(toJS(context))); 1367 } else 1368 targetFunction = 0; 1369 } else { 1370 // object.signal.connect(object, someFunction); 1371 targetObject = JSValueToObject(context, arguments[0], exception); 1372 if (JSValueIsObject(context, arguments[1])) { 1373 JSObjectRef obj = JSValueToObject(context, arguments[1], exception); 1374 if (JSObjectIsFunction(context, obj)) 1375 targetFunction = obj; 1376 } 1377 if (!targetFunction) { 1378 // Maybe the second argument is a string 1379 JSValueRef conversionException = 0; 1380 JSRetainPtr<JSStringRef> functionName(Adopt, JSValueToStringCopy(context, arguments[1], &conversionException)); 1381 if (functionName && !conversionException) { 1382 JSValueRef functionProperty = JSObjectGetProperty(context, targetObject, functionName.get(), &conversionException); 1383 if (!conversionException && functionProperty && JSValueIsObject(context, functionProperty)) { 1384 targetFunction = JSValueToObject(context, functionProperty, 0); 1385 if (!JSObjectIsFunction(context, targetFunction)) 1386 targetFunction = 0; 1387 } 1388 } 1389 } 1390 } 1391 1392 // object.signal.connect(someObject); 1393 if (!targetFunction) { 1394 QString message = QStringLiteral("QtMetaMethod.%1: target is not a function"); 1395 if (connect) 1396 message = message.arg(QStringLiteral("connect")); 1397 else 1398 message = message.arg(QStringLiteral("disconnect")); 1399 setException(context, exception, message); 1400 return JSValueMakeUndefined(context); 1401 } 1402 1403 if (connect) { 1404 // to connect, we need: 1405 // target object [from ctor] 1406 // target signal index etc. [from ctor] 1407 // receiver function [from arguments] 1408 // receiver this object [from arguments] 1409 1410 QtConnectionObject* conn = new QtConnectionObject(context, QtInstance::getQtInstance(sender, d->m_instance->rootObject(), QtInstance::QtOwnership), signalIndex, targetObject, targetFunction); 1411 bool ok = QMetaObject::connect(sender, signalIndex, conn, conn->metaObject()->methodOffset()); 1412 if (!ok) { 1413 delete conn; 1414 QString msg = QString(QLatin1String("QtMetaMethod.connect: failed to connect to %1::%2()")) 1415 .arg(QLatin1String(sender->metaObject()->className())) 1416 .arg(QLatin1String(d->m_identifier)); 1417 setException(context, exception, msg); 1418 return JSValueMakeUndefined(context); 1419 } 1420 1421 // Store connection 1422 QtConnectionObject::connections.insert(sender, conn); 1423 1424 return JSValueMakeUndefined(context); 1425 } 1426 1427 // Now to find our previous connection object. 1428 QList<QtConnectionObject*> conns = QtConnectionObject::connections.values(sender); 1429 1430 foreach (QtConnectionObject* conn, conns) { 1431 // Is this the right connection? 1432 if (!conn->match(context, sender, signalIndex, targetObject, targetFunction)) 1433 continue; 1434 1435 // Yep, disconnect it 1436 QMetaObject::disconnect(sender, signalIndex, conn, conn->metaObject()->methodOffset()); 1437 delete conn; // this will also remove it from the map 1438 return JSValueMakeUndefined(context); 1439 } 1440 1441 QString msg = QStringLiteral("QtMetaMethod.disconnect: failed to disconnect from %1::%2()") 1442 .arg(QLatin1String(sender->metaObject()->className())) 1443 .arg(QLatin1String(d->m_identifier)); 1444 1445 setException(context, exception, msg); 1446 return JSValueMakeUndefined(context); 1447} 1448 1449// =============== 1450 1451QMultiMap<QObject*, QtConnectionObject*> QtConnectionObject::connections; 1452 1453QtConnectionObject::QtConnectionObject(JSContextRef context, PassRefPtr<QtInstance> senderInstance, int signalIndex, JSObjectRef receiver, JSObjectRef receiverFunction) 1454 : QObject(senderInstance->getObject()) 1455 , m_context(JSContextGetGlobalContext(context)) 1456 , m_rootObject(senderInstance->rootObject()) 1457 , m_signalIndex(signalIndex) 1458 , m_receiver(receiver) 1459 , m_receiverFunction(receiverFunction) 1460{ 1461 if (m_receiver) 1462 JSValueProtect(m_context, m_receiver); 1463 JSValueProtect(m_context, m_receiverFunction); 1464} 1465 1466QtConnectionObject::~QtConnectionObject() 1467{ 1468 connections.remove(parent(), this); 1469 1470 if (m_receiver) 1471 JSValueUnprotect(m_context, m_receiver); 1472 JSValueUnprotect(m_context, m_receiverFunction); 1473} 1474 1475// Begin moc-generated code -- modify with care! Check "HAND EDIT" parts 1476struct qt_meta_stringdata_QtConnectionObject_t { 1477 QByteArrayData data[3]; 1478 char stringdata[44]; 1479}; 1480#define QT_MOC_LITERAL(idx, ofs, len) { \ 1481 Q_REFCOUNT_INITIALIZE_STATIC, len, 0, 0, \ 1482 offsetof(qt_meta_stringdata_QtConnectionObject_t, stringdata) + ofs \ 1483 - idx * sizeof(QByteArrayData) \ 1484 } 1485static const qt_meta_stringdata_QtConnectionObject_t qt_meta_stringdata_QtConnectionObject = { 1486 { 1487QT_MOC_LITERAL(0, 0, 33), 1488QT_MOC_LITERAL(1, 34, 7), 1489QT_MOC_LITERAL(2, 42, 0) 1490 }, 1491 "JSC::Bindings::QtConnectionObject\0" 1492 "execute\0\0" 1493}; 1494#undef QT_MOC_LITERAL 1495 1496static const uint qt_meta_data_QtConnectionObject[] = { 1497 1498 // content: 1499 7, // revision 1500 0, // classname 1501 0, 0, // classinfo 1502 1, 14, // methods 1503 0, 0, // properties 1504 0, 0, // enums/sets 1505 0, 0, // constructors 1506 0, // flags 1507 0, // signalCount 1508 1509 // slots: name, argc, parameters, tag, flags 1510 1, 0, 19, 2, 0x0a, 1511 1512 // slots: parameters 1513 QMetaType::Void, 1514 1515 0 // eod 1516}; 1517 1518void QtConnectionObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) 1519{ 1520 if (_c == QMetaObject::InvokeMetaMethod) { 1521 Q_ASSERT(staticMetaObject.cast(_o)); 1522 QtConnectionObject *_t = static_cast<QtConnectionObject *>(_o); 1523 switch (_id) { 1524 case 0: _t->execute(_a); break; // HAND EDIT: add _a parameter 1525 default: ; 1526 } 1527 } 1528} 1529 1530const QMetaObject QtConnectionObject::staticMetaObject = { 1531 { &QObject::staticMetaObject, qt_meta_stringdata_QtConnectionObject.data, 1532 qt_meta_data_QtConnectionObject, qt_static_metacall, 0, 0 } 1533}; 1534 1535const QMetaObject *QtConnectionObject::metaObject() const 1536{ 1537 return &staticMetaObject; 1538} 1539 1540void *QtConnectionObject::qt_metacast(const char *_clname) 1541{ 1542 if (!_clname) return 0; 1543 if (!strcmp(_clname, qt_meta_stringdata_QtConnectionObject.stringdata)) 1544 return static_cast<void*>(const_cast<QtConnectionObject*>(this)); 1545 return QObject::qt_metacast(_clname); 1546} 1547 1548int QtConnectionObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a) 1549{ 1550 _id = QObject::qt_metacall(_c, _id, _a); 1551 if (_id < 0) 1552 return _id; 1553 if (_c == QMetaObject::InvokeMetaMethod) { 1554 if (_id < 1) 1555 qt_static_metacall(this, _c, _id, _a); 1556 _id -= 1; 1557 } 1558 return _id; 1559} 1560// End of moc-generated code 1561 1562void QtConnectionObject::execute(void** argv) 1563{ 1564 QObject* sender = parent(); 1565 const QMetaObject* meta = sender->metaObject(); 1566 const QMetaMethod method = meta->method(m_signalIndex); 1567 1568 JSValueRef* ignoredException = 0; 1569 JSRetainPtr<JSStringRef> lengthProperty(Adopt, JSStringCreateWithUTF8CString("length")); 1570 int receiverLength = int(JSValueToNumber(m_context, JSObjectGetProperty(m_context, m_receiverFunction, lengthProperty.get(), ignoredException), ignoredException)); 1571 int argc = qMax(method.parameterCount(), receiverLength); 1572 WTF::Vector<JSValueRef> args(argc); 1573 1574 for (int i = 0; i < argc; i++) { 1575 int argType = method.parameterType(i); 1576 args[i] = convertQVariantToValue(m_context, m_rootObject, QVariant(argType, argv[i+1]), ignoredException); 1577 } 1578 1579 JSObjectCallAsFunction(m_context, m_receiverFunction, m_receiver, argc, args.data(), 0); 1580} 1581 1582bool QtConnectionObject::match(JSContextRef context, QObject* sender, int signalIndex, JSObjectRef receiver, JSObjectRef receiverFunction) 1583{ 1584 if (sender != parent() || signalIndex != m_signalIndex) 1585 return false; 1586 JSValueRef* ignoredException = 0; 1587 const bool receiverMatch = (!receiver && !m_receiver) || (receiver && m_receiver && JSValueIsEqual(context, receiver, m_receiver, ignoredException)); 1588 return receiverMatch && JSValueIsEqual(context, receiverFunction, m_receiverFunction, ignoredException); 1589} 1590 1591} } 1592