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 <QtCore/qmath.h> 28 #include <QtCore/qnumeric.h> 29 #include <QtCore/qshareddata.h> 30 #include <QtCore/qvarlengtharray.h> 31 32 class QScriptEngine; 33 class QScriptValue; 34 35 /* 36 \internal 37 \class QScriptValuePrivate 38 39 Implementation of QScriptValue. 40 The implementation is based on a state machine. The states names are included in 41 QScriptValuePrivate::States. Each method should check for the current state and then perform a 42 correct action. 43 44 States: 45 Invalid -> QSVP is invalid, no assumptions should be made about class members (apart from m_value). 46 CString -> QSVP is created from QString or const char* and no JSC engine has been associated yet. 47 Current value is kept in m_string, 48 CNumber -> QSVP is created from int, uint, double... and no JSC engine has been bind yet. Current 49 value is kept in m_number 50 CBool -> QSVP is created from bool and no JSC engine has been associated yet. Current value is kept 51 in m_number 52 CSpecial -> QSVP is Undefined or Null, but a JSC engine hasn't been associated yet, current value 53 is kept in m_number (cast of QScriptValue::SpecialValue) 54 JSValue -> QSVP is associated with engine, but there is no information about real type, the state 55 have really short live cycle. Normally it is created as a function call result. 56 JSNative -> QSVP is associated with engine, and it is sure that it isn't a JavaScript object. 57 JSObject -> QSVP is associated with engine, and it is sure that it is a JavaScript object. 58 59 Each state keep all necessary information to invoke all methods, if not it should be changed to 60 a proper state. Changed state shouldn't be reverted. 61 */ 62 63 class QScriptValuePrivate : public QSharedData { 64 public: 65 inline static QScriptValuePrivate* get(const QScriptValue& q); 66 inline static QScriptValue get(const QScriptValuePrivate* d); 67 inline static QScriptValue get(QScriptValuePrivate* d); 68 69 inline ~QScriptValuePrivate(); 70 71 inline QScriptValuePrivate(); 72 inline QScriptValuePrivate(const QString& string); 73 inline QScriptValuePrivate(bool value); 74 inline QScriptValuePrivate(int number); 75 inline QScriptValuePrivate(uint number); 76 inline QScriptValuePrivate(qsreal number); 77 inline QScriptValuePrivate(QScriptValue::SpecialValue value); 78 79 inline QScriptValuePrivate(const QScriptEngine* engine, bool value); 80 inline QScriptValuePrivate(const QScriptEngine* engine, int value); 81 inline QScriptValuePrivate(const QScriptEngine* engine, uint value); 82 inline QScriptValuePrivate(const QScriptEngine* engine, qsreal value); 83 inline QScriptValuePrivate(const QScriptEngine* engine, const QString& value); 84 inline QScriptValuePrivate(const QScriptEngine* engine, QScriptValue::SpecialValue value); 85 86 inline QScriptValuePrivate(const QScriptEnginePrivate* engine, JSValueRef value); 87 inline QScriptValuePrivate(const QScriptEnginePrivate* engine, JSValueRef value, JSObjectRef object); 88 89 inline bool isValid() const; 90 inline bool isBool(); 91 inline bool isNumber(); 92 inline bool isNull(); 93 inline bool isString(); 94 inline bool isUndefined(); 95 inline bool isError(); 96 inline bool isObject(); 97 inline bool isFunction(); 98 99 inline QString toString() const; 100 inline qsreal toNumber() const; 101 inline bool toBool() const; 102 inline qsreal toInteger() const; 103 inline qint32 toInt32() const; 104 inline quint32 toUInt32() const; 105 inline quint16 toUInt16() const; 106 107 inline bool equals(QScriptValuePrivate* other); 108 inline bool strictlyEquals(const QScriptValuePrivate* other) const; 109 inline bool assignEngine(QScriptEnginePrivate* engine); 110 111 inline QScriptValuePrivate* call(const QScriptValuePrivate* , const QScriptValueList& args); 112 113 inline JSGlobalContextRef context() const; 114 inline JSValueRef value() const; 115 inline JSObjectRef object() const; 116 inline QScriptEnginePrivate* engine() const; 117 118 private: 119 // Please, update class documentation when you change the enum. 120 enum States { 121 Invalid = 0, 122 CString = 0x1000, 123 CNumber, 124 CBool, 125 CSpecial, 126 JSValue = 0x2000, // JS values are equal or higher then this value. 127 JSNative, 128 JSObject 129 } m_state; 130 QScriptEnginePtr m_engine; 131 QString m_string; 132 qsreal m_number; 133 JSValueRef m_value; 134 JSObjectRef m_object; 135 136 inline void setValue(JSValueRef); 137 138 inline bool inherits(const char*); 139 140 inline bool isJSBased() const; 141 inline bool isNumberBased() const; 142 inline bool isStringBased() const; 143 }; 144 145 QScriptValuePrivate* QScriptValuePrivate::get(const QScriptValue& q) { return q.d_ptr.data(); } 146 147 QScriptValue QScriptValuePrivate::get(const QScriptValuePrivate* d) 148 { 149 return QScriptValue(const_cast<QScriptValuePrivate*>(d)); 150 } 151 152 QScriptValue QScriptValuePrivate::get(QScriptValuePrivate* d) 153 { 154 return QScriptValue(d); 155 } 156 157 QScriptValuePrivate::~QScriptValuePrivate() 158 { 159 if (m_value) 160 JSValueUnprotect(context(), m_value); 161 } 162 163 QScriptValuePrivate::QScriptValuePrivate() 164 : m_state(Invalid) 165 , m_value(0) 166 { 167 } 168 169 QScriptValuePrivate::QScriptValuePrivate(const QString& string) 170 : m_state(CString) 171 , m_string(string) 172 , m_value(0) 173 { 174 } 175 176 QScriptValuePrivate::QScriptValuePrivate(bool value) 177 : m_state(CBool) 178 , m_number(value) 179 , m_value(0) 180 { 181 } 182 183 QScriptValuePrivate::QScriptValuePrivate(int number) 184 : m_state(CNumber) 185 , m_number(number) 186 , m_value(0) 187 { 188 } 189 190 QScriptValuePrivate::QScriptValuePrivate(uint number) 191 : m_state(CNumber) 192 , m_number(number) 193 , m_value(0) 194 { 195 } 196 197 QScriptValuePrivate::QScriptValuePrivate(qsreal number) 198 : m_state(CNumber) 199 , m_number(number) 200 , m_value(0) 201 { 202 } 203 204 QScriptValuePrivate::QScriptValuePrivate(QScriptValue::SpecialValue value) 205 : m_state(CSpecial) 206 , m_number(value) 207 , m_value(0) 208 { 209 } 210 211 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, bool value) 212 : m_state(JSNative) 213 { 214 if (!engine) { 215 // slower path reinitialization 216 m_state = CBool; 217 m_number = value; 218 m_value = 0; 219 } else { 220 m_engine = QScriptEnginePrivate::get(engine); 221 m_value = m_engine->makeJSValue(value); 222 JSValueProtect(context(), m_value); 223 } 224 } 225 226 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, int value) 227 : m_state(JSNative) 228 { 229 if (!engine) { 230 // slower path reinitialization 231 m_state = CNumber; 232 m_number = value; 233 m_value = 0; 234 } else { 235 m_engine = QScriptEnginePrivate::get(engine); 236 m_value = m_engine->makeJSValue(value); 237 JSValueProtect(context(), m_value); 238 } 239 } 240 241 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, uint value) 242 : m_state(JSNative) 243 { 244 if (!engine) { 245 // slower path reinitialization 246 m_state = CNumber; 247 m_number = value; 248 m_value = 0; 249 } else { 250 m_engine = QScriptEnginePrivate::get(engine); 251 m_value = m_engine->makeJSValue(value); 252 JSValueProtect(context(), m_value); 253 } 254 } 255 256 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, qsreal value) 257 : m_state(JSNative) 258 { 259 if (!engine) { 260 // slower path reinitialization 261 m_state = CNumber; 262 m_number = value; 263 m_value = 0; 264 } else { 265 m_engine = QScriptEnginePrivate::get(engine); 266 m_value = m_engine->makeJSValue(value); 267 JSValueProtect(context(), m_value); 268 } 269 } 270 271 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, const QString& value) 272 : m_state(JSNative) 273 { 274 if (!engine) { 275 // slower path reinitialization 276 m_state = CString; 277 m_string = value; 278 m_value = 0; 279 } else { 280 m_engine = QScriptEnginePrivate::get(engine); 281 m_value = m_engine->makeJSValue(value); 282 JSValueProtect(context(), m_value); 283 } 284 } 285 286 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, QScriptValue::SpecialValue value) 287 : m_state(JSNative) 288 { 289 if (!engine) { 290 // slower path reinitialization 291 m_state = CSpecial; 292 m_number = value; 293 m_value = 0; 294 } else { 295 m_engine = QScriptEnginePrivate::get(engine); 296 m_value = m_engine->makeJSValue(value); 297 JSValueProtect(context(), m_value); 298 } 299 } 300 301 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, JSValueRef value) 302 : m_state(JSValue) 303 , m_engine(const_cast<QScriptEnginePrivate*>(engine)) 304 , m_value(value) 305 { 306 Q_ASSERT(engine); 307 JSValueProtect(context(), m_value); 308 } 309 310 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, JSValueRef value, JSObjectRef object) 311 : m_state(JSObject) 312 , m_engine(const_cast<QScriptEnginePrivate*>(engine)) 313 , m_value(value) 314 , m_object(object) 315 { 316 Q_ASSERT(engine); 317 JSValueProtect(context(), m_value); 318 } 319 320 bool QScriptValuePrivate::isValid() const { return m_state != Invalid; } 321 322 bool QScriptValuePrivate::isBool() 323 { 324 switch (m_state) { 325 case CBool: 326 return true; 327 case JSValue: 328 if (isObject()) 329 return false; 330 // Fall-through. 331 case JSNative: 332 return JSValueIsBoolean(context(), value()); 333 default: 334 return false; 335 } 336 } 337 338 bool QScriptValuePrivate::isNumber() 339 { 340 switch (m_state) { 341 case CNumber: 342 return true; 343 case JSValue: 344 if (isObject()) 345 return false; 346 // Fall-through. 347 case JSNative: 348 return JSValueIsNumber(context(), value()); 349 default: 350 return false; 351 } 352 } 353 354 bool QScriptValuePrivate::isNull() 355 { 356 switch (m_state) { 357 case CSpecial: 358 return m_number == static_cast<int>(QScriptValue::NullValue); 359 case JSValue: 360 if (isObject()) 361 return false; 362 // Fall-through. 363 case JSNative: 364 return JSValueIsNull(context(), value()); 365 default: 366 return false; 367 } 368 } 369 370 bool QScriptValuePrivate::isString() 371 { 372 switch (m_state) { 373 case CString: 374 return true; 375 case JSValue: 376 if (isObject()) 377 return false; 378 // Fall-through. 379 case JSNative: 380 return JSValueIsString(context(), value()); 381 default: 382 return false; 383 } 384 } 385 386 bool QScriptValuePrivate::isUndefined() 387 { 388 switch (m_state) { 389 case CSpecial: 390 return m_number == static_cast<int>(QScriptValue::UndefinedValue); 391 case JSValue: 392 if (isObject()) 393 return false; 394 // Fall-through. 395 case JSNative: 396 return JSValueIsUndefined(context(), value()); 397 default: 398 return false; 399 } 400 } 401 402 bool QScriptValuePrivate::isError() 403 { 404 switch (m_state) { 405 case JSValue: 406 if (!isObject()) 407 return false; 408 // Fall-through. 409 case JSObject: 410 return inherits("Error"); 411 default: 412 return false; 413 } 414 } 415 416 bool QScriptValuePrivate::isObject() 417 { 418 switch (m_state) { 419 case JSObject: 420 return true; 421 case JSValue: 422 m_object = JSValueToObject(context(), value(), /* exception */ 0); 423 if (!m_object) 424 return false; 425 m_state = JSObject; 426 return true; 427 default: 428 return false; 429 } 430 } 431 432 bool QScriptValuePrivate::isFunction() 433 { 434 switch (m_state) { 435 case JSValue: 436 m_object = JSValueToObject(context(), value(), /* exception */ 0); 437 if (!m_object) 438 return false; 439 m_state = JSObject; 440 // Fall-through. 441 case JSObject: 442 return JSObjectIsFunction(context(), object()); 443 default: 444 return false; 445 } 446 } 447 448 QString QScriptValuePrivate::toString() const 449 { 450 switch (m_state) { 451 case Invalid: 452 return QString(); 453 case CBool: 454 return m_number ? QString::fromLatin1("true") : QString::fromLatin1("false"); 455 case CString: 456 return m_string; 457 case CNumber: 458 return QString::number(m_number); 459 case CSpecial: 460 return m_number == QScriptValue::NullValue ? QString::fromLatin1("null") : QString::fromLatin1("undefined"); 461 case JSValue: 462 case JSNative: 463 case JSObject: 464 return QScriptConverter::toString(JSValueToStringCopy(context(), value(), /* exception */ 0)); 465 } 466 467 Q_ASSERT_X(false, "toString()", "Not all states are included in the previous switch statement."); 468 return QString(); // Avoid compiler warning. 469 } 470 471 qsreal QScriptValuePrivate::toNumber() const 472 { 473 switch (m_state) { 474 case JSValue: 475 case JSNative: 476 case JSObject: 477 return JSValueToNumber(context(), value(), /* exception */ 0); 478 case CNumber: 479 return m_number; 480 case CBool: 481 return m_number ? 1 : 0; 482 case Invalid: 483 return 0; 484 case CSpecial: 485 return m_number == QScriptValue::NullValue ? 0 : qQNaN(); 486 case CString: 487 bool ok; 488 qsreal result = m_string.toDouble(&ok); 489 if (ok) 490 return result; 491 result = m_string.toInt(&ok, 0); // Try other bases. 492 if (ok) 493 return result; 494 if (m_string == "Infinity" || m_string == "-Infinity") 495 return qInf(); 496 return m_string.length() ? qQNaN() : 0; 497 } 498 499 Q_ASSERT_X(false, "toNumber()", "Not all states are included in the previous switch statement."); 500 return 0; // Avoid compiler warning. 501 } 502 503 bool QScriptValuePrivate::toBool() const 504 { 505 switch (m_state) { 506 case JSValue: 507 case JSNative: 508 return JSValueToBoolean(context(), value()); 509 case JSObject: 510 return true; 511 case CNumber: 512 return !(qIsNaN(m_number) || !m_number); 513 case CBool: 514 return m_number; 515 case Invalid: 516 case CSpecial: 517 return false; 518 case CString: 519 return m_string.length(); 520 } 521 522 Q_ASSERT_X(false, "toBool()", "Not all states are included in the previous switch statement."); 523 return false; // Avoid compiler warning. 524 } 525 526 qsreal QScriptValuePrivate::toInteger() const 527 { 528 // TODO it is not true implementation! 529 return toNumber(); 530 } 531 532 qint32 QScriptValuePrivate::toInt32() const 533 { 534 // TODO it is not true implementation! 535 return toNumber(); 536 } 537 538 quint32 QScriptValuePrivate::toUInt32() const 539 { 540 // TODO it is not true implementation! 541 return toNumber(); 542 } 543 544 quint16 QScriptValuePrivate::toUInt16() const 545 { 546 // TODO it is not true implementation! 547 return toNumber(); 548 } 549 550 551 bool QScriptValuePrivate::equals(QScriptValuePrivate* other) 552 { 553 if (!isValid() || !other->isValid()) 554 return false; 555 556 if ((m_state == other->m_state) && !isJSBased()) { 557 if (isNumberBased()) 558 return m_number == other->m_number; 559 return m_string == other->m_string; 560 } 561 562 if (isJSBased() && !other->isJSBased()) { 563 if (!other->assignEngine(engine())) { 564 qWarning("equals(): Cannot compare to a value created in a different engine"); 565 return false; 566 } 567 } else if (!isJSBased() && other->isJSBased()) { 568 if (!other->assignEngine(other->engine())) { 569 qWarning("equals(): Cannot compare to a value created in a different engine"); 570 return false; 571 } 572 } 573 574 return JSValueIsEqual(context(), value(), other->value(), /* exception */ 0); 575 } 576 577 bool QScriptValuePrivate::strictlyEquals(const QScriptValuePrivate* other) const 578 { 579 if (m_state != other->m_state) 580 return false; 581 if (isJSBased()) { 582 if (other->engine() != engine()) { 583 qWarning("strictlyEquals(): Cannot compare to a value created in a different engine"); 584 return false; 585 } 586 return JSValueIsStrictEqual(context(), value(), other->value()); 587 } 588 if (isStringBased()) 589 return m_string == other->m_string; 590 if (isNumberBased()) 591 return m_number == other->m_number; 592 593 return false; // Invalid state. 594 } 595 596 /*! 597 Tries to assign \a engine to this value. Returns true on success; otherwise returns false. 598 */ 599 bool QScriptValuePrivate::assignEngine(QScriptEnginePrivate* engine) 600 { 601 JSValueRef value; 602 switch (m_state) { 603 case CBool: 604 value = engine->makeJSValue(static_cast<bool>(m_number)); 605 break; 606 case CString: 607 value = engine->makeJSValue(m_string); 608 break; 609 case CNumber: 610 value = engine->makeJSValue(m_number); 611 break; 612 case CSpecial: 613 value = engine->makeJSValue(static_cast<QScriptValue::SpecialValue>(m_number)); 614 break; 615 default: 616 if (!isJSBased()) 617 Q_ASSERT_X(!isJSBased(), "assignEngine()", "Not all states are included in the previous switch statement."); 618 else 619 qWarning("JSValue can't be rassigned to an another engine."); 620 return false; 621 } 622 m_engine = engine; 623 m_state = JSNative; 624 setValue(value); 625 return true; 626 } 627 628 QScriptValuePrivate* QScriptValuePrivate::call(const QScriptValuePrivate*, const QScriptValueList& args) 629 { 630 switch (m_state) { 631 case JSValue: 632 m_object = JSValueToObject(context(), value(), /* exception */ 0); 633 if (!object()) { 634 m_state = JSValue; 635 return new QScriptValuePrivate; 636 } 637 m_state = JSObject; 638 // Fall-through. 639 case JSObject: 640 { 641 // Convert all arguments and bind to the engine. 642 int argc = args.size(); 643 QVarLengthArray<JSValueRef, 8> argv(argc); 644 QScriptValueList::const_iterator i = args.constBegin(); 645 for (int j = 0; i != args.constEnd(); j++, i++) { 646 QScriptValuePrivate* value = QScriptValuePrivate::get(*i); 647 if (!value->assignEngine(engine())) { 648 qWarning("QScriptValue::call() failed: cannot call function with values created in a different engine"); 649 return new QScriptValuePrivate; 650 } 651 argv[j] = value->value(); 652 } 653 654 // Make the call 655 JSValueRef exception = 0; 656 JSValueRef result = JSObjectCallAsFunction(context(), object(), /* thisObject */ 0, argc, argv.constData(), &exception); 657 if (!result && exception) 658 return new QScriptValuePrivate(engine(), exception); 659 if (result && !exception) 660 return new QScriptValuePrivate(engine(), result); 661 } 662 // this QSV is not a function <-- !result && !exception. Fall-through. 663 default: 664 return new QScriptValuePrivate; 665 } 666 } 667 668 QScriptEnginePrivate* QScriptValuePrivate::engine() const 669 { 670 // As long as m_engine is an autoinitializated pointer we can safely return it without 671 // checking current state. 672 return m_engine.data(); 673 } 674 675 JSGlobalContextRef QScriptValuePrivate::context() const 676 { 677 Q_ASSERT(isJSBased()); 678 return m_engine->context(); 679 } 680 681 JSValueRef QScriptValuePrivate::value() const 682 { 683 Q_ASSERT(isJSBased()); 684 return m_value; 685 } 686 687 JSObjectRef QScriptValuePrivate::object() const 688 { 689 Q_ASSERT(m_state == JSObject); 690 return m_object; 691 } 692 693 void QScriptValuePrivate::setValue(JSValueRef value) 694 { 695 if (m_value) 696 JSValueUnprotect(context(), m_value); 697 if (value) 698 JSValueProtect(context(), value); 699 m_value = value; 700 } 701 702 /*! 703 \internal 704 Returns true if QSV is created from constructor with the given \a name, it has to be a 705 built-in type. 706 */ 707 bool QScriptValuePrivate::inherits(const char* name) 708 { 709 Q_ASSERT(isJSBased()); 710 JSObjectRef globalObject = JSContextGetGlobalObject(context()); 711 JSValueRef error = JSObjectGetProperty(context(), globalObject, QScriptConverter::toString(name), 0); 712 return JSValueIsInstanceOfConstructor(context(), value(), JSValueToObject(context(), error, /* exception */ 0), /* exception */ 0); 713 } 714 715 /*! 716 \internal 717 Returns true if QSV have an engine associated. 718 */ 719 bool QScriptValuePrivate::isJSBased() const { return m_state >= JSValue; } 720 721 /*! 722 \internal 723 Returns true if current value of QSV is placed in m_number. 724 */ 725 bool QScriptValuePrivate::isNumberBased() const { return !isJSBased() && !isStringBased() && m_state != Invalid; } 726 727 /*! 728 \internal 729 Returns true if current value of QSV is placed in m_string. 730 */ 731 bool QScriptValuePrivate::isStringBased() const { return m_state == CString; } 732 733 #endif // qscriptvalue_p_h 734