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 Library 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 Library General Public License for more details. 13 14 You should have received a copy of the GNU Library General Public License 15 along with this library; see the file COPYING.LIB. If not, write to 16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 Boston, MA 02110-1301, USA. 18 */ 19 20 #ifndef qscriptvalue_p_h 21 #define qscriptvalue_p_h 22 23 #include "qscriptconverter_p.h" 24 #include "qscriptengine_p.h" 25 #include "qscriptvalue.h" 26 #include <JavaScriptCore/JavaScript.h> 27 #include <JavaScriptCore/JSRetainPtr.h> 28 #include <JSObjectRefPrivate.h> 29 #include <QtCore/qdatetime.h> 30 #include <QtCore/qmath.h> 31 #include <QtCore/qnumeric.h> 32 #include <QtCore/qshareddata.h> 33 #include <QtCore/qvarlengtharray.h> 34 35 class QScriptEngine; 36 class QScriptValue; 37 38 /* 39 \internal 40 \class QScriptValuePrivate 41 42 Implementation of QScriptValue. 43 The implementation is based on a state machine. The states names are included in 44 QScriptValuePrivate::State. Each method should check for the current state and then perform a 45 correct action. 46 47 State: 48 Invalid -> QSVP is invalid, no assumptions should be made about class members (apart from m_value). 49 CString -> QSVP is created from QString or const char* and no JSC engine has been associated yet. 50 Current value is kept in m_string, 51 CNumber -> QSVP is created from int, uint, double... and no JSC engine has been bind yet. Current 52 value is kept in m_number 53 CBool -> QSVP is created from bool and no JSC engine has been associated yet. Current value is kept 54 in m_bool 55 CNull -> QSVP is null, but a JSC engine hasn't been associated yet. 56 CUndefined -> QSVP is undefined, but a JSC engine hasn't been associated yet. 57 JSValue -> QSVP is associated with engine, but there is no information about real type, the state 58 have really short live cycle. Normally it is created as a function call result. 59 JSPrimitive -> QSVP is associated with engine, and it is sure that it isn't a JavaScript object. 60 JSObject -> QSVP is associated with engine, and it is sure that it is a JavaScript object. 61 62 Each state keep all necessary information to invoke all methods, if not it should be changed to 63 a proper state. Changed state shouldn't be reverted. 64 65 The QScriptValuePrivate use the JSC C API directly. The QSVP type is equal to combination of 66 the JSValueRef and the JSObjectRef, and it could be automatically casted to these types by cast 67 operators. 68 */ 69 70 class QScriptValuePrivate : public QSharedData { 71 public: 72 inline static QScriptValuePrivate* get(const QScriptValue& q); 73 inline static QScriptValue get(const QScriptValuePrivate* d); 74 inline static QScriptValue get(QScriptValuePrivate* d); 75 76 inline ~QScriptValuePrivate(); 77 78 inline QScriptValuePrivate(); 79 inline QScriptValuePrivate(const QString& string); 80 inline QScriptValuePrivate(bool value); 81 inline QScriptValuePrivate(int number); 82 inline QScriptValuePrivate(uint number); 83 inline QScriptValuePrivate(qsreal number); 84 inline QScriptValuePrivate(QScriptValue::SpecialValue value); 85 86 inline QScriptValuePrivate(const QScriptEnginePrivate* engine, bool value); 87 inline QScriptValuePrivate(const QScriptEnginePrivate* engine, int value); 88 inline QScriptValuePrivate(const QScriptEnginePrivate* engine, uint value); 89 inline QScriptValuePrivate(const QScriptEnginePrivate* engine, qsreal value); 90 inline QScriptValuePrivate(const QScriptEnginePrivate* engine, const QString& value); 91 inline QScriptValuePrivate(const QScriptEnginePrivate* engine, QScriptValue::SpecialValue value); 92 93 inline QScriptValuePrivate(const QScriptEnginePrivate* engine, JSValueRef value); 94 inline QScriptValuePrivate(const QScriptEnginePrivate* engine, JSObjectRef object); 95 96 inline bool isValid() const; 97 inline bool isBool(); 98 inline bool isNumber(); 99 inline bool isNull(); 100 inline bool isString(); 101 inline bool isUndefined(); 102 inline bool isError(); 103 inline bool isObject(); 104 inline bool isFunction(); 105 inline bool isArray(); 106 inline bool isDate(); 107 108 inline QString toString() const; 109 inline qsreal toNumber() const; 110 inline bool toBool() const; 111 inline qsreal toInteger() const; 112 inline qint32 toInt32() const; 113 inline quint32 toUInt32() const; 114 inline quint16 toUInt16() const; 115 116 inline QScriptValuePrivate* toObject(QScriptEnginePrivate* engine); 117 inline QScriptValuePrivate* toObject(); 118 inline QDateTime toDateTime(); 119 inline QScriptValuePrivate* prototype(); 120 inline void setPrototype(QScriptValuePrivate* prototype); 121 122 inline bool equals(QScriptValuePrivate* other); 123 inline bool strictlyEquals(QScriptValuePrivate* other); 124 inline bool instanceOf(QScriptValuePrivate* other); 125 inline bool assignEngine(QScriptEnginePrivate* engine); 126 127 inline QScriptValuePrivate* property(const QString& name, const QScriptValue::ResolveFlags& mode); 128 inline QScriptValuePrivate* property(const QScriptStringPrivate* name, const QScriptValue::ResolveFlags& mode); 129 inline QScriptValuePrivate* property(quint32 arrayIndex, const QScriptValue::ResolveFlags& mode); 130 inline JSValueRef property(quint32 property, JSValueRef* exception); 131 inline JSValueRef property(JSStringRef property, JSValueRef* exception); 132 inline bool hasOwnProperty(quint32 property); 133 inline bool hasOwnProperty(JSStringRef property); 134 template<typename T> 135 inline QScriptValuePrivate* property(T name, const QScriptValue::ResolveFlags& mode); 136 137 inline void setProperty(const QString& name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags); 138 inline void setProperty(const QScriptStringPrivate* name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags); 139 inline void setProperty(const quint32 indexArray, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags); 140 inline void setProperty(quint32 property, JSValueRef value, JSPropertyAttributes flags, JSValueRef* exception); 141 inline void setProperty(JSStringRef property, JSValueRef value, JSPropertyAttributes flags, JSValueRef* exception); 142 inline void deleteProperty(quint32 property, JSValueRef* exception); 143 inline void deleteProperty(JSStringRef property, JSValueRef* exception); 144 template<typename T> 145 inline void setProperty(T name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags); 146 147 QScriptValue::PropertyFlags propertyFlags(const QString& name, const QScriptValue::ResolveFlags& mode); 148 QScriptValue::PropertyFlags propertyFlags(const QScriptStringPrivate* name, const QScriptValue::ResolveFlags& mode); 149 QScriptValue::PropertyFlags propertyFlags(const JSStringRef name, const QScriptValue::ResolveFlags& mode); 150 151 inline QScriptValuePrivate* call(const QScriptValuePrivate* , const QScriptValueList& args); 152 153 inline operator JSValueRef() const; 154 inline operator JSObjectRef() const; 155 156 inline QScriptEnginePrivate* engine() const; 157 158 private: 159 // Please, update class documentation when you change the enum. 160 enum State { 161 Invalid = 0, 162 CString = 0x1000, 163 CNumber, 164 CBool, 165 CNull, 166 CUndefined, 167 JSValue = 0x2000, // JS values are equal or higher then this value. 168 JSPrimitive, 169 JSObject 170 } m_state; 171 QScriptEnginePtr m_engine; 172 union Value 173 { 174 bool m_bool; 175 qsreal m_number; 176 JSValueRef m_value; 177 JSObjectRef m_object; 178 QString* m_string; 179 180 Value() : m_number(0) {} 181 Value(bool value) : m_bool(value) {} 182 Value(int number) : m_number(number) {} 183 Value(uint number) : m_number(number) {} 184 Value(qsreal number) : m_number(number) {} 185 Value(JSValueRef value) : m_value(value) {} 186 Value(JSObjectRef object) : m_object(object) {} 187 Value(QString* string) : m_string(string) {} 188 } u; 189 190 inline State refinedJSValue(); 191 192 inline bool isJSBased() const; 193 inline bool isNumberBased() const; 194 inline bool isStringBased() const; 195 }; 196 197 QScriptValuePrivate* QScriptValuePrivate::get(const QScriptValue& q) { return q.d_ptr.data(); } 198 199 QScriptValue QScriptValuePrivate::get(const QScriptValuePrivate* d) 200 { 201 return QScriptValue(const_cast<QScriptValuePrivate*>(d)); 202 } 203 204 QScriptValue QScriptValuePrivate::get(QScriptValuePrivate* d) 205 { 206 return QScriptValue(d); 207 } 208 209 QScriptValuePrivate::~QScriptValuePrivate() 210 { 211 if (isJSBased()) 212 JSValueUnprotect(*m_engine, u.m_value); 213 else if (isStringBased()) 214 delete u.m_string; 215 } 216 217 QScriptValuePrivate::QScriptValuePrivate() 218 : m_state(Invalid) 219 { 220 } 221 222 QScriptValuePrivate::QScriptValuePrivate(const QString& string) 223 : m_state(CString) 224 , u(new QString(string)) 225 { 226 } 227 228 QScriptValuePrivate::QScriptValuePrivate(bool value) 229 : m_state(CBool) 230 , u(value) 231 { 232 } 233 234 QScriptValuePrivate::QScriptValuePrivate(int number) 235 : m_state(CNumber) 236 , u(number) 237 { 238 } 239 240 QScriptValuePrivate::QScriptValuePrivate(uint number) 241 : m_state(CNumber) 242 , u(number) 243 { 244 } 245 246 QScriptValuePrivate::QScriptValuePrivate(qsreal number) 247 : m_state(CNumber) 248 , u(number) 249 { 250 } 251 252 QScriptValuePrivate::QScriptValuePrivate(QScriptValue::SpecialValue value) 253 : m_state(value == QScriptValue::NullValue ? CNull : CUndefined) 254 { 255 } 256 257 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, bool value) 258 : m_state(JSPrimitive) 259 , m_engine(const_cast<QScriptEnginePrivate*>(engine)) 260 , u(engine->makeJSValue(value)) 261 { 262 Q_ASSERT(engine); 263 JSValueProtect(*m_engine, u.m_value); 264 } 265 266 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, int value) 267 : m_state(JSPrimitive) 268 , m_engine(const_cast<QScriptEnginePrivate*>(engine)) 269 , u(m_engine->makeJSValue(value)) 270 { 271 Q_ASSERT(engine); 272 JSValueProtect(*m_engine, u.m_value); 273 } 274 275 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, uint value) 276 : m_state(JSPrimitive) 277 , m_engine(const_cast<QScriptEnginePrivate*>(engine)) 278 , u(m_engine->makeJSValue(value)) 279 { 280 Q_ASSERT(engine); 281 JSValueProtect(*m_engine, u.m_value); 282 } 283 284 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, qsreal value) 285 : m_state(JSPrimitive) 286 , m_engine(const_cast<QScriptEnginePrivate*>(engine)) 287 , u(m_engine->makeJSValue(value)) 288 { 289 Q_ASSERT(engine); 290 JSValueProtect(*m_engine, u.m_value); 291 } 292 293 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, const QString& value) 294 : m_state(JSPrimitive) 295 , m_engine(const_cast<QScriptEnginePrivate*>(engine)) 296 , u(m_engine->makeJSValue(value)) 297 { 298 Q_ASSERT(engine); 299 JSValueProtect(*m_engine, u.m_value); 300 } 301 302 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, QScriptValue::SpecialValue value) 303 : m_state(JSPrimitive) 304 , m_engine(const_cast<QScriptEnginePrivate*>(engine)) 305 , u(m_engine->makeJSValue(value)) 306 { 307 Q_ASSERT(engine); 308 JSValueProtect(*m_engine, u.m_value); 309 } 310 311 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, JSValueRef value) 312 : m_state(JSValue) 313 , m_engine(const_cast<QScriptEnginePrivate*>(engine)) 314 , u(value) 315 { 316 Q_ASSERT(engine); 317 Q_ASSERT(value); 318 JSValueProtect(*m_engine, u.m_value); 319 } 320 321 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, JSObjectRef object) 322 : m_state(JSObject) 323 , m_engine(const_cast<QScriptEnginePrivate*>(engine)) 324 , u(object) 325 { 326 Q_ASSERT(engine); 327 Q_ASSERT(object); 328 JSValueProtect(*m_engine, object); 329 } 330 331 bool QScriptValuePrivate::isValid() const { return m_state != Invalid; } 332 333 bool QScriptValuePrivate::isBool() 334 { 335 switch (m_state) { 336 case CBool: 337 return true; 338 case JSValue: 339 if (refinedJSValue() != JSPrimitive) 340 return false; 341 // Fall-through. 342 case JSPrimitive: 343 return JSValueIsBoolean(*m_engine, *this); 344 default: 345 return false; 346 } 347 } 348 349 bool QScriptValuePrivate::isNumber() 350 { 351 switch (m_state) { 352 case CNumber: 353 return true; 354 case JSValue: 355 if (refinedJSValue() != JSPrimitive) 356 return false; 357 // Fall-through. 358 case JSPrimitive: 359 return JSValueIsNumber(*m_engine, *this); 360 default: 361 return false; 362 } 363 } 364 365 bool QScriptValuePrivate::isNull() 366 { 367 switch (m_state) { 368 case CNull: 369 return true; 370 case JSValue: 371 if (refinedJSValue() != JSPrimitive) 372 return false; 373 // Fall-through. 374 case JSPrimitive: 375 return JSValueIsNull(*m_engine, *this); 376 default: 377 return false; 378 } 379 } 380 381 bool QScriptValuePrivate::isString() 382 { 383 switch (m_state) { 384 case CString: 385 return true; 386 case JSValue: 387 if (refinedJSValue() != JSPrimitive) 388 return false; 389 // Fall-through. 390 case JSPrimitive: 391 return JSValueIsString(*m_engine, *this); 392 default: 393 return false; 394 } 395 } 396 397 bool QScriptValuePrivate::isUndefined() 398 { 399 switch (m_state) { 400 case CUndefined: 401 return true; 402 case JSValue: 403 if (refinedJSValue() != JSPrimitive) 404 return false; 405 // Fall-through. 406 case JSPrimitive: 407 return JSValueIsUndefined(*m_engine, *this); 408 default: 409 return false; 410 } 411 } 412 413 bool QScriptValuePrivate::isError() 414 { 415 switch (m_state) { 416 case JSValue: 417 if (refinedJSValue() != JSObject) 418 return false; 419 // Fall-through. 420 case JSObject: 421 return m_engine->isError(*this); 422 default: 423 return false; 424 } 425 } 426 427 bool QScriptValuePrivate::isObject() 428 { 429 switch (m_state) { 430 case JSValue: 431 return refinedJSValue() == JSObject; 432 case JSObject: 433 return true; 434 435 default: 436 return false; 437 } 438 } 439 440 bool QScriptValuePrivate::isFunction() 441 { 442 switch (m_state) { 443 case JSValue: 444 if (refinedJSValue() != JSObject) 445 return false; 446 // Fall-through. 447 case JSObject: 448 return JSObjectIsFunction(*m_engine, *this); 449 default: 450 return false; 451 } 452 } 453 454 bool QScriptValuePrivate::isArray() 455 { 456 switch (m_state) { 457 case JSValue: 458 if (refinedJSValue() != JSObject) 459 return false; 460 // Fall-through. 461 case JSObject: 462 return m_engine->isArray(*this); 463 default: 464 return false; 465 } 466 } 467 468 bool QScriptValuePrivate::isDate() 469 { 470 switch (m_state) { 471 case JSValue: 472 if (refinedJSValue() != JSObject) 473 return false; 474 // Fall-through. 475 case JSObject: 476 return m_engine->isDate(*this); 477 default: 478 return false; 479 } 480 } 481 482 QString QScriptValuePrivate::toString() const 483 { 484 switch (m_state) { 485 case Invalid: 486 return QString(); 487 case CBool: 488 return u.m_bool ? QString::fromLatin1("true") : QString::fromLatin1("false"); 489 case CString: 490 return *u.m_string; 491 case CNumber: 492 return QScriptConverter::toString(u.m_number); 493 case CNull: 494 return QString::fromLatin1("null"); 495 case CUndefined: 496 return QString::fromLatin1("undefined"); 497 case JSValue: 498 case JSPrimitive: 499 case JSObject: 500 JSValueRef exception = 0; 501 JSRetainPtr<JSStringRef> ptr(Adopt, JSValueToStringCopy(*m_engine, *this, &exception)); 502 m_engine->setException(exception); 503 return QScriptConverter::toString(ptr.get()); 504 } 505 506 Q_ASSERT_X(false, "toString()", "Not all states are included in the previous switch statement."); 507 return QString(); // Avoid compiler warning. 508 } 509 510 qsreal QScriptValuePrivate::toNumber() const 511 { 512 switch (m_state) { 513 case JSValue: 514 case JSPrimitive: 515 case JSObject: 516 { 517 JSValueRef exception = 0; 518 qsreal result = JSValueToNumber(*m_engine, *this, &exception); 519 m_engine->setException(exception); 520 return result; 521 } 522 case CNumber: 523 return u.m_number; 524 case CBool: 525 return u.m_bool ? 1 : 0; 526 case CNull: 527 case Invalid: 528 return 0; 529 case CUndefined: 530 return qQNaN(); 531 case CString: 532 bool ok; 533 qsreal result = u.m_string->toDouble(&ok); 534 if (ok) 535 return result; 536 result = u.m_string->toInt(&ok, 0); // Try other bases. 537 if (ok) 538 return result; 539 if (*u.m_string == "Infinity" || *u.m_string == "-Infinity") 540 return qInf(); 541 return u.m_string->length() ? qQNaN() : 0; 542 } 543 544 Q_ASSERT_X(false, "toNumber()", "Not all states are included in the previous switch statement."); 545 return 0; // Avoid compiler warning. 546 } 547 548 bool QScriptValuePrivate::toBool() const 549 { 550 switch (m_state) { 551 case JSValue: 552 case JSPrimitive: 553 return JSValueToBoolean(*m_engine, *this); 554 case JSObject: 555 return true; 556 case CNumber: 557 return !(qIsNaN(u.m_number) || !u.m_number); 558 case CBool: 559 return u.m_bool; 560 case Invalid: 561 case CNull: 562 case CUndefined: 563 return false; 564 case CString: 565 return u.m_string->length(); 566 } 567 568 Q_ASSERT_X(false, "toBool()", "Not all states are included in the previous switch statement."); 569 return false; // Avoid compiler warning. 570 } 571 572 qsreal QScriptValuePrivate::toInteger() const 573 { 574 qsreal result = toNumber(); 575 if (qIsNaN(result)) 576 return 0; 577 if (qIsInf(result)) 578 return result; 579 return (result > 0) ? qFloor(result) : -1 * qFloor(-result); 580 } 581 582 qint32 QScriptValuePrivate::toInt32() const 583 { 584 qsreal result = toInteger(); 585 // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but 586 // some of these operation are invoked in toInteger subcall. 587 if (qIsInf(result)) 588 return 0; 589 return result; 590 } 591 592 quint32 QScriptValuePrivate::toUInt32() const 593 { 594 qsreal result = toInteger(); 595 // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but 596 // some of these operation are invoked in toInteger subcall. 597 if (qIsInf(result)) 598 return 0; 599 return result; 600 } 601 602 quint16 QScriptValuePrivate::toUInt16() const 603 { 604 return toInt32(); 605 } 606 607 /*! 608 Creates a copy of this value and converts it to an object. If this value is an object 609 then pointer to this value will be returned. 610 \attention it should not happen but if this value is bounded to a different engine that the given, the first 611 one will be used. 612 \internal 613 */ 614 QScriptValuePrivate* QScriptValuePrivate::toObject(QScriptEnginePrivate* engine) 615 { 616 switch (m_state) { 617 case Invalid: 618 case CNull: 619 case CUndefined: 620 return new QScriptValuePrivate; 621 case CString: 622 { 623 // Exception can't occur here. 624 JSObjectRef object = JSValueToObject(*engine, engine->makeJSValue(*u.m_string), /* exception */ 0); 625 Q_ASSERT(object); 626 return new QScriptValuePrivate(engine, object); 627 } 628 case CNumber: 629 { 630 // Exception can't occur here. 631 JSObjectRef object = JSValueToObject(*engine, engine->makeJSValue(u.m_number), /* exception */ 0); 632 Q_ASSERT(object); 633 return new QScriptValuePrivate(engine, object); 634 } 635 case CBool: 636 { 637 // Exception can't occure here. 638 JSObjectRef object = JSValueToObject(*engine, engine->makeJSValue(u.m_bool), /* exception */ 0); 639 Q_ASSERT(object); 640 return new QScriptValuePrivate(engine, object); 641 } 642 case JSValue: 643 if (refinedJSValue() != JSPrimitive) 644 break; 645 // Fall-through. 646 case JSPrimitive: 647 { 648 if (engine != this->engine()) 649 qWarning("QScriptEngine::toObject: cannot convert value created in a different engine"); 650 JSValueRef exception = 0; 651 JSObjectRef object = JSValueToObject(*m_engine, *this, &exception); 652 if (object) 653 return new QScriptValuePrivate(m_engine.constData(), object); 654 else 655 m_engine->setException(exception, QScriptEnginePrivate::NotNullException); 656 657 } 658 return new QScriptValuePrivate; 659 case JSObject: 660 break; 661 } 662 663 if (engine != this->engine()) 664 qWarning("QScriptEngine::toObject: cannot convert value created in a different engine"); 665 Q_ASSERT(m_state == JSObject); 666 return this; 667 } 668 669 /*! 670 This method is created only for QScriptValue::toObject() purpose which is obsolete. 671 \internal 672 */ 673 QScriptValuePrivate* QScriptValuePrivate::toObject() 674 { 675 if (isJSBased()) 676 return toObject(m_engine.data()); 677 678 // Without an engine there is not much we can do. 679 return new QScriptValuePrivate; 680 } 681 682 QDateTime QScriptValuePrivate::toDateTime() 683 { 684 if (!isDate()) 685 return QDateTime(); 686 687 JSValueRef exception = 0; 688 qsreal t = JSValueToNumber(*m_engine, *this, &exception); 689 690 if (exception) { 691 m_engine->setException(exception, QScriptEnginePrivate::NotNullException); 692 return QDateTime(); 693 } 694 695 QDateTime result; 696 result.setMSecsSinceEpoch(qint64(t)); 697 return result; 698 } 699 700 inline QScriptValuePrivate* QScriptValuePrivate::prototype() 701 { 702 if (isObject()) { 703 JSValueRef prototype = JSObjectGetPrototype(*m_engine, *this); 704 if (JSValueIsNull(*m_engine, prototype)) 705 return new QScriptValuePrivate(engine(), prototype); 706 // The prototype could be either a null or a JSObject, so it is safe to cast the prototype 707 // to the JSObjectRef here. 708 return new QScriptValuePrivate(engine(), const_cast<JSObjectRef>(prototype)); 709 } 710 return new QScriptValuePrivate; 711 } 712 713 inline void QScriptValuePrivate::setPrototype(QScriptValuePrivate* prototype) 714 { 715 if (isObject() && prototype->isValid() && (prototype->isObject() || prototype->isNull())) { 716 if (engine() != prototype->engine()) { 717 qWarning("QScriptValue::setPrototype() failed: cannot set a prototype created in a different engine"); 718 return; 719 } 720 // FIXME: This could be replaced by a new, faster API 721 // look at https://bugs.webkit.org/show_bug.cgi?id=40060 722 JSObjectSetPrototype(*m_engine, *this, *prototype); 723 JSValueRef proto = JSObjectGetPrototype(*m_engine, *this); 724 if (!JSValueIsStrictEqual(*m_engine, proto, *prototype)) 725 qWarning("QScriptValue::setPrototype() failed: cyclic prototype value"); 726 } 727 } 728 729 bool QScriptValuePrivate::equals(QScriptValuePrivate* other) 730 { 731 if (!isValid()) 732 return !other->isValid(); 733 734 if (!other->isValid()) 735 return false; 736 737 if (!isJSBased() && !other->isJSBased()) { 738 switch (m_state) { 739 case CNull: 740 case CUndefined: 741 return other->isUndefined() || other->isNull(); 742 case CNumber: 743 switch (other->m_state) { 744 case CBool: 745 case CString: 746 return u.m_number == other->toNumber(); 747 case CNumber: 748 return u.m_number == other->u.m_number; 749 default: 750 return false; 751 } 752 case CBool: 753 switch (other->m_state) { 754 case CBool: 755 return u.m_bool == other->u.m_bool; 756 case CNumber: 757 return toNumber() == other->u.m_number; 758 case CString: 759 return toNumber() == other->toNumber(); 760 default: 761 return false; 762 } 763 case CString: 764 switch (other->m_state) { 765 case CBool: 766 return toNumber() == other->toNumber(); 767 case CNumber: 768 return toNumber() == other->u.m_number; 769 case CString: 770 return *u.m_string == *other->u.m_string; 771 default: 772 return false; 773 } 774 default: 775 Q_ASSERT_X(false, "equals()", "Not all states are included in the previous switch statement."); 776 } 777 } 778 779 if (isJSBased() && !other->isJSBased()) { 780 if (!other->assignEngine(engine())) { 781 qWarning("equals(): Cannot compare to a value created in a different engine"); 782 return false; 783 } 784 } else if (!isJSBased() && other->isJSBased()) { 785 if (!assignEngine(other->engine())) { 786 qWarning("equals(): Cannot compare to a value created in a different engine"); 787 return false; 788 } 789 } 790 791 JSValueRef exception = 0; 792 bool result = JSValueIsEqual(*m_engine, *this, *other, &exception); 793 m_engine->setException(exception); 794 return result; 795 } 796 797 bool QScriptValuePrivate::strictlyEquals(QScriptValuePrivate* other) 798 { 799 if (isJSBased()) { 800 // We can't compare these two values without binding to the same engine. 801 if (!other->isJSBased()) { 802 if (other->assignEngine(engine())) 803 return JSValueIsStrictEqual(*m_engine, *this, *other); 804 return false; 805 } 806 if (other->engine() != engine()) { 807 qWarning("strictlyEquals(): Cannot compare to a value created in a different engine"); 808 return false; 809 } 810 return JSValueIsStrictEqual(*m_engine, *this, *other); 811 } 812 if (isStringBased()) { 813 if (other->isStringBased()) 814 return *u.m_string == *(other->u.m_string); 815 if (other->isJSBased()) { 816 assignEngine(other->engine()); 817 return JSValueIsStrictEqual(*m_engine, *this, *other); 818 } 819 } 820 if (isNumberBased()) { 821 if (other->isNumberBased()) 822 return u.m_number == other->u.m_number; 823 if (other->isJSBased()) { 824 assignEngine(other->engine()); 825 return JSValueIsStrictEqual(*m_engine, *this, *other); 826 } 827 } 828 if (!isValid() && !other->isValid()) 829 return true; 830 831 return false; 832 } 833 834 inline bool QScriptValuePrivate::instanceOf(QScriptValuePrivate* other) 835 { 836 if (!isJSBased() || !other->isObject()) 837 return false; 838 JSValueRef exception = 0; 839 bool result = JSValueIsInstanceOfConstructor(*m_engine, *this, *other, &exception); 840 m_engine->setException(exception); 841 return result; 842 } 843 844 /*! 845 Tries to assign \a engine to this value. Returns true on success; otherwise returns false. 846 */ 847 bool QScriptValuePrivate::assignEngine(QScriptEnginePrivate* engine) 848 { 849 Q_ASSERT(engine); 850 JSValueRef value; 851 switch (m_state) { 852 case CBool: 853 value = engine->makeJSValue(u.m_bool); 854 break; 855 case CString: 856 value = engine->makeJSValue(*u.m_string); 857 delete u.m_string; 858 break; 859 case CNumber: 860 value = engine->makeJSValue(u.m_number); 861 break; 862 case CNull: 863 value = engine->makeJSValue(QScriptValue::NullValue); 864 break; 865 case CUndefined: 866 value = engine->makeJSValue(QScriptValue::UndefinedValue); 867 break; 868 default: 869 if (!isJSBased()) 870 Q_ASSERT_X(!isJSBased(), "assignEngine()", "Not all states are included in the previous switch statement."); 871 else 872 qWarning("JSValue can't be rassigned to an another engine."); 873 return false; 874 } 875 m_engine = engine; 876 m_state = JSPrimitive; 877 u.m_value = value; 878 JSValueProtect(*m_engine, value); 879 return true; 880 } 881 882 inline QScriptValuePrivate* QScriptValuePrivate::property(const QString& name, const QScriptValue::ResolveFlags& mode) 883 { 884 JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(name)); 885 return property<JSStringRef>(propertyName.get(), mode); 886 } 887 888 inline QScriptValuePrivate* QScriptValuePrivate::property(const QScriptStringPrivate* name, const QScriptValue::ResolveFlags& mode) 889 { 890 return property<JSStringRef>(*name, mode); 891 } 892 893 inline QScriptValuePrivate* QScriptValuePrivate::property(quint32 arrayIndex, const QScriptValue::ResolveFlags& mode) 894 { 895 return property<quint32>(arrayIndex, mode); 896 } 897 898 /*! 899 \internal 900 This method was created to unify access to the JSObjectGetPropertyAtIndex and the JSObjectGetProperty. 901 */ 902 inline JSValueRef QScriptValuePrivate::property(quint32 property, JSValueRef* exception) 903 { 904 return JSObjectGetPropertyAtIndex(*m_engine, *this, property, exception); 905 } 906 907 /*! 908 \internal 909 This method was created to unify access to the JSObjectGetPropertyAtIndex and the JSObjectGetProperty. 910 */ 911 inline JSValueRef QScriptValuePrivate::property(JSStringRef property, JSValueRef* exception) 912 { 913 return JSObjectGetProperty(*m_engine, *this, property, exception); 914 } 915 916 /*! 917 \internal 918 This method was created to unify acccess to hasOwnProperty, same function for an array index 919 and a property name access. 920 */ 921 inline bool QScriptValuePrivate::hasOwnProperty(quint32 property) 922 { 923 Q_ASSERT(isObject()); 924 // FIXME it could be faster, but JSC C API doesn't expose needed functionality. 925 JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(QString::number(property))); 926 return hasOwnProperty(propertyName.get()); 927 } 928 929 /*! 930 \internal 931 This method was created to unify acccess to hasOwnProperty, same function for an array index 932 and a property name access. 933 */ 934 inline bool QScriptValuePrivate::hasOwnProperty(JSStringRef property) 935 { 936 Q_ASSERT(isObject()); 937 return m_engine->objectHasOwnProperty(*this, property); 938 } 939 940 /*! 941 \internal 942 This function gets property of an object. 943 \arg propertyName could be type of quint32 (an array index) or JSStringRef (a property name). 944 */ 945 template<typename T> 946 inline QScriptValuePrivate* QScriptValuePrivate::property(T propertyName, const QScriptValue::ResolveFlags& mode) 947 { 948 if (!isObject()) 949 return new QScriptValuePrivate(); 950 951 if ((mode == QScriptValue::ResolveLocal) && (!hasOwnProperty(propertyName))) 952 return new QScriptValuePrivate(); 953 954 JSValueRef exception = 0; 955 JSValueRef value = property(propertyName, &exception); 956 957 if (exception) { 958 m_engine->setException(exception, QScriptEnginePrivate::NotNullException); 959 return new QScriptValuePrivate(engine(), exception); 960 } 961 if (JSValueIsUndefined(*m_engine, value)) 962 return new QScriptValuePrivate; 963 return new QScriptValuePrivate(engine(), value); 964 } 965 966 inline void QScriptValuePrivate::setProperty(const QString& name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags) 967 { 968 JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(name)); 969 setProperty<JSStringRef>(propertyName.get(), value, flags); 970 } 971 972 inline void QScriptValuePrivate::setProperty(quint32 arrayIndex, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags) 973 { 974 setProperty<quint32>(arrayIndex, value, flags); 975 } 976 977 inline void QScriptValuePrivate::setProperty(const QScriptStringPrivate* name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags) 978 { 979 setProperty<JSStringRef>(*name, value, flags); 980 } 981 982 /*! 983 \internal 984 This method was created to unify access to the JSObjectSetPropertyAtIndex and the JSObjectSetProperty. 985 */ 986 inline void QScriptValuePrivate::setProperty(quint32 property, JSValueRef value, JSPropertyAttributes flags, JSValueRef* exception) 987 { 988 Q_ASSERT(isObject()); 989 if (flags) { 990 // FIXME This could be better, but JSC C API doesn't expose needed functionality. It is 991 // not possible to create / modify a property attribute via an array index. 992 JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(QString::number(property))); 993 JSObjectSetProperty(*m_engine, *this, propertyName.get(), value, flags, exception); 994 return; 995 } 996 JSObjectSetPropertyAtIndex(*m_engine, *this, property, value, exception); 997 } 998 999 /*! 1000 \internal 1001 This method was created to unify access to the JSObjectSetPropertyAtIndex and the JSObjectSetProperty. 1002 */ 1003 inline void QScriptValuePrivate::setProperty(JSStringRef property, JSValueRef value, JSPropertyAttributes flags, JSValueRef* exception) 1004 { 1005 Q_ASSERT(isObject()); 1006 JSObjectSetProperty(*m_engine, *this, property, value, flags, exception); 1007 } 1008 1009 /*! 1010 \internal 1011 This method was created to unify access to the JSObjectDeleteProperty and a teoretical JSObjectDeletePropertyAtIndex 1012 which doesn't exist now. 1013 */ 1014 inline void QScriptValuePrivate::deleteProperty(quint32 property, JSValueRef* exception) 1015 { 1016 // FIXME It could be faster, we need a JSC C API for deleting array index properties. 1017 JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(QString::number(property))); 1018 JSObjectDeleteProperty(*m_engine, *this, propertyName.get(), exception); 1019 } 1020 1021 /*! 1022 \internal 1023 This method was created to unify access to the JSObjectDeleteProperty and a teoretical JSObjectDeletePropertyAtIndex. 1024 */ 1025 inline void QScriptValuePrivate::deleteProperty(JSStringRef property, JSValueRef* exception) 1026 { 1027 Q_ASSERT(isObject()); 1028 JSObjectDeleteProperty(*m_engine, *this, property, exception); 1029 } 1030 1031 template<typename T> 1032 inline void QScriptValuePrivate::setProperty(T name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags) 1033 { 1034 if (!isObject()) 1035 return; 1036 1037 if (!value->isJSBased()) 1038 value->assignEngine(engine()); 1039 1040 JSValueRef exception = 0; 1041 if (!value->isValid()) { 1042 // Remove the property. 1043 deleteProperty(name, &exception); 1044 m_engine->setException(exception); 1045 return; 1046 } 1047 if (m_engine != value->m_engine) { 1048 qWarning("QScriptValue::setProperty() failed: cannot set value created in a different engine"); 1049 return; 1050 } 1051 1052 setProperty(name, *value, QScriptConverter::toPropertyFlags(flags), &exception); 1053 m_engine->setException(exception); 1054 } 1055 1056 inline QScriptValue::PropertyFlags QScriptValuePrivate::propertyFlags(const QString& name, const QScriptValue::ResolveFlags& mode) 1057 { 1058 JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(name)); 1059 return propertyFlags(propertyName.get(), mode); 1060 } 1061 1062 inline QScriptValue::PropertyFlags QScriptValuePrivate::propertyFlags(const QScriptStringPrivate* name, const QScriptValue::ResolveFlags& mode) 1063 { 1064 return propertyFlags(*name, mode); 1065 } 1066 1067 inline QScriptValue::PropertyFlags QScriptValuePrivate::propertyFlags(JSStringRef name, const QScriptValue::ResolveFlags& mode) 1068 { 1069 unsigned flags = 0; 1070 if (!isObject()) 1071 return QScriptValue::PropertyFlags(flags); 1072 1073 // FIXME It could be faster and nicer, but new JSC C API should be created. 1074 static JSStringRef objectName = QScriptConverter::toString("Object"); 1075 static JSStringRef propertyDescriptorName = QScriptConverter::toString("getOwnPropertyDescriptor"); 1076 1077 // FIXME This is dangerous if global object was modified (bug 41839). 1078 JSValueRef exception = 0; 1079 JSObjectRef globalObject = JSContextGetGlobalObject(*m_engine); 1080 JSValueRef objectConstructor = JSObjectGetProperty(*m_engine, globalObject, objectName, &exception); 1081 Q_ASSERT(JSValueIsObject(*m_engine, objectConstructor)); 1082 JSValueRef propertyDescriptorGetter = JSObjectGetProperty(*m_engine, const_cast<JSObjectRef>(objectConstructor), propertyDescriptorName, &exception); 1083 Q_ASSERT(JSValueIsObject(*m_engine, propertyDescriptorGetter)); 1084 1085 JSValueRef arguments[] = { *this, JSValueMakeString(*m_engine, name) }; 1086 JSObjectRef propertyDescriptor 1087 = const_cast<JSObjectRef>(JSObjectCallAsFunction(*m_engine, 1088 const_cast<JSObjectRef>(propertyDescriptorGetter), 1089 /* thisObject */ 0, 1090 /* argumentCount */ 2, 1091 arguments, 1092 &exception)); 1093 if (exception) { 1094 // Invalid property. 1095 return QScriptValue::PropertyFlags(flags); 1096 } 1097 1098 if (!JSValueIsObject(*m_engine, propertyDescriptor)) { 1099 // Property isn't owned by this object. 1100 JSObjectRef proto; 1101 if (mode == QScriptValue::ResolveLocal 1102 || ((proto = const_cast<JSObjectRef>(JSObjectGetPrototype(*m_engine, *this))) && JSValueIsNull(*m_engine, proto))) { 1103 return QScriptValue::PropertyFlags(flags); 1104 } 1105 QScriptValuePrivate p(engine(), proto); 1106 return p.propertyFlags(name, QScriptValue::ResolvePrototype); 1107 } 1108 1109 static JSStringRef writableName = QScriptConverter::toString("writable"); 1110 static JSStringRef configurableName = QScriptConverter::toString("configurable"); 1111 static JSStringRef enumerableName = QScriptConverter::toString("enumerable"); 1112 1113 bool readOnly = !JSValueToBoolean(*m_engine, JSObjectGetProperty(*m_engine, propertyDescriptor, writableName, &exception)); 1114 if (!exception && readOnly) 1115 flags |= QScriptValue::ReadOnly; 1116 bool undeletable = !JSValueToBoolean(*m_engine, JSObjectGetProperty(*m_engine, propertyDescriptor, configurableName, &exception)); 1117 if (!exception && undeletable) 1118 flags |= QScriptValue::Undeletable; 1119 bool skipInEnum = !JSValueToBoolean(*m_engine, JSObjectGetProperty(*m_engine, propertyDescriptor, enumerableName, &exception)); 1120 if (!exception && skipInEnum) 1121 flags |= QScriptValue::SkipInEnumeration; 1122 1123 return QScriptValue::PropertyFlags(flags); 1124 } 1125 1126 QScriptValuePrivate* QScriptValuePrivate::call(const QScriptValuePrivate*, const QScriptValueList& args) 1127 { 1128 switch (m_state) { 1129 case JSValue: 1130 if (refinedJSValue() != JSObject) 1131 return new QScriptValuePrivate; 1132 // Fall-through. 1133 case JSObject: 1134 { 1135 // Convert all arguments and bind to the engine. 1136 int argc = args.size(); 1137 QVarLengthArray<JSValueRef, 8> argv(argc); 1138 QScriptValueList::const_iterator i = args.constBegin(); 1139 for (int j = 0; i != args.constEnd(); j++, i++) { 1140 QScriptValuePrivate* value = QScriptValuePrivate::get(*i); 1141 if (!value->assignEngine(engine())) { 1142 qWarning("QScriptValue::call() failed: cannot call function with values created in a different engine"); 1143 return new QScriptValuePrivate; 1144 } 1145 argv[j] = *value; 1146 } 1147 1148 // Make the call 1149 JSValueRef exception = 0; 1150 JSValueRef result = JSObjectCallAsFunction(*m_engine, *this, /* thisObject */ 0, argc, argv.constData(), &exception); 1151 if (!result && exception) { 1152 m_engine->setException(exception); 1153 return new QScriptValuePrivate(engine(), exception); 1154 } 1155 if (result && !exception) 1156 return new QScriptValuePrivate(engine(), result); 1157 } 1158 // this QSV is not a function <-- !result && !exception. Fall-through. 1159 default: 1160 return new QScriptValuePrivate; 1161 } 1162 } 1163 1164 QScriptEnginePrivate* QScriptValuePrivate::engine() const 1165 { 1166 // As long as m_engine is an autoinitializated pointer we can safely return it without 1167 // checking current state. 1168 return m_engine.data(); 1169 } 1170 1171 QScriptValuePrivate::operator JSValueRef() const 1172 { 1173 Q_ASSERT(isJSBased()); 1174 Q_ASSERT(u.m_value); 1175 return u.m_value; 1176 } 1177 1178 QScriptValuePrivate::operator JSObjectRef() const 1179 { 1180 Q_ASSERT(m_state == JSObject); 1181 Q_ASSERT(u.m_object); 1182 return u.m_object; 1183 } 1184 1185 /*! 1186 \internal 1187 Refines the state of this QScriptValuePrivate. Returns the new state. 1188 */ 1189 QScriptValuePrivate::State QScriptValuePrivate::refinedJSValue() 1190 { 1191 Q_ASSERT(m_state == JSValue); 1192 if (!JSValueIsObject(*m_engine, *this)) { 1193 m_state = JSPrimitive; 1194 } else { 1195 // Difference between JSValueRef and JSObjectRef is only in their type, binarywise it is the same. 1196 // As m_value and m_object are stored in the u union, it is enough to change the m_state only. 1197 m_state = JSObject; 1198 } 1199 return m_state; 1200 } 1201 1202 /*! 1203 \internal 1204 Returns true if QSV have an engine associated. 1205 */ 1206 bool QScriptValuePrivate::isJSBased() const { return m_state >= JSValue; } 1207 1208 /*! 1209 \internal 1210 Returns true if current value of QSV is placed in m_number. 1211 */ 1212 bool QScriptValuePrivate::isNumberBased() const { return m_state == CNumber || m_state == CBool; } 1213 1214 /*! 1215 \internal 1216 Returns true if current value of QSV is placed in m_string. 1217 */ 1218 bool QScriptValuePrivate::isStringBased() const { return m_state == CString; } 1219 1220 #endif // qscriptvalue_p_h 1221