]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - qt/api/qscriptvalue_p.h
JavaScriptCore-621.1.tar.gz
[apple/javascriptcore.git] / qt / api / qscriptvalue_p.h
index 6a5b3884f34bf96f359f4d0bb91cbbeeef0b3d49..03e8621a3ef3fa90f1c468d10b9940baedeb5221 100644 (file)
@@ -24,6 +24,9 @@
 #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>
 
@@ -36,10 +39,10 @@ class QScriptValue;
 
   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,
@@ -51,7 +54,7 @@ class QScriptValue;
         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
@@ -74,12 +77,12 @@ public:
     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);
@@ -115,14 +118,14 @@ public:
 
 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;
@@ -134,6 +137,7 @@ private:
     inline void setValue(JSValueRef);
 
     inline bool inherits(const char*);
+    inline State refinedJSValue();
 
     inline bool isJSBased() const;
     inline bool isNumberBased() const;
@@ -206,94 +210,58 @@ QScriptValuePrivate::QScriptValuePrivate(QScriptValue::SpecialValue value)
 {
 }
 
-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)
@@ -302,6 +270,7 @@ QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, JSV
     , m_value(value)
 {
     Q_ASSERT(engine);
+    Q_ASSERT(value);
     JSValueProtect(context(), m_value);
 }
 
@@ -312,6 +281,8 @@ QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, JSV
     , m_object(object)
 {
     Q_ASSERT(engine);
+    Q_ASSERT(value);
+    Q_ASSERT(object);
     JSValueProtect(context(), m_value);
 }
 
@@ -323,10 +294,10 @@ bool QScriptValuePrivate::isBool()
     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;
@@ -337,12 +308,12 @@ bool QScriptValuePrivate::isNumber()
 {
     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;
@@ -355,10 +326,10 @@ bool QScriptValuePrivate::isNull()
     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;
@@ -371,10 +342,10 @@ bool QScriptValuePrivate::isString()
     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;
@@ -387,10 +358,10 @@ bool QScriptValuePrivate::isUndefined()
     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;
@@ -401,7 +372,7 @@ bool QScriptValuePrivate::isError()
 {
     switch (m_state) {
     case JSValue:
-        if (!isObject())
+        if (refinedJSValue() != JSObject)
             return false;
         // Fall-through.
     case JSObject:
@@ -414,14 +385,11 @@ bool QScriptValuePrivate::isError()
 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;
     }
@@ -431,10 +399,8 @@ bool QScriptValuePrivate::isFunction()
 {
     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());
@@ -453,13 +419,14 @@ QString QScriptValuePrivate::toString() const
     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.");
@@ -468,20 +435,30 @@ QString QScriptValuePrivate::toString() const
 
 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.");
@@ -492,17 +469,19 @@ bool QScriptValuePrivate::toBool() const
 {
     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.");
@@ -511,26 +490,37 @@ bool QScriptValuePrivate::toBool() const
 
 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();
 }
 
 
@@ -606,7 +596,7 @@ bool QScriptValuePrivate::assignEngine(QScriptEnginePrivate* engine)
         return false;
     }
     m_engine = engine;
-    m_state = JSNative;
+    m_state = JSPrimitive;
     setValue(value);
     return true;
 }
@@ -615,12 +605,8 @@ QScriptValuePrivate* QScriptValuePrivate::call(const QScriptValuePrivate*, const
 {
     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:
         {
@@ -694,10 +680,30 @@ bool QScriptValuePrivate::inherits(const char* name)
 {
     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.