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