2 Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 #ifndef qscriptvalue_p_h
21 #define qscriptvalue_p_h
23 #include "qscriptconverter_p.h"
24 #include "qscriptengine_p.h"
25 #include "qscriptvalue.h"
26 #include <JavaScriptCore/JavaScript.h>
27 #include <JavaScriptCore/JSRetainPtr.h>
28 #include <QtCore/qmath.h>
29 #include <QtCore/qnumeric.h>
30 #include <QtCore/qshareddata.h>
31 #include <QtCore/qvarlengtharray.h>
38 \class QScriptValuePrivate
40 Implementation of QScriptValue.
41 The implementation is based on a state machine. The states names are included in
42 QScriptValuePrivate::State. Each method should check for the current state and then perform a
46 Invalid -> QSVP is invalid, no assumptions should be made about class members (apart from m_value).
47 CString -> QSVP is created from QString or const char* and no JSC engine has been associated yet.
48 Current value is kept in m_string,
49 CNumber -> QSVP is created from int, uint, double... and no JSC engine has been bind yet. Current
50 value is kept in m_number
51 CBool -> QSVP is created from bool and no JSC engine has been associated yet. Current value is kept
53 CSpecial -> QSVP is Undefined or Null, but a JSC engine hasn't been associated yet, current value
54 is kept in m_number (cast of QScriptValue::SpecialValue)
55 JSValue -> QSVP is associated with engine, but there is no information about real type, the state
56 have really short live cycle. Normally it is created as a function call result.
57 JSPrimitive -> QSVP is associated with engine, and it is sure that it isn't a JavaScript object.
58 JSObject -> QSVP is associated with engine, and it is sure that it is a JavaScript object.
60 Each state keep all necessary information to invoke all methods, if not it should be changed to
61 a proper state. Changed state shouldn't be reverted.
64 class QScriptValuePrivate
: public QSharedData
{
66 inline static QScriptValuePrivate
* get(const QScriptValue
& q
);
67 inline static QScriptValue
get(const QScriptValuePrivate
* d
);
68 inline static QScriptValue
get(QScriptValuePrivate
* d
);
70 inline ~QScriptValuePrivate();
72 inline QScriptValuePrivate();
73 inline QScriptValuePrivate(const QString
& string
);
74 inline QScriptValuePrivate(bool value
);
75 inline QScriptValuePrivate(int number
);
76 inline QScriptValuePrivate(uint number
);
77 inline QScriptValuePrivate(qsreal number
);
78 inline QScriptValuePrivate(QScriptValue::SpecialValue value
);
80 inline QScriptValuePrivate(const QScriptEnginePrivate
* engine
, bool value
);
81 inline QScriptValuePrivate(const QScriptEnginePrivate
* engine
, int value
);
82 inline QScriptValuePrivate(const QScriptEnginePrivate
* engine
, uint value
);
83 inline QScriptValuePrivate(const QScriptEnginePrivate
* engine
, qsreal value
);
84 inline QScriptValuePrivate(const QScriptEnginePrivate
* engine
, const QString
& value
);
85 inline QScriptValuePrivate(const QScriptEnginePrivate
* engine
, QScriptValue::SpecialValue value
);
87 inline QScriptValuePrivate(const QScriptEnginePrivate
* engine
, JSValueRef value
);
88 inline QScriptValuePrivate(const QScriptEnginePrivate
* engine
, JSValueRef value
, JSObjectRef object
);
90 inline bool isValid() const;
92 inline bool isNumber();
94 inline bool isString();
95 inline bool isUndefined();
96 inline bool isError();
97 inline bool isObject();
98 inline bool isFunction();
100 inline QString
toString() const;
101 inline qsreal
toNumber() const;
102 inline bool toBool() const;
103 inline qsreal
toInteger() const;
104 inline qint32
toInt32() const;
105 inline quint32
toUInt32() const;
106 inline quint16
toUInt16() const;
108 inline bool equals(QScriptValuePrivate
* other
);
109 inline bool strictlyEquals(const QScriptValuePrivate
* other
) const;
110 inline bool assignEngine(QScriptEnginePrivate
* engine
);
112 inline QScriptValuePrivate
* call(const QScriptValuePrivate
* , const QScriptValueList
& args
);
114 inline JSGlobalContextRef
context() const;
115 inline JSValueRef
value() const;
116 inline JSObjectRef
object() const;
117 inline QScriptEnginePrivate
* engine() const;
120 // Please, update class documentation when you change the enum.
127 JSValue
= 0x2000, // JS values are equal or higher then this value.
131 QScriptEnginePtr m_engine
;
135 JSObjectRef m_object
;
137 inline void setValue(JSValueRef
);
139 inline bool inherits(const char*);
140 inline State
refinedJSValue();
142 inline bool isJSBased() const;
143 inline bool isNumberBased() const;
144 inline bool isStringBased() const;
147 QScriptValuePrivate
* QScriptValuePrivate::get(const QScriptValue
& q
) { return q
.d_ptr
.data(); }
149 QScriptValue
QScriptValuePrivate::get(const QScriptValuePrivate
* d
)
151 return QScriptValue(const_cast<QScriptValuePrivate
*>(d
));
154 QScriptValue
QScriptValuePrivate::get(QScriptValuePrivate
* d
)
156 return QScriptValue(d
);
159 QScriptValuePrivate::~QScriptValuePrivate()
162 JSValueUnprotect(context(), m_value
);
165 QScriptValuePrivate::QScriptValuePrivate()
171 QScriptValuePrivate::QScriptValuePrivate(const QString
& string
)
178 QScriptValuePrivate::QScriptValuePrivate(bool value
)
185 QScriptValuePrivate::QScriptValuePrivate(int number
)
192 QScriptValuePrivate::QScriptValuePrivate(uint number
)
199 QScriptValuePrivate::QScriptValuePrivate(qsreal number
)
206 QScriptValuePrivate::QScriptValuePrivate(QScriptValue::SpecialValue value
)
213 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate
* engine
, bool value
)
214 : m_state(JSPrimitive
)
215 , m_engine(const_cast<QScriptEnginePrivate
*>(engine
))
216 , m_value(engine
->makeJSValue(value
))
219 JSValueProtect(context(), m_value
);
222 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate
* engine
, int value
)
223 : m_state(JSPrimitive
)
224 , m_engine(const_cast<QScriptEnginePrivate
*>(engine
))
225 , m_value(m_engine
->makeJSValue(value
))
228 JSValueProtect(context(), m_value
);
231 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate
* engine
, uint value
)
232 : m_state(JSPrimitive
)
233 , m_engine(const_cast<QScriptEnginePrivate
*>(engine
))
234 , m_value(m_engine
->makeJSValue(value
))
237 JSValueProtect(context(), m_value
);
240 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate
* engine
, qsreal value
)
241 : m_state(JSPrimitive
)
242 , m_engine(const_cast<QScriptEnginePrivate
*>(engine
))
243 , m_value(m_engine
->makeJSValue(value
))
246 JSValueProtect(context(), m_value
);
249 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate
* engine
, const QString
& value
)
250 : m_state(JSPrimitive
)
251 , m_engine(const_cast<QScriptEnginePrivate
*>(engine
))
252 , m_value(m_engine
->makeJSValue(value
))
255 JSValueProtect(context(), m_value
);
258 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate
* engine
, QScriptValue::SpecialValue value
)
259 : m_state(JSPrimitive
)
260 , m_engine(const_cast<QScriptEnginePrivate
*>(engine
))
261 , m_value(m_engine
->makeJSValue(value
))
264 JSValueProtect(context(), m_value
);
267 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate
* engine
, JSValueRef value
)
269 , m_engine(const_cast<QScriptEnginePrivate
*>(engine
))
274 JSValueProtect(context(), m_value
);
277 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate
* engine
, JSValueRef value
, JSObjectRef object
)
279 , m_engine(const_cast<QScriptEnginePrivate
*>(engine
))
286 JSValueProtect(context(), m_value
);
289 bool QScriptValuePrivate::isValid() const { return m_state
!= Invalid
; }
291 bool QScriptValuePrivate::isBool()
297 if (refinedJSValue() != JSPrimitive
)
301 return JSValueIsBoolean(context(), value());
307 bool QScriptValuePrivate::isNumber()
313 if (refinedJSValue() != JSPrimitive
)
317 return JSValueIsNumber(context(), value());
323 bool QScriptValuePrivate::isNull()
327 return m_number
== static_cast<int>(QScriptValue::NullValue
);
329 if (refinedJSValue() != JSPrimitive
)
333 return JSValueIsNull(context(), value());
339 bool QScriptValuePrivate::isString()
345 if (refinedJSValue() != JSPrimitive
)
349 return JSValueIsString(context(), value());
355 bool QScriptValuePrivate::isUndefined()
359 return m_number
== static_cast<int>(QScriptValue::UndefinedValue
);
361 if (refinedJSValue() != JSPrimitive
)
365 return JSValueIsUndefined(context(), value());
371 bool QScriptValuePrivate::isError()
375 if (refinedJSValue() != JSObject
)
379 return inherits("Error");
385 bool QScriptValuePrivate::isObject()
389 return refinedJSValue() == JSObject
;
398 bool QScriptValuePrivate::isFunction()
402 if (refinedJSValue() != JSObject
)
406 return JSObjectIsFunction(context(), object());
412 QString
QScriptValuePrivate::toString() const
418 return m_number
? QString::fromLatin1("true") : QString::fromLatin1("false");
422 return QScriptConverter::toString(m_number
);
424 return m_number
== QScriptValue::NullValue
? QString::fromLatin1("null") : QString::fromLatin1("undefined");
428 JSRetainPtr
<JSStringRef
> ptr(Adopt
, JSValueToStringCopy(context(), value(), /* exception */ 0));
429 return QScriptConverter::toString(ptr
.get());
432 Q_ASSERT_X(false, "toString()", "Not all states are included in the previous switch statement.");
433 return QString(); // Avoid compiler warning.
436 qsreal
QScriptValuePrivate::toNumber() const
442 return JSValueToNumber(context(), value(), /* exception */ 0);
446 return m_number
? 1 : 0;
450 return m_number
== QScriptValue::NullValue
? 0 : qQNaN();
453 qsreal result
= m_string
.toDouble(&ok
);
456 result
= m_string
.toInt(&ok
, 0); // Try other bases.
459 if (m_string
== "Infinity" || m_string
== "-Infinity")
461 return m_string
.length() ? qQNaN() : 0;
464 Q_ASSERT_X(false, "toNumber()", "Not all states are included in the previous switch statement.");
465 return 0; // Avoid compiler warning.
468 bool QScriptValuePrivate::toBool() const
473 return JSValueToBoolean(context(), value());
477 return !(qIsNaN(m_number
) || !m_number
);
484 return m_string
.length();
487 Q_ASSERT_X(false, "toBool()", "Not all states are included in the previous switch statement.");
488 return false; // Avoid compiler warning.
491 qsreal
QScriptValuePrivate::toInteger() const
493 qsreal result
= toNumber();
498 return (result
> 0) ? qFloor(result
) : -1 * qFloor(-result
);
501 qint32
QScriptValuePrivate::toInt32() const
503 qsreal result
= toInteger();
504 // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but
505 // some of these operation are invoked in toInteger subcall.
511 quint32
QScriptValuePrivate::toUInt32() const
513 qsreal result
= toInteger();
514 // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but
515 // some of these operation are invoked in toInteger subcall.
521 quint16
QScriptValuePrivate::toUInt16() const
527 bool QScriptValuePrivate::equals(QScriptValuePrivate
* other
)
529 if (!isValid() || !other
->isValid())
532 if ((m_state
== other
->m_state
) && !isJSBased()) {
534 return m_number
== other
->m_number
;
535 return m_string
== other
->m_string
;
538 if (isJSBased() && !other
->isJSBased()) {
539 if (!other
->assignEngine(engine())) {
540 qWarning("equals(): Cannot compare to a value created in a different engine");
543 } else if (!isJSBased() && other
->isJSBased()) {
544 if (!other
->assignEngine(other
->engine())) {
545 qWarning("equals(): Cannot compare to a value created in a different engine");
550 return JSValueIsEqual(context(), value(), other
->value(), /* exception */ 0);
553 bool QScriptValuePrivate::strictlyEquals(const QScriptValuePrivate
* other
) const
555 if (m_state
!= other
->m_state
)
558 if (other
->engine() != engine()) {
559 qWarning("strictlyEquals(): Cannot compare to a value created in a different engine");
562 return JSValueIsStrictEqual(context(), value(), other
->value());
565 return m_string
== other
->m_string
;
567 return m_number
== other
->m_number
;
569 return false; // Invalid state.
573 Tries to assign \a engine to this value. Returns true on success; otherwise returns false.
575 bool QScriptValuePrivate::assignEngine(QScriptEnginePrivate
* engine
)
580 value
= engine
->makeJSValue(static_cast<bool>(m_number
));
583 value
= engine
->makeJSValue(m_string
);
586 value
= engine
->makeJSValue(m_number
);
589 value
= engine
->makeJSValue(static_cast<QScriptValue::SpecialValue
>(m_number
));
593 Q_ASSERT_X(!isJSBased(), "assignEngine()", "Not all states are included in the previous switch statement.");
595 qWarning("JSValue can't be rassigned to an another engine.");
599 m_state
= JSPrimitive
;
604 QScriptValuePrivate
* QScriptValuePrivate::call(const QScriptValuePrivate
*, const QScriptValueList
& args
)
608 if (refinedJSValue() != JSObject
)
609 return new QScriptValuePrivate
;
613 // Convert all arguments and bind to the engine.
614 int argc
= args
.size();
615 QVarLengthArray
<JSValueRef
, 8> argv(argc
);
616 QScriptValueList::const_iterator i
= args
.constBegin();
617 for (int j
= 0; i
!= args
.constEnd(); j
++, i
++) {
618 QScriptValuePrivate
* value
= QScriptValuePrivate::get(*i
);
619 if (!value
->assignEngine(engine())) {
620 qWarning("QScriptValue::call() failed: cannot call function with values created in a different engine");
621 return new QScriptValuePrivate
;
623 argv
[j
] = value
->value();
627 JSValueRef exception
= 0;
628 JSValueRef result
= JSObjectCallAsFunction(context(), object(), /* thisObject */ 0, argc
, argv
.constData(), &exception
);
629 if (!result
&& exception
)
630 return new QScriptValuePrivate(engine(), exception
);
631 if (result
&& !exception
)
632 return new QScriptValuePrivate(engine(), result
);
634 // this QSV is not a function <-- !result && !exception. Fall-through.
636 return new QScriptValuePrivate
;
640 QScriptEnginePrivate
* QScriptValuePrivate::engine() const
642 // As long as m_engine is an autoinitializated pointer we can safely return it without
643 // checking current state.
644 return m_engine
.data();
647 JSGlobalContextRef
QScriptValuePrivate::context() const
649 Q_ASSERT(isJSBased());
650 return m_engine
->context();
653 JSValueRef
QScriptValuePrivate::value() const
655 Q_ASSERT(isJSBased());
659 JSObjectRef
QScriptValuePrivate::object() const
661 Q_ASSERT(m_state
== JSObject
);
665 void QScriptValuePrivate::setValue(JSValueRef value
)
668 JSValueUnprotect(context(), m_value
);
670 JSValueProtect(context(), value
);
676 Returns true if QSV is created from constructor with the given \a name, it has to be a
679 bool QScriptValuePrivate::inherits(const char* name
)
681 Q_ASSERT(isJSBased());
682 JSObjectRef globalObject
= JSContextGetGlobalObject(context());
683 JSStringRef errorAttrName
= QScriptConverter::toString(name
);
684 JSValueRef error
= JSObjectGetProperty(context(), globalObject
, errorAttrName
, /* exception */ 0);
685 JSStringRelease(errorAttrName
);
686 return JSValueIsInstanceOfConstructor(context(), value(), JSValueToObject(context(), error
, /* exception */ 0), /* exception */ 0);
691 Refines the state of this QScriptValuePrivate. Returns the new state.
693 QScriptValuePrivate::State
QScriptValuePrivate::refinedJSValue()
695 Q_ASSERT(m_state
== JSValue
);
696 if (!JSValueIsObject(context(), value())) {
697 m_state
= JSPrimitive
;
700 // We are sure that value is an JSObject, so we can const_cast safely without
701 // calling JSC C API (JSValueToObject(context(), value(), /* exceptions */ 0)).
702 m_object
= const_cast<JSObjectRef
>(m_value
);
709 Returns true if QSV have an engine associated.
711 bool QScriptValuePrivate::isJSBased() const { return m_state
>= JSValue
; }
715 Returns true if current value of QSV is placed in m_number.
717 bool QScriptValuePrivate::isNumberBased() const { return !isJSBased() && !isStringBased() && m_state
!= Invalid
; }
721 Returns true if current value of QSV is placed in m_string.
723 bool QScriptValuePrivate::isStringBased() const { return m_state
== CString
; }
725 #endif // qscriptvalue_p_h