Home | History | Annotate | Download | only in api
      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