2     Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) 
   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. 
   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. 
  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. 
  20 #ifndef qscriptvalue_p_h 
  21 #define qscriptvalue_p_h 
  23 #include "qscriptconverter_p.h" 
  24 #include "qscriptengine_p.h" 
  25 #include "qscriptvalue.h" 
  26 #include <JavaScriptCore/JavaScript.h> 
  27 #include <QtCore/qshareddata.h> 
  28 #include <QtCore/qvarlengtharray.h> 
  35   \class QScriptValuePrivate 
  37   Implementation of QScriptValue. 
  38   The implementation is based on a state machine. The states names are included in 
  39   QScriptValuePrivate::States. Each method should check for the current state and then perform a 
  43     Invalid -> QSVP is invalid, no assumptions should be made about class members (apart from m_value). 
  44     CString -> QSVP is created from QString or const char* and no JSC engine has been associated yet. 
  45         Current value is kept in m_string, 
  46     CNumber -> QSVP is created from int, uint, double... and no JSC engine has been bind yet. Current 
  47         value is kept in m_number 
  48     CBool -> QSVP is created from bool and no JSC engine has been associated yet. Current value is kept 
  50     CSpecial -> QSVP is Undefined or Null, but a JSC engine hasn't been associated yet, current value 
  51         is kept in m_number (cast of QScriptValue::SpecialValue) 
  52     JSValue -> QSVP is associated with engine, but there is no information about real type, the state 
  53         have really short live cycle. Normally it is created as a function call result. 
  54     JSNative -> QSVP is associated with engine, and it is sure that it isn't a JavaScript object. 
  55     JSObject -> QSVP is associated with engine, and it is sure that it is a JavaScript object. 
  57   Each state keep all necessary information to invoke all methods, if not it should be changed to 
  58   a proper state. Changed state shouldn't be reverted. 
  61 class QScriptValuePrivate 
: public QSharedData 
{ 
  63     inline static QScriptValuePrivate
* get(const QScriptValue
& q
); 
  64     inline static QScriptValue 
get(const QScriptValuePrivate
* d
); 
  65     inline static QScriptValue 
get(QScriptValuePrivate
* d
); 
  67     inline ~QScriptValuePrivate(); 
  69     inline QScriptValuePrivate(); 
  70     inline QScriptValuePrivate(const QString
& string
); 
  71     inline QScriptValuePrivate(bool value
); 
  72     inline QScriptValuePrivate(int number
); 
  73     inline QScriptValuePrivate(uint number
); 
  74     inline QScriptValuePrivate(qsreal number
); 
  75     inline QScriptValuePrivate(QScriptValue::SpecialValue value
); 
  77     inline QScriptValuePrivate(const QScriptEngine
* engine
, bool value
); 
  78     inline QScriptValuePrivate(const QScriptEngine
* engine
, int value
); 
  79     inline QScriptValuePrivate(const QScriptEngine
* engine
, uint value
); 
  80     inline QScriptValuePrivate(const QScriptEngine
* engine
, qsreal value
); 
  81     inline QScriptValuePrivate(const QScriptEngine
* engine
, const QString
& value
); 
  82     inline QScriptValuePrivate(const QScriptEngine
* engine
, QScriptValue::SpecialValue value
); 
  84     inline QScriptValuePrivate(const QScriptEnginePrivate
* engine
, JSValueRef value
); 
  85     inline QScriptValuePrivate(const QScriptEnginePrivate
* engine
, JSValueRef value
, JSObjectRef object
); 
  87     inline bool isValid() const; 
  89     inline bool isNumber(); 
  91     inline bool isString(); 
  92     inline bool isUndefined(); 
  93     inline bool isError(); 
  94     inline bool isObject(); 
  95     inline bool isFunction(); 
  97     inline QString 
toString() const; 
  98     inline qsreal 
toNumber() const; 
  99     inline bool toBool() const; 
 100     inline qsreal 
toInteger() const; 
 101     inline qint32 
toInt32() const; 
 102     inline quint32 
toUInt32() const; 
 103     inline quint16 
toUInt16() const; 
 105     inline bool equals(QScriptValuePrivate
* other
); 
 106     inline bool strictlyEquals(const QScriptValuePrivate
* other
) const; 
 107     inline bool assignEngine(QScriptEnginePrivate
* engine
); 
 109     inline QScriptValuePrivate
* call(const QScriptValuePrivate
* , const QScriptValueList
& args
); 
 111     inline JSGlobalContextRef 
context() const; 
 112     inline JSValueRef 
value() const; 
 113     inline JSObjectRef 
object() const; 
 114     inline QScriptEnginePrivate
* engine() const; 
 117     // Please, update class documentation when you change the enum. 
 124         JSValue 
= 0x2000, // JS values are equal or higher then this value. 
 128     QScriptEnginePtr m_engine
; 
 132     JSObjectRef m_object
; 
 134     inline void setValue(JSValueRef
); 
 136     inline bool inherits(const char*); 
 138     inline bool isJSBased() const; 
 139     inline bool isNumberBased() const; 
 140     inline bool isStringBased() const; 
 143 QScriptValuePrivate
* QScriptValuePrivate::get(const QScriptValue
& q
) { return q
.d_ptr
.data(); } 
 145 QScriptValue 
QScriptValuePrivate::get(const QScriptValuePrivate
* d
) 
 147     return QScriptValue(const_cast<QScriptValuePrivate
*>(d
)); 
 150 QScriptValue 
QScriptValuePrivate::get(QScriptValuePrivate
* d
) 
 152     return QScriptValue(d
); 
 155 QScriptValuePrivate::~QScriptValuePrivate() 
 158         JSValueUnprotect(context(), m_value
); 
 161 QScriptValuePrivate::QScriptValuePrivate() 
 167 QScriptValuePrivate::QScriptValuePrivate(const QString
& string
) 
 174 QScriptValuePrivate::QScriptValuePrivate(bool value
) 
 181 QScriptValuePrivate::QScriptValuePrivate(int number
) 
 188 QScriptValuePrivate::QScriptValuePrivate(uint number
) 
 195 QScriptValuePrivate::QScriptValuePrivate(qsreal number
) 
 202 QScriptValuePrivate::QScriptValuePrivate(QScriptValue::SpecialValue value
) 
 209 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine
* engine
, bool value
) 
 213         // slower path reinitialization 
 218         m_engine 
= QScriptEnginePrivate::get(engine
); 
 219         m_value 
= m_engine
->makeJSValue(value
); 
 220         JSValueProtect(context(), m_value
); 
 224 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine
* engine
, int value
) 
 228         // slower path reinitialization 
 233         m_engine 
= QScriptEnginePrivate::get(engine
); 
 234         m_value 
= m_engine
->makeJSValue(value
); 
 235         JSValueProtect(context(), m_value
); 
 239 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine
* engine
, uint value
) 
 243         // slower path reinitialization 
 248         m_engine 
= QScriptEnginePrivate::get(engine
); 
 249         m_value 
= m_engine
->makeJSValue(value
); 
 250         JSValueProtect(context(), m_value
); 
 254 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine
* engine
, qsreal value
) 
 258         // slower path reinitialization 
 263         m_engine 
= QScriptEnginePrivate::get(engine
); 
 264         m_value 
= m_engine
->makeJSValue(value
); 
 265         JSValueProtect(context(), m_value
); 
 269 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine
* engine
, const QString
& value
) 
 273         // slower path reinitialization 
 278         m_engine 
= QScriptEnginePrivate::get(engine
); 
 279         m_value 
= m_engine
->makeJSValue(value
); 
 280         JSValueProtect(context(), m_value
); 
 284 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine
* engine
, QScriptValue::SpecialValue value
) 
 288         // slower path reinitialization 
 293         m_engine 
= QScriptEnginePrivate::get(engine
); 
 294         m_value 
= m_engine
->makeJSValue(value
); 
 295         JSValueProtect(context(), m_value
); 
 299 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate
* engine
, JSValueRef value
) 
 301     , m_engine(const_cast<QScriptEnginePrivate
*>(engine
)) 
 305     JSValueProtect(context(), m_value
); 
 308 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate
* engine
, JSValueRef value
, JSObjectRef object
) 
 310     , m_engine(const_cast<QScriptEnginePrivate
*>(engine
)) 
 315     JSValueProtect(context(), m_value
); 
 318 bool QScriptValuePrivate::isValid() const { return m_state 
!= Invalid
; } 
 320 bool QScriptValuePrivate::isBool() 
 330         return JSValueIsBoolean(context(), value()); 
 336 bool QScriptValuePrivate::isNumber() 
 346         return JSValueIsNumber(context(), value()); 
 352 bool QScriptValuePrivate::isNull() 
 356         return m_number 
== static_cast<int>(QScriptValue::NullValue
); 
 362         return JSValueIsNull(context(), value()); 
 368 bool QScriptValuePrivate::isString() 
 378         return JSValueIsString(context(), value()); 
 384 bool QScriptValuePrivate::isUndefined() 
 388         return m_number 
== static_cast<int>(QScriptValue::UndefinedValue
); 
 394         return JSValueIsUndefined(context(), value()); 
 400 bool QScriptValuePrivate::isError() 
 408         return inherits("Error"); 
 414 bool QScriptValuePrivate::isObject() 
 420         m_object 
= JSValueToObject(context(), value(), /* exception */ 0); 
 430 bool QScriptValuePrivate::isFunction() 
 434         m_object 
= JSValueToObject(context(), value(), /* exception */ 0); 
 440         return JSObjectIsFunction(context(), object()); 
 446 QString 
QScriptValuePrivate::toString() const 
 452         return m_number 
? QString::fromLatin1("true") : QString::fromLatin1("false"); 
 456         return QString::number(m_number
); 
 458         return m_number 
== QScriptValue::NullValue 
? QString::fromLatin1("null") : QString::fromLatin1("undefined"); 
 462         return QScriptConverter::toString(JSValueToStringCopy(context(), value(), /* exception */ 0)); 
 465     Q_ASSERT_X(false, "toString()", "Not all states are included in the previous switch statement."); 
 466     return QString(); // Avoid compiler warning. 
 469 qsreal 
QScriptValuePrivate::toNumber() const 
 476         return JSValueToNumber(context(), value(), /* exception */ 0); 
 484         return m_string
.isEmpty(); 
 487     Q_ASSERT_X(false, "toNumber()", "Not all states are included in the previous switch statement."); 
 488     return 0; // Avoid compiler warning. 
 491 bool QScriptValuePrivate::toBool() const 
 497         return JSValueToBoolean(context(), value()); 
 505         return m_string
.isEmpty(); 
 508     Q_ASSERT_X(false, "toBool()", "Not all states are included in the previous switch statement."); 
 509     return false; // Avoid compiler warning. 
 512 qsreal 
QScriptValuePrivate::toInteger() const 
 514     // TODO it is not true implementation! 
 518 qint32 
QScriptValuePrivate::toInt32() const 
 520     // TODO it is not true implementation! 
 524 quint32 
QScriptValuePrivate::toUInt32() const 
 526     // TODO it is not true implementation! 
 530 quint16 
QScriptValuePrivate::toUInt16() const 
 532     // TODO it is not true implementation! 
 537 bool QScriptValuePrivate::equals(QScriptValuePrivate
* other
) 
 539     if (!isValid() || !other
->isValid()) 
 542     if ((m_state 
== other
->m_state
) && !isJSBased()) { 
 544             return m_number 
== other
->m_number
; 
 545         return m_string 
== other
->m_string
; 
 548     if (isJSBased() && !other
->isJSBased()) { 
 549         if (!other
->assignEngine(engine())) { 
 550             qWarning("equals(): Cannot compare to a value created in a different engine"); 
 553     } else if (!isJSBased() && other
->isJSBased()) { 
 554         if (!other
->assignEngine(other
->engine())) { 
 555             qWarning("equals(): Cannot compare to a value created in a different engine"); 
 560     return JSValueIsEqual(context(), value(), other
->value(), /* exception */ 0); 
 563 bool QScriptValuePrivate::strictlyEquals(const QScriptValuePrivate
* other
) const 
 565     if (m_state 
!= other
->m_state
) 
 568         if (other
->engine() != engine()) { 
 569             qWarning("strictlyEquals(): Cannot compare to a value created in a different engine"); 
 572         return JSValueIsStrictEqual(context(), value(), other
->value()); 
 575         return m_string 
== other
->m_string
; 
 577         return m_number 
== other
->m_number
; 
 579     return false; // Invalid state. 
 583   Tries to assign \a engine to this value. Returns true on success; otherwise returns false. 
 585 bool QScriptValuePrivate::assignEngine(QScriptEnginePrivate
* engine
) 
 590         value 
= engine
->makeJSValue(static_cast<bool>(m_number
)); 
 593         value 
= engine
->makeJSValue(m_string
); 
 596         value 
= engine
->makeJSValue(m_number
); 
 599         value 
= engine
->makeJSValue(static_cast<QScriptValue::SpecialValue
>(m_number
)); 
 603             Q_ASSERT_X(!isJSBased(), "assignEngine()", "Not all states are included in the previous switch statement."); 
 605             qWarning("JSValue can't be rassigned to an another engine."); 
 614 QScriptValuePrivate
* QScriptValuePrivate::call(const QScriptValuePrivate
*, const QScriptValueList
& args
) 
 618         m_object 
= JSValueToObject(context(), value(), /* exception */ 0); 
 621             return new QScriptValuePrivate
; 
 627             // Convert all arguments and bind to the engine. 
 628             int argc 
= args
.size(); 
 629             QVarLengthArray
<JSValueRef
, 8> argv(argc
); 
 630             QScriptValueList::const_iterator i 
= args
.constBegin(); 
 631             for (int j 
= 0; i 
!= args
.constEnd(); j
++, i
++) { 
 632                 QScriptValuePrivate
* value 
= QScriptValuePrivate::get(*i
); 
 633                 if (!value
->assignEngine(engine())) { 
 634                     qWarning("QScriptValue::call() failed: cannot call function with values created in a different engine"); 
 635                     return new QScriptValuePrivate
; 
 637                 argv
[j
] = value
->value(); 
 641             JSValueRef exception 
= 0; 
 642             JSValueRef result 
= JSObjectCallAsFunction(context(), object(), /* thisObject */ 0, argc
, argv
.constData(), &exception
); 
 643             if (!result 
&& exception
) 
 644                 return new QScriptValuePrivate(engine(), exception
); 
 645             if (result 
&& !exception
) 
 646                 return new QScriptValuePrivate(engine(), result
); 
 648         // this QSV is not a function <-- !result && !exception. Fall-through. 
 650         return new QScriptValuePrivate
; 
 654 QScriptEnginePrivate
* QScriptValuePrivate::engine() const 
 656     // As long as m_engine is an autoinitializated pointer we can safely return it without 
 657     // checking current state. 
 658     return m_engine
.data(); 
 661 JSGlobalContextRef 
QScriptValuePrivate::context() const 
 663     Q_ASSERT(isJSBased()); 
 664     return m_engine
->context(); 
 667 JSValueRef 
QScriptValuePrivate::value() const 
 669     Q_ASSERT(isJSBased()); 
 673 JSObjectRef 
QScriptValuePrivate::object() const 
 675     Q_ASSERT(m_state 
== JSObject
); 
 679 void QScriptValuePrivate::setValue(JSValueRef value
) 
 682         JSValueUnprotect(context(), m_value
); 
 684         JSValueProtect(context(), value
); 
 690   Returns true if QSV is created from constructor with the given \a name, it has to be a 
 693 bool QScriptValuePrivate::inherits(const char* name
) 
 695     Q_ASSERT(isJSBased()); 
 696     JSObjectRef globalObject 
= JSContextGetGlobalObject(context()); 
 697     JSValueRef error 
= JSObjectGetProperty(context(), globalObject
, QScriptConverter::toString(name
), 0); 
 698     return JSValueIsInstanceOfConstructor(context(), value(), JSValueToObject(context(), error
, /* exception */ 0), /* exception */ 0); 
 703   Returns true if QSV have an engine associated. 
 705 bool QScriptValuePrivate::isJSBased() const { return m_state 
>= JSValue
; } 
 709   Returns true if current value of QSV is placed in m_number. 
 711 bool QScriptValuePrivate::isNumberBased() const { return !isJSBased() && !isStringBased() && m_state 
!= Invalid
; } 
 715   Returns true if current value of QSV is placed in m_string. 
 717 bool QScriptValuePrivate::isStringBased() const { return m_state 
== CString
; } 
 719 #endif // qscriptvalue_p_h