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: 71 inline QWKNoDebug(){} 72 inline ~QWKNoDebug(){} 73 74 template<typename 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) 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 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 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 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 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 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 921 QtRuntimeMethod::~QtRuntimeMethod() 922 { 923 QW_D(QtRuntimeMethod); 924 d->m_instance->removeCachedMethod(this); 925 delete d_ptr; 926 } 927 928 // =============== 929 930 QtRuntimeMethodData::~QtRuntimeMethodData() 931 { 932 } 933 934 QtRuntimeMetaMethodData::~QtRuntimeMetaMethodData() 935 { 936 937 } 938 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 959 QtMethodMatchType() 960 : m_kind(Invalid) { } 961 962 Kind kind() const 963 { return m_kind; } 964 965 QMetaType::Type typeId() const; 966 967 bool isValid() const 968 { return (m_kind != Invalid); } 969 970 bool isVariant() const 971 { return (m_kind == Variant); } 972 973 bool isMetaType() const 974 { return (m_kind == MetaType); } 975 976 bool isUnresolved() const 977 { return (m_kind == Unresolved); } 978 979 bool isMetaEnum() const 980 { return (m_kind == MetaEnum); } 981 982 QByteArray name() const; 983 984 int enumeratorIndex() const 985 { Q_ASSERT(isMetaEnum()); return m_typeId; } 986 987 static QtMethodMatchType variant() 988 { return QtMethodMatchType(Variant); } 989 990 static QtMethodMatchType metaType(int typeId, const QByteArray &name) 991 { return QtMethodMatchType(MetaType, typeId, name); } 992 993 static QtMethodMatchType metaEnum(int enumIndex, const QByteArray &name) 994 { return QtMethodMatchType(MetaEnum, enumIndex, name); } 995 996 static QtMethodMatchType unresolved(const QByteArray &name) 997 { return QtMethodMatchType(Unresolved, /*typeId=*/0, name); } 998 999 private: 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 1008 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 1015 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 1031 QtMethodMatchData(int dist, int idx, QVector<QtMethodMatchType> typs, 1032 const QVarLengthArray<QVariant, 10> &as) 1033 : matchDistance(dist), index(idx), types(typs), args(as) { } 1034 QtMethodMatchData() 1035 : index(-1) { } 1036 1037 bool isValid() const 1038 { return (index != -1); } 1039 1040 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 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 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 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 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 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 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 1381 CallType QtRuntimeMetaMethod::getCallData(CallData& callData) 1382 { 1383 callData.native.function = call; 1384 return CallTypeHost; 1385 } 1386 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 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 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 1440 JSValue QtRuntimeMetaMethod::lengthGetter(ExecState* exec, const Identifier&, const PropertySlot&) 1441 { 1442 // QtScript always returns 0 1443 return jsNumber(exec, 0); 1444 } 1445 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 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 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 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 1610 CallType QtRuntimeConnectionMethod::getCallData(CallData& callData) 1611 { 1612 callData.native.function = call; 1613 return CallTypeHost; 1614 } 1615 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 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 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 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 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 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 1696 const QMetaObject *QtConnectionObject::metaObject() const 1697 { 1698 return &staticMetaObject; 1699 } 1700 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 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 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 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 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 1803 template <typename T> QtArray<T>::~QtArray () 1804 { 1805 } 1806 1807 template <typename T> RootObject* QtArray<T>::rootObject() const 1808 { 1809 return m_rootObject && m_rootObject->isValid() ? m_rootObject.get() : 0; 1810 } 1811 1812 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 1826 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