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