#include "qscriptengine_p.h"
#include "qscriptvalue.h"
#include <JavaScriptCore/JavaScript.h>
+#include <JavaScriptCore/JSRetainPtr.h>
+#include <QtCore/qmath.h>
+#include <QtCore/qnumeric.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qvarlengtharray.h>
Implementation of QScriptValue.
The implementation is based on a state machine. The states names are included in
- QScriptValuePrivate::States. Each method should check for the current state and then perform a
+ QScriptValuePrivate::State. Each method should check for the current state and then perform a
correct action.
- States:
+ State:
Invalid -> QSVP is invalid, no assumptions should be made about class members (apart from m_value).
CString -> QSVP is created from QString or const char* and no JSC engine has been associated yet.
Current value is kept in m_string,
is kept in m_number (cast of QScriptValue::SpecialValue)
JSValue -> QSVP is associated with engine, but there is no information about real type, the state
have really short live cycle. Normally it is created as a function call result.
- JSNative -> QSVP is associated with engine, and it is sure that it isn't a JavaScript object.
+ JSPrimitive -> QSVP is associated with engine, and it is sure that it isn't a JavaScript object.
JSObject -> QSVP is associated with engine, and it is sure that it is a JavaScript object.
Each state keep all necessary information to invoke all methods, if not it should be changed to
inline QScriptValuePrivate(qsreal number);
inline QScriptValuePrivate(QScriptValue::SpecialValue value);
- inline QScriptValuePrivate(const QScriptEngine* engine, bool value);
- inline QScriptValuePrivate(const QScriptEngine* engine, int value);
- inline QScriptValuePrivate(const QScriptEngine* engine, uint value);
- inline QScriptValuePrivate(const QScriptEngine* engine, qsreal value);
- inline QScriptValuePrivate(const QScriptEngine* engine, const QString& value);
- inline QScriptValuePrivate(const QScriptEngine* engine, QScriptValue::SpecialValue value);
+ inline QScriptValuePrivate(const QScriptEnginePrivate* engine, bool value);
+ inline QScriptValuePrivate(const QScriptEnginePrivate* engine, int value);
+ inline QScriptValuePrivate(const QScriptEnginePrivate* engine, uint value);
+ inline QScriptValuePrivate(const QScriptEnginePrivate* engine, qsreal value);
+ inline QScriptValuePrivate(const QScriptEnginePrivate* engine, const QString& value);
+ inline QScriptValuePrivate(const QScriptEnginePrivate* engine, QScriptValue::SpecialValue value);
inline QScriptValuePrivate(const QScriptEnginePrivate* engine, JSValueRef value);
inline QScriptValuePrivate(const QScriptEnginePrivate* engine, JSValueRef value, JSObjectRef object);
private:
// Please, update class documentation when you change the enum.
- enum States {
+ enum State {
Invalid = 0,
CString = 0x1000,
CNumber,
CBool,
CSpecial,
JSValue = 0x2000, // JS values are equal or higher then this value.
- JSNative,
+ JSPrimitive,
JSObject
} m_state;
QScriptEnginePtr m_engine;
inline void setValue(JSValueRef);
inline bool inherits(const char*);
+ inline State refinedJSValue();
inline bool isJSBased() const;
inline bool isNumberBased() const;
{
}
-QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, bool value)
- : m_state(JSNative)
+QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, bool value)
+ : m_state(JSPrimitive)
+ , m_engine(const_cast<QScriptEnginePrivate*>(engine))
+ , m_value(engine->makeJSValue(value))
{
- if (!engine) {
- // slower path reinitialization
- m_state = CBool;
- m_number = value;
- m_value = 0;
- } else {
- m_engine = QScriptEnginePrivate::get(engine);
- m_value = m_engine->makeJSValue(value);
- JSValueProtect(context(), m_value);
- }
+ Q_ASSERT(engine);
+ JSValueProtect(context(), m_value);
}
-QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, int value)
- : m_state(JSNative)
+QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, int value)
+ : m_state(JSPrimitive)
+ , m_engine(const_cast<QScriptEnginePrivate*>(engine))
+ , m_value(m_engine->makeJSValue(value))
{
- if (!engine) {
- // slower path reinitialization
- m_state = CNumber;
- m_number = value;
- m_value = 0;
- } else {
- m_engine = QScriptEnginePrivate::get(engine);
- m_value = m_engine->makeJSValue(value);
- JSValueProtect(context(), m_value);
- }
+ Q_ASSERT(engine);
+ JSValueProtect(context(), m_value);
}
-QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, uint value)
- : m_state(JSNative)
+QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, uint value)
+ : m_state(JSPrimitive)
+ , m_engine(const_cast<QScriptEnginePrivate*>(engine))
+ , m_value(m_engine->makeJSValue(value))
{
- if (!engine) {
- // slower path reinitialization
- m_state = CNumber;
- m_number = value;
- m_value = 0;
- } else {
- m_engine = QScriptEnginePrivate::get(engine);
- m_value = m_engine->makeJSValue(value);
- JSValueProtect(context(), m_value);
- }
+ Q_ASSERT(engine);
+ JSValueProtect(context(), m_value);
}
-QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, qsreal value)
- : m_state(JSNative)
+QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, qsreal value)
+ : m_state(JSPrimitive)
+ , m_engine(const_cast<QScriptEnginePrivate*>(engine))
+ , m_value(m_engine->makeJSValue(value))
{
- if (!engine) {
- // slower path reinitialization
- m_state = CNumber;
- m_number = value;
- m_value = 0;
- } else {
- m_engine = QScriptEnginePrivate::get(engine);
- m_value = m_engine->makeJSValue(value);
- JSValueProtect(context(), m_value);
- }
+ Q_ASSERT(engine);
+ JSValueProtect(context(), m_value);
}
-QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, const QString& value)
- : m_state(JSNative)
+QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, const QString& value)
+ : m_state(JSPrimitive)
+ , m_engine(const_cast<QScriptEnginePrivate*>(engine))
+ , m_value(m_engine->makeJSValue(value))
{
- if (!engine) {
- // slower path reinitialization
- m_state = CString;
- m_string = value;
- m_value = 0;
- } else {
- m_engine = QScriptEnginePrivate::get(engine);
- m_value = m_engine->makeJSValue(value);
- JSValueProtect(context(), m_value);
- }
+ Q_ASSERT(engine);
+ JSValueProtect(context(), m_value);
}
-QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, QScriptValue::SpecialValue value)
- : m_state(JSNative)
+QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, QScriptValue::SpecialValue value)
+ : m_state(JSPrimitive)
+ , m_engine(const_cast<QScriptEnginePrivate*>(engine))
+ , m_value(m_engine->makeJSValue(value))
{
- if (!engine) {
- // slower path reinitialization
- m_state = CSpecial;
- m_number = value;
- m_value = 0;
- } else {
- m_engine = QScriptEnginePrivate::get(engine);
- m_value = m_engine->makeJSValue(value);
- JSValueProtect(context(), m_value);
- }
+ Q_ASSERT(engine);
+ JSValueProtect(context(), m_value);
}
QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, JSValueRef value)
, m_value(value)
{
Q_ASSERT(engine);
+ Q_ASSERT(value);
JSValueProtect(context(), m_value);
}
, m_object(object)
{
Q_ASSERT(engine);
+ Q_ASSERT(value);
+ Q_ASSERT(object);
JSValueProtect(context(), m_value);
}
case CBool:
return true;
case JSValue:
- if (isObject())
+ if (refinedJSValue() != JSPrimitive)
return false;
// Fall-through.
- case JSNative:
+ case JSPrimitive:
return JSValueIsBoolean(context(), value());
default:
return false;
{
switch (m_state) {
case CNumber:
- return m_number;
+ return true;
case JSValue:
- if (isObject())
+ if (refinedJSValue() != JSPrimitive)
return false;
// Fall-through.
- case JSNative:
+ case JSPrimitive:
return JSValueIsNumber(context(), value());
default:
return false;
case CSpecial:
return m_number == static_cast<int>(QScriptValue::NullValue);
case JSValue:
- if (isObject())
+ if (refinedJSValue() != JSPrimitive)
return false;
// Fall-through.
- case JSNative:
+ case JSPrimitive:
return JSValueIsNull(context(), value());
default:
return false;
case CString:
return true;
case JSValue:
- if (isObject())
+ if (refinedJSValue() != JSPrimitive)
return false;
// Fall-through.
- case JSNative:
+ case JSPrimitive:
return JSValueIsString(context(), value());
default:
return false;
case CSpecial:
return m_number == static_cast<int>(QScriptValue::UndefinedValue);
case JSValue:
- if (isObject())
+ if (refinedJSValue() != JSPrimitive)
return false;
// Fall-through.
- case JSNative:
+ case JSPrimitive:
return JSValueIsUndefined(context(), value());
default:
return false;
{
switch (m_state) {
case JSValue:
- if (!isObject())
+ if (refinedJSValue() != JSObject)
return false;
// Fall-through.
case JSObject:
bool QScriptValuePrivate::isObject()
{
switch (m_state) {
- case JSObject:
- return true;
case JSValue:
- m_object = JSValueToObject(context(), value(), /* exception */ 0);
- if (!m_object)
- return false;
- m_state = JSObject;
+ return refinedJSValue() == JSObject;
+ case JSObject:
return true;
+
default:
return false;
}
{
switch (m_state) {
case JSValue:
- m_object = JSValueToObject(context(), value(), /* exception */ 0);
- if (!m_object)
+ if (refinedJSValue() != JSObject)
return false;
- m_state = JSObject;
// Fall-through.
case JSObject:
return JSObjectIsFunction(context(), object());
case CString:
return m_string;
case CNumber:
- return QString::number(m_number);
+ return QScriptConverter::toString(m_number);
case CSpecial:
return m_number == QScriptValue::NullValue ? QString::fromLatin1("null") : QString::fromLatin1("undefined");
case JSValue:
- case JSNative:
+ case JSPrimitive:
case JSObject:
- return QScriptConverter::toString(JSValueToStringCopy(context(), value(), /* exception */ 0));
+ JSRetainPtr<JSStringRef> ptr(Adopt, JSValueToStringCopy(context(), value(), /* exception */ 0));
+ return QScriptConverter::toString(ptr.get());
}
Q_ASSERT_X(false, "toString()", "Not all states are included in the previous switch statement.");
qsreal QScriptValuePrivate::toNumber() const
{
- // TODO Check it.
switch (m_state) {
case JSValue:
- case JSNative:
+ case JSPrimitive:
case JSObject:
return JSValueToNumber(context(), value(), /* exception */ 0);
case CNumber:
- case CBool:
return m_number;
+ case CBool:
+ return m_number ? 1 : 0;
case Invalid:
+ return 0;
case CSpecial:
- return false;
+ return m_number == QScriptValue::NullValue ? 0 : qQNaN();
case CString:
- return m_string.isEmpty();
+ bool ok;
+ qsreal result = m_string.toDouble(&ok);
+ if (ok)
+ return result;
+ result = m_string.toInt(&ok, 0); // Try other bases.
+ if (ok)
+ return result;
+ if (m_string == "Infinity" || m_string == "-Infinity")
+ return qInf();
+ return m_string.length() ? qQNaN() : 0;
}
Q_ASSERT_X(false, "toNumber()", "Not all states are included in the previous switch statement.");
{
switch (m_state) {
case JSValue:
- case JSNative:
- case JSObject:
+ case JSPrimitive:
return JSValueToBoolean(context(), value());
+ case JSObject:
+ return true;
case CNumber:
+ return !(qIsNaN(m_number) || !m_number);
case CBool:
return m_number;
case Invalid:
case CSpecial:
return false;
case CString:
- return m_string.isEmpty();
+ return m_string.length();
}
Q_ASSERT_X(false, "toBool()", "Not all states are included in the previous switch statement.");
qsreal QScriptValuePrivate::toInteger() const
{
- // TODO it is not true implementation!
- return toNumber();
+ qsreal result = toNumber();
+ if (qIsNaN(result))
+ return 0;
+ if (qIsInf(result))
+ return result;
+ return (result > 0) ? qFloor(result) : -1 * qFloor(-result);
}
qint32 QScriptValuePrivate::toInt32() const
{
- // TODO it is not true implementation!
- return toNumber();
+ qsreal result = toInteger();
+ // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but
+ // some of these operation are invoked in toInteger subcall.
+ if (qIsInf(result))
+ return 0;
+ return result;
}
quint32 QScriptValuePrivate::toUInt32() const
{
- // TODO it is not true implementation!
- return toNumber();
+ qsreal result = toInteger();
+ // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but
+ // some of these operation are invoked in toInteger subcall.
+ if (qIsInf(result))
+ return 0;
+ return result;
}
quint16 QScriptValuePrivate::toUInt16() const
{
- // TODO it is not true implementation!
- return toNumber();
+ return toInt32();
}
return false;
}
m_engine = engine;
- m_state = JSNative;
+ m_state = JSPrimitive;
setValue(value);
return true;
}
{
switch (m_state) {
case JSValue:
- m_object = JSValueToObject(context(), value(), /* exception */ 0);
- if (!object()) {
- m_state = JSValue;
+ if (refinedJSValue() != JSObject)
return new QScriptValuePrivate;
- }
- m_state = JSObject;
// Fall-through.
case JSObject:
{
{
Q_ASSERT(isJSBased());
JSObjectRef globalObject = JSContextGetGlobalObject(context());
- JSValueRef error = JSObjectGetProperty(context(), globalObject, QScriptConverter::toString(name), 0);
+ JSStringRef errorAttrName = QScriptConverter::toString(name);
+ JSValueRef error = JSObjectGetProperty(context(), globalObject, errorAttrName, /* exception */ 0);
+ JSStringRelease(errorAttrName);
return JSValueIsInstanceOfConstructor(context(), value(), JSValueToObject(context(), error, /* exception */ 0), /* exception */ 0);
}
+/*!
+ \internal
+ Refines the state of this QScriptValuePrivate. Returns the new state.
+*/
+QScriptValuePrivate::State QScriptValuePrivate::refinedJSValue()
+{
+ Q_ASSERT(m_state == JSValue);
+ if (!JSValueIsObject(context(), value())) {
+ m_state = JSPrimitive;
+ } else {
+ m_state = JSObject;
+ // We are sure that value is an JSObject, so we can const_cast safely without
+ // calling JSC C API (JSValueToObject(context(), value(), /* exceptions */ 0)).
+ m_object = const_cast<JSObjectRef>(m_value);
+ }
+ return m_state;
+}
+
/*!
\internal
Returns true if QSV have an engine associated.