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
35class QScriptEngine;
36class 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
70class QScriptValuePrivate : public QSharedData {
71public:
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
158private:
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
197QScriptValuePrivate* QScriptValuePrivate::get(const QScriptValue& q) { return q.d_ptr.data(); }
198
199QScriptValue QScriptValuePrivate::get(const QScriptValuePrivate* d)
200{
201    return QScriptValue(const_cast<QScriptValuePrivate*>(d));
202}
203
204QScriptValue QScriptValuePrivate::get(QScriptValuePrivate* d)
205{
206    return QScriptValue(d);
207}
208
209QScriptValuePrivate::~QScriptValuePrivate()
210{
211    if (isJSBased())
212        JSValueUnprotect(*m_engine, u.m_value);
213    else if (isStringBased())
214        delete u.m_string;
215}
216
217QScriptValuePrivate::QScriptValuePrivate()
218    : m_state(Invalid)
219{
220}
221
222QScriptValuePrivate::QScriptValuePrivate(const QString& string)
223    : m_state(CString)
224    , u(new QString(string))
225{
226}
227
228QScriptValuePrivate::QScriptValuePrivate(bool value)
229    : m_state(CBool)
230    , u(value)
231{
232}
233
234QScriptValuePrivate::QScriptValuePrivate(int number)
235    : m_state(CNumber)
236    , u(number)
237{
238}
239
240QScriptValuePrivate::QScriptValuePrivate(uint number)
241    : m_state(CNumber)
242    , u(number)
243{
244}
245
246QScriptValuePrivate::QScriptValuePrivate(qsreal number)
247    : m_state(CNumber)
248    , u(number)
249{
250}
251
252QScriptValuePrivate::QScriptValuePrivate(QScriptValue::SpecialValue value)
253    : m_state(value == QScriptValue::NullValue ? CNull : CUndefined)
254{
255}
256
257QScriptValuePrivate::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
266QScriptValuePrivate::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
275QScriptValuePrivate::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
284QScriptValuePrivate::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
293QScriptValuePrivate::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
302QScriptValuePrivate::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
311QScriptValuePrivate::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
321QScriptValuePrivate::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
331bool QScriptValuePrivate::isValid() const { return m_state != Invalid; }
332
333bool 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
349bool 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
365bool 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
381bool 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
397bool 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
413bool 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
427bool 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
440bool 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
454bool 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
468bool 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
482QString 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
510qsreal 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
548bool 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
572qsreal 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
582qint32 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
592quint32 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
602quint16 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  */
614QScriptValuePrivate* 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 */
673QScriptValuePrivate* 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
682QDateTime 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
700inline 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
713inline 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
729bool 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
797bool 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
834inline 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*/
847bool 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
882inline 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
888inline QScriptValuePrivate* QScriptValuePrivate::property(const QScriptStringPrivate* name, const QScriptValue::ResolveFlags& mode)
889{
890    return property<JSStringRef>(*name, mode);
891}
892
893inline 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*/
902inline 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*/
911inline 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*/
921inline 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*/
934inline 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*/
945template<typename T>
946inline 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
966inline 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
972inline void QScriptValuePrivate::setProperty(quint32 arrayIndex, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags)
973{
974    setProperty<quint32>(arrayIndex, value, flags);
975}
976
977inline 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*/
986inline 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*/
1003inline 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*/
1014inline 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*/
1025inline void QScriptValuePrivate::deleteProperty(JSStringRef property, JSValueRef* exception)
1026{
1027    Q_ASSERT(isObject());
1028    JSObjectDeleteProperty(*m_engine, *this, property, exception);
1029}
1030
1031template<typename T>
1032inline 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
1056inline 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
1062inline QScriptValue::PropertyFlags QScriptValuePrivate::propertyFlags(const QScriptStringPrivate* name, const QScriptValue::ResolveFlags& mode)
1063{
1064    return propertyFlags(*name, mode);
1065}
1066
1067inline 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
1126QScriptValuePrivate* 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
1164QScriptEnginePrivate* 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
1171QScriptValuePrivate::operator JSValueRef() const
1172{
1173    Q_ASSERT(isJSBased());
1174    Q_ASSERT(u.m_value);
1175    return u.m_value;
1176}
1177
1178QScriptValuePrivate::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*/
1189QScriptValuePrivate::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*/
1206bool 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*/
1212bool 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*/
1218bool QScriptValuePrivate::isStringBased() const { return m_state == CString; }
1219
1220#endif // qscriptvalue_p_h
1221