diff options
Diffstat (limited to 'Source/JavaScriptCore/qt/api/qscriptvalue_p.h')
-rw-r--r-- | Source/JavaScriptCore/qt/api/qscriptvalue_p.h | 1220 |
1 files changed, 1220 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/qt/api/qscriptvalue_p.h b/Source/JavaScriptCore/qt/api/qscriptvalue_p.h new file mode 100644 index 0000000..f98a06a --- /dev/null +++ b/Source/JavaScriptCore/qt/api/qscriptvalue_p.h @@ -0,0 +1,1220 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef qscriptvalue_p_h +#define qscriptvalue_p_h + +#include "qscriptconverter_p.h" +#include "qscriptengine_p.h" +#include "qscriptvalue.h" +#include <JavaScriptCore/JavaScript.h> +#include <JavaScriptCore/JSRetainPtr.h> +#include <JSObjectRefPrivate.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qmath.h> +#include <QtCore/qnumeric.h> +#include <QtCore/qshareddata.h> +#include <QtCore/qvarlengtharray.h> + +class QScriptEngine; +class QScriptValue; + +/* + \internal + \class QScriptValuePrivate + + Implementation of QScriptValue. + The implementation is based on a state machine. The states names are included in + QScriptValuePrivate::State. Each method should check for the current state and then perform a + correct action. + + 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, + CNumber -> QSVP is created from int, uint, double... and no JSC engine has been bind yet. Current + value is kept in m_number + CBool -> QSVP is created from bool and no JSC engine has been associated yet. Current value is kept + in m_bool + CNull -> QSVP is null, but a JSC engine hasn't been associated yet. + CUndefined -> QSVP is undefined, but a JSC engine hasn't been associated yet. + 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. + 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 + a proper state. Changed state shouldn't be reverted. + + The QScriptValuePrivate use the JSC C API directly. The QSVP type is equal to combination of + the JSValueRef and the JSObjectRef, and it could be automatically casted to these types by cast + operators. +*/ + +class QScriptValuePrivate : public QSharedData { +public: + inline static QScriptValuePrivate* get(const QScriptValue& q); + inline static QScriptValue get(const QScriptValuePrivate* d); + inline static QScriptValue get(QScriptValuePrivate* d); + + inline ~QScriptValuePrivate(); + + inline QScriptValuePrivate(); + inline QScriptValuePrivate(const QString& string); + inline QScriptValuePrivate(bool value); + inline QScriptValuePrivate(int number); + inline QScriptValuePrivate(uint number); + inline QScriptValuePrivate(qsreal number); + inline QScriptValuePrivate(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, JSObjectRef object); + + inline bool isValid() const; + inline bool isBool(); + inline bool isNumber(); + inline bool isNull(); + inline bool isString(); + inline bool isUndefined(); + inline bool isError(); + inline bool isObject(); + inline bool isFunction(); + inline bool isArray(); + inline bool isDate(); + + inline QString toString() const; + inline qsreal toNumber() const; + inline bool toBool() const; + inline qsreal toInteger() const; + inline qint32 toInt32() const; + inline quint32 toUInt32() const; + inline quint16 toUInt16() const; + + inline QScriptValuePrivate* toObject(QScriptEnginePrivate* engine); + inline QScriptValuePrivate* toObject(); + inline QDateTime toDateTime(); + inline QScriptValuePrivate* prototype(); + inline void setPrototype(QScriptValuePrivate* prototype); + + inline bool equals(QScriptValuePrivate* other); + inline bool strictlyEquals(QScriptValuePrivate* other); + inline bool instanceOf(QScriptValuePrivate* other); + inline bool assignEngine(QScriptEnginePrivate* engine); + + inline QScriptValuePrivate* property(const QString& name, const QScriptValue::ResolveFlags& mode); + inline QScriptValuePrivate* property(const QScriptStringPrivate* name, const QScriptValue::ResolveFlags& mode); + inline QScriptValuePrivate* property(quint32 arrayIndex, const QScriptValue::ResolveFlags& mode); + inline JSValueRef property(quint32 property, JSValueRef* exception); + inline JSValueRef property(JSStringRef property, JSValueRef* exception); + inline bool hasOwnProperty(quint32 property); + inline bool hasOwnProperty(JSStringRef property); + template<typename T> + inline QScriptValuePrivate* property(T name, const QScriptValue::ResolveFlags& mode); + + inline void setProperty(const QString& name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags); + inline void setProperty(const QScriptStringPrivate* name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags); + inline void setProperty(const quint32 indexArray, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags); + inline void setProperty(quint32 property, JSValueRef value, JSPropertyAttributes flags, JSValueRef* exception); + inline void setProperty(JSStringRef property, JSValueRef value, JSPropertyAttributes flags, JSValueRef* exception); + inline void deleteProperty(quint32 property, JSValueRef* exception); + inline void deleteProperty(JSStringRef property, JSValueRef* exception); + template<typename T> + inline void setProperty(T name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags); + + QScriptValue::PropertyFlags propertyFlags(const QString& name, const QScriptValue::ResolveFlags& mode); + QScriptValue::PropertyFlags propertyFlags(const QScriptStringPrivate* name, const QScriptValue::ResolveFlags& mode); + QScriptValue::PropertyFlags propertyFlags(const JSStringRef name, const QScriptValue::ResolveFlags& mode); + + inline QScriptValuePrivate* call(const QScriptValuePrivate* , const QScriptValueList& args); + + inline operator JSValueRef() const; + inline operator JSObjectRef() const; + + inline QScriptEnginePrivate* engine() const; + +private: + // Please, update class documentation when you change the enum. + enum State { + Invalid = 0, + CString = 0x1000, + CNumber, + CBool, + CNull, + CUndefined, + JSValue = 0x2000, // JS values are equal or higher then this value. + JSPrimitive, + JSObject + } m_state; + QScriptEnginePtr m_engine; + union Value + { + bool m_bool; + qsreal m_number; + JSValueRef m_value; + JSObjectRef m_object; + QString* m_string; + + Value() : m_number(0) {} + Value(bool value) : m_bool(value) {} + Value(int number) : m_number(number) {} + Value(uint number) : m_number(number) {} + Value(qsreal number) : m_number(number) {} + Value(JSValueRef value) : m_value(value) {} + Value(JSObjectRef object) : m_object(object) {} + Value(QString* string) : m_string(string) {} + } u; + + inline State refinedJSValue(); + + inline bool isJSBased() const; + inline bool isNumberBased() const; + inline bool isStringBased() const; +}; + +QScriptValuePrivate* QScriptValuePrivate::get(const QScriptValue& q) { return q.d_ptr.data(); } + +QScriptValue QScriptValuePrivate::get(const QScriptValuePrivate* d) +{ + return QScriptValue(const_cast<QScriptValuePrivate*>(d)); +} + +QScriptValue QScriptValuePrivate::get(QScriptValuePrivate* d) +{ + return QScriptValue(d); +} + +QScriptValuePrivate::~QScriptValuePrivate() +{ + if (isJSBased()) + JSValueUnprotect(*m_engine, u.m_value); + else if (isStringBased()) + delete u.m_string; +} + +QScriptValuePrivate::QScriptValuePrivate() + : m_state(Invalid) +{ +} + +QScriptValuePrivate::QScriptValuePrivate(const QString& string) + : m_state(CString) + , u(new QString(string)) +{ +} + +QScriptValuePrivate::QScriptValuePrivate(bool value) + : m_state(CBool) + , u(value) +{ +} + +QScriptValuePrivate::QScriptValuePrivate(int number) + : m_state(CNumber) + , u(number) +{ +} + +QScriptValuePrivate::QScriptValuePrivate(uint number) + : m_state(CNumber) + , u(number) +{ +} + +QScriptValuePrivate::QScriptValuePrivate(qsreal number) + : m_state(CNumber) + , u(number) +{ +} + +QScriptValuePrivate::QScriptValuePrivate(QScriptValue::SpecialValue value) + : m_state(value == QScriptValue::NullValue ? CNull : CUndefined) +{ +} + +QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, bool value) + : m_state(JSPrimitive) + , m_engine(const_cast<QScriptEnginePrivate*>(engine)) + , u(engine->makeJSValue(value)) +{ + Q_ASSERT(engine); + JSValueProtect(*m_engine, u.m_value); +} + +QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, int value) + : m_state(JSPrimitive) + , m_engine(const_cast<QScriptEnginePrivate*>(engine)) + , u(m_engine->makeJSValue(value)) +{ + Q_ASSERT(engine); + JSValueProtect(*m_engine, u.m_value); +} + +QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, uint value) + : m_state(JSPrimitive) + , m_engine(const_cast<QScriptEnginePrivate*>(engine)) + , u(m_engine->makeJSValue(value)) +{ + Q_ASSERT(engine); + JSValueProtect(*m_engine, u.m_value); +} + +QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, qsreal value) + : m_state(JSPrimitive) + , m_engine(const_cast<QScriptEnginePrivate*>(engine)) + , u(m_engine->makeJSValue(value)) +{ + Q_ASSERT(engine); + JSValueProtect(*m_engine, u.m_value); +} + +QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, const QString& value) + : m_state(JSPrimitive) + , m_engine(const_cast<QScriptEnginePrivate*>(engine)) + , u(m_engine->makeJSValue(value)) +{ + Q_ASSERT(engine); + JSValueProtect(*m_engine, u.m_value); +} + +QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, QScriptValue::SpecialValue value) + : m_state(JSPrimitive) + , m_engine(const_cast<QScriptEnginePrivate*>(engine)) + , u(m_engine->makeJSValue(value)) +{ + Q_ASSERT(engine); + JSValueProtect(*m_engine, u.m_value); +} + +QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, JSValueRef value) + : m_state(JSValue) + , m_engine(const_cast<QScriptEnginePrivate*>(engine)) + , u(value) +{ + Q_ASSERT(engine); + Q_ASSERT(value); + JSValueProtect(*m_engine, u.m_value); +} + +QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, JSObjectRef object) + : m_state(JSObject) + , m_engine(const_cast<QScriptEnginePrivate*>(engine)) + , u(object) +{ + Q_ASSERT(engine); + Q_ASSERT(object); + JSValueProtect(*m_engine, object); +} + +bool QScriptValuePrivate::isValid() const { return m_state != Invalid; } + +bool QScriptValuePrivate::isBool() +{ + switch (m_state) { + case CBool: + return true; + case JSValue: + if (refinedJSValue() != JSPrimitive) + return false; + // Fall-through. + case JSPrimitive: + return JSValueIsBoolean(*m_engine, *this); + default: + return false; + } +} + +bool QScriptValuePrivate::isNumber() +{ + switch (m_state) { + case CNumber: + return true; + case JSValue: + if (refinedJSValue() != JSPrimitive) + return false; + // Fall-through. + case JSPrimitive: + return JSValueIsNumber(*m_engine, *this); + default: + return false; + } +} + +bool QScriptValuePrivate::isNull() +{ + switch (m_state) { + case CNull: + return true; + case JSValue: + if (refinedJSValue() != JSPrimitive) + return false; + // Fall-through. + case JSPrimitive: + return JSValueIsNull(*m_engine, *this); + default: + return false; + } +} + +bool QScriptValuePrivate::isString() +{ + switch (m_state) { + case CString: + return true; + case JSValue: + if (refinedJSValue() != JSPrimitive) + return false; + // Fall-through. + case JSPrimitive: + return JSValueIsString(*m_engine, *this); + default: + return false; + } +} + +bool QScriptValuePrivate::isUndefined() +{ + switch (m_state) { + case CUndefined: + return true; + case JSValue: + if (refinedJSValue() != JSPrimitive) + return false; + // Fall-through. + case JSPrimitive: + return JSValueIsUndefined(*m_engine, *this); + default: + return false; + } +} + +bool QScriptValuePrivate::isError() +{ + switch (m_state) { + case JSValue: + if (refinedJSValue() != JSObject) + return false; + // Fall-through. + case JSObject: + return m_engine->isError(*this); + default: + return false; + } +} + +bool QScriptValuePrivate::isObject() +{ + switch (m_state) { + case JSValue: + return refinedJSValue() == JSObject; + case JSObject: + return true; + + default: + return false; + } +} + +bool QScriptValuePrivate::isFunction() +{ + switch (m_state) { + case JSValue: + if (refinedJSValue() != JSObject) + return false; + // Fall-through. + case JSObject: + return JSObjectIsFunction(*m_engine, *this); + default: + return false; + } +} + +bool QScriptValuePrivate::isArray() +{ + switch (m_state) { + case JSValue: + if (refinedJSValue() != JSObject) + return false; + // Fall-through. + case JSObject: + return m_engine->isArray(*this); + default: + return false; + } +} + +bool QScriptValuePrivate::isDate() +{ + switch (m_state) { + case JSValue: + if (refinedJSValue() != JSObject) + return false; + // Fall-through. + case JSObject: + return m_engine->isDate(*this); + default: + return false; + } +} + +QString QScriptValuePrivate::toString() const +{ + switch (m_state) { + case Invalid: + return QString(); + case CBool: + return u.m_bool ? QString::fromLatin1("true") : QString::fromLatin1("false"); + case CString: + return *u.m_string; + case CNumber: + return QScriptConverter::toString(u.m_number); + case CNull: + return QString::fromLatin1("null"); + case CUndefined: + return QString::fromLatin1("undefined"); + case JSValue: + case JSPrimitive: + case JSObject: + JSValueRef exception = 0; + JSRetainPtr<JSStringRef> ptr(Adopt, JSValueToStringCopy(*m_engine, *this, &exception)); + m_engine->setException(exception); + return QScriptConverter::toString(ptr.get()); + } + + Q_ASSERT_X(false, "toString()", "Not all states are included in the previous switch statement."); + return QString(); // Avoid compiler warning. +} + +qsreal QScriptValuePrivate::toNumber() const +{ + switch (m_state) { + case JSValue: + case JSPrimitive: + case JSObject: + { + JSValueRef exception = 0; + qsreal result = JSValueToNumber(*m_engine, *this, &exception); + m_engine->setException(exception); + return result; + } + case CNumber: + return u.m_number; + case CBool: + return u.m_bool ? 1 : 0; + case CNull: + case Invalid: + return 0; + case CUndefined: + return qQNaN(); + case CString: + bool ok; + qsreal result = u.m_string->toDouble(&ok); + if (ok) + return result; + result = u.m_string->toInt(&ok, 0); // Try other bases. + if (ok) + return result; + if (*u.m_string == "Infinity" || *u.m_string == "-Infinity") + return qInf(); + return u.m_string->length() ? qQNaN() : 0; + } + + Q_ASSERT_X(false, "toNumber()", "Not all states are included in the previous switch statement."); + return 0; // Avoid compiler warning. +} + +bool QScriptValuePrivate::toBool() const +{ + switch (m_state) { + case JSValue: + case JSPrimitive: + return JSValueToBoolean(*m_engine, *this); + case JSObject: + return true; + case CNumber: + return !(qIsNaN(u.m_number) || !u.m_number); + case CBool: + return u.m_bool; + case Invalid: + case CNull: + case CUndefined: + return false; + case CString: + return u.m_string->length(); + } + + Q_ASSERT_X(false, "toBool()", "Not all states are included in the previous switch statement."); + return false; // Avoid compiler warning. +} + +qsreal QScriptValuePrivate::toInteger() const +{ + 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 +{ + 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 +{ + 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 +{ + return toInt32(); +} + +/*! + Creates a copy of this value and converts it to an object. If this value is an object + then pointer to this value will be returned. + \attention it should not happen but if this value is bounded to a different engine that the given, the first + one will be used. + \internal + */ +QScriptValuePrivate* QScriptValuePrivate::toObject(QScriptEnginePrivate* engine) +{ + switch (m_state) { + case Invalid: + case CNull: + case CUndefined: + return new QScriptValuePrivate; + case CString: + { + // Exception can't occur here. + JSObjectRef object = JSValueToObject(*engine, engine->makeJSValue(*u.m_string), /* exception */ 0); + Q_ASSERT(object); + return new QScriptValuePrivate(engine, object); + } + case CNumber: + { + // Exception can't occur here. + JSObjectRef object = JSValueToObject(*engine, engine->makeJSValue(u.m_number), /* exception */ 0); + Q_ASSERT(object); + return new QScriptValuePrivate(engine, object); + } + case CBool: + { + // Exception can't occure here. + JSObjectRef object = JSValueToObject(*engine, engine->makeJSValue(u.m_bool), /* exception */ 0); + Q_ASSERT(object); + return new QScriptValuePrivate(engine, object); + } + case JSValue: + if (refinedJSValue() != JSPrimitive) + break; + // Fall-through. + case JSPrimitive: + { + if (engine != this->engine()) + qWarning("QScriptEngine::toObject: cannot convert value created in a different engine"); + JSValueRef exception = 0; + JSObjectRef object = JSValueToObject(*m_engine, *this, &exception); + if (object) + return new QScriptValuePrivate(m_engine.constData(), object); + else + m_engine->setException(exception, QScriptEnginePrivate::NotNullException); + + } + return new QScriptValuePrivate; + case JSObject: + break; + } + + if (engine != this->engine()) + qWarning("QScriptEngine::toObject: cannot convert value created in a different engine"); + Q_ASSERT(m_state == JSObject); + return this; +} + +/*! + This method is created only for QScriptValue::toObject() purpose which is obsolete. + \internal + */ +QScriptValuePrivate* QScriptValuePrivate::toObject() +{ + if (isJSBased()) + return toObject(m_engine.data()); + + // Without an engine there is not much we can do. + return new QScriptValuePrivate; +} + +QDateTime QScriptValuePrivate::toDateTime() +{ + if (!isDate()) + return QDateTime(); + + JSValueRef exception = 0; + qsreal t = JSValueToNumber(*m_engine, *this, &exception); + + if (exception) { + m_engine->setException(exception, QScriptEnginePrivate::NotNullException); + return QDateTime(); + } + + QDateTime result; + result.setMSecsSinceEpoch(qint64(t)); + return result; +} + +inline QScriptValuePrivate* QScriptValuePrivate::prototype() +{ + if (isObject()) { + JSValueRef prototype = JSObjectGetPrototype(*m_engine, *this); + if (JSValueIsNull(*m_engine, prototype)) + return new QScriptValuePrivate(engine(), prototype); + // The prototype could be either a null or a JSObject, so it is safe to cast the prototype + // to the JSObjectRef here. + return new QScriptValuePrivate(engine(), const_cast<JSObjectRef>(prototype)); + } + return new QScriptValuePrivate; +} + +inline void QScriptValuePrivate::setPrototype(QScriptValuePrivate* prototype) +{ + if (isObject() && prototype->isValid() && (prototype->isObject() || prototype->isNull())) { + if (engine() != prototype->engine()) { + qWarning("QScriptValue::setPrototype() failed: cannot set a prototype created in a different engine"); + return; + } + // FIXME: This could be replaced by a new, faster API + // look at https://bugs.webkit.org/show_bug.cgi?id=40060 + JSObjectSetPrototype(*m_engine, *this, *prototype); + JSValueRef proto = JSObjectGetPrototype(*m_engine, *this); + if (!JSValueIsStrictEqual(*m_engine, proto, *prototype)) + qWarning("QScriptValue::setPrototype() failed: cyclic prototype value"); + } +} + +bool QScriptValuePrivate::equals(QScriptValuePrivate* other) +{ + if (!isValid()) + return !other->isValid(); + + if (!other->isValid()) + return false; + + if (!isJSBased() && !other->isJSBased()) { + switch (m_state) { + case CNull: + case CUndefined: + return other->isUndefined() || other->isNull(); + case CNumber: + switch (other->m_state) { + case CBool: + case CString: + return u.m_number == other->toNumber(); + case CNumber: + return u.m_number == other->u.m_number; + default: + return false; + } + case CBool: + switch (other->m_state) { + case CBool: + return u.m_bool == other->u.m_bool; + case CNumber: + return toNumber() == other->u.m_number; + case CString: + return toNumber() == other->toNumber(); + default: + return false; + } + case CString: + switch (other->m_state) { + case CBool: + return toNumber() == other->toNumber(); + case CNumber: + return toNumber() == other->u.m_number; + case CString: + return *u.m_string == *other->u.m_string; + default: + return false; + } + default: + Q_ASSERT_X(false, "equals()", "Not all states are included in the previous switch statement."); + } + } + + if (isJSBased() && !other->isJSBased()) { + if (!other->assignEngine(engine())) { + qWarning("equals(): Cannot compare to a value created in a different engine"); + return false; + } + } else if (!isJSBased() && other->isJSBased()) { + if (!assignEngine(other->engine())) { + qWarning("equals(): Cannot compare to a value created in a different engine"); + return false; + } + } + + JSValueRef exception = 0; + bool result = JSValueIsEqual(*m_engine, *this, *other, &exception); + m_engine->setException(exception); + return result; +} + +bool QScriptValuePrivate::strictlyEquals(QScriptValuePrivate* other) +{ + if (isJSBased()) { + // We can't compare these two values without binding to the same engine. + if (!other->isJSBased()) { + if (other->assignEngine(engine())) + return JSValueIsStrictEqual(*m_engine, *this, *other); + return false; + } + if (other->engine() != engine()) { + qWarning("strictlyEquals(): Cannot compare to a value created in a different engine"); + return false; + } + return JSValueIsStrictEqual(*m_engine, *this, *other); + } + if (isStringBased()) { + if (other->isStringBased()) + return *u.m_string == *(other->u.m_string); + if (other->isJSBased()) { + assignEngine(other->engine()); + return JSValueIsStrictEqual(*m_engine, *this, *other); + } + } + if (isNumberBased()) { + if (other->isNumberBased()) + return u.m_number == other->u.m_number; + if (other->isJSBased()) { + assignEngine(other->engine()); + return JSValueIsStrictEqual(*m_engine, *this, *other); + } + } + if (!isValid() && !other->isValid()) + return true; + + return false; +} + +inline bool QScriptValuePrivate::instanceOf(QScriptValuePrivate* other) +{ + if (!isJSBased() || !other->isObject()) + return false; + JSValueRef exception = 0; + bool result = JSValueIsInstanceOfConstructor(*m_engine, *this, *other, &exception); + m_engine->setException(exception); + return result; +} + +/*! + Tries to assign \a engine to this value. Returns true on success; otherwise returns false. +*/ +bool QScriptValuePrivate::assignEngine(QScriptEnginePrivate* engine) +{ + Q_ASSERT(engine); + JSValueRef value; + switch (m_state) { + case CBool: + value = engine->makeJSValue(u.m_bool); + break; + case CString: + value = engine->makeJSValue(*u.m_string); + delete u.m_string; + break; + case CNumber: + value = engine->makeJSValue(u.m_number); + break; + case CNull: + value = engine->makeJSValue(QScriptValue::NullValue); + break; + case CUndefined: + value = engine->makeJSValue(QScriptValue::UndefinedValue); + break; + default: + if (!isJSBased()) + Q_ASSERT_X(!isJSBased(), "assignEngine()", "Not all states are included in the previous switch statement."); + else + qWarning("JSValue can't be rassigned to an another engine."); + return false; + } + m_engine = engine; + m_state = JSPrimitive; + u.m_value = value; + JSValueProtect(*m_engine, value); + return true; +} + +inline QScriptValuePrivate* QScriptValuePrivate::property(const QString& name, const QScriptValue::ResolveFlags& mode) +{ + JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(name)); + return property<JSStringRef>(propertyName.get(), mode); +} + +inline QScriptValuePrivate* QScriptValuePrivate::property(const QScriptStringPrivate* name, const QScriptValue::ResolveFlags& mode) +{ + return property<JSStringRef>(*name, mode); +} + +inline QScriptValuePrivate* QScriptValuePrivate::property(quint32 arrayIndex, const QScriptValue::ResolveFlags& mode) +{ + return property<quint32>(arrayIndex, mode); +} + +/*! + \internal + This method was created to unify access to the JSObjectGetPropertyAtIndex and the JSObjectGetProperty. +*/ +inline JSValueRef QScriptValuePrivate::property(quint32 property, JSValueRef* exception) +{ + return JSObjectGetPropertyAtIndex(*m_engine, *this, property, exception); +} + +/*! + \internal + This method was created to unify access to the JSObjectGetPropertyAtIndex and the JSObjectGetProperty. +*/ +inline JSValueRef QScriptValuePrivate::property(JSStringRef property, JSValueRef* exception) +{ + return JSObjectGetProperty(*m_engine, *this, property, exception); +} + +/*! + \internal + This method was created to unify acccess to hasOwnProperty, same function for an array index + and a property name access. +*/ +inline bool QScriptValuePrivate::hasOwnProperty(quint32 property) +{ + Q_ASSERT(isObject()); + // FIXME it could be faster, but JSC C API doesn't expose needed functionality. + JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(QString::number(property))); + return hasOwnProperty(propertyName.get()); +} + +/*! + \internal + This method was created to unify acccess to hasOwnProperty, same function for an array index + and a property name access. +*/ +inline bool QScriptValuePrivate::hasOwnProperty(JSStringRef property) +{ + Q_ASSERT(isObject()); + return m_engine->objectHasOwnProperty(*this, property); +} + +/*! + \internal + This function gets property of an object. + \arg propertyName could be type of quint32 (an array index) or JSStringRef (a property name). +*/ +template<typename T> +inline QScriptValuePrivate* QScriptValuePrivate::property(T propertyName, const QScriptValue::ResolveFlags& mode) +{ + if (!isObject()) + return new QScriptValuePrivate(); + + if ((mode == QScriptValue::ResolveLocal) && (!hasOwnProperty(propertyName))) + return new QScriptValuePrivate(); + + JSValueRef exception = 0; + JSValueRef value = property(propertyName, &exception); + + if (exception) { + m_engine->setException(exception, QScriptEnginePrivate::NotNullException); + return new QScriptValuePrivate(engine(), exception); + } + if (JSValueIsUndefined(*m_engine, value)) + return new QScriptValuePrivate; + return new QScriptValuePrivate(engine(), value); +} + +inline void QScriptValuePrivate::setProperty(const QString& name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags) +{ + JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(name)); + setProperty<JSStringRef>(propertyName.get(), value, flags); +} + +inline void QScriptValuePrivate::setProperty(quint32 arrayIndex, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags) +{ + setProperty<quint32>(arrayIndex, value, flags); +} + +inline void QScriptValuePrivate::setProperty(const QScriptStringPrivate* name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags) +{ + setProperty<JSStringRef>(*name, value, flags); +} + +/*! + \internal + This method was created to unify access to the JSObjectSetPropertyAtIndex and the JSObjectSetProperty. +*/ +inline void QScriptValuePrivate::setProperty(quint32 property, JSValueRef value, JSPropertyAttributes flags, JSValueRef* exception) +{ + Q_ASSERT(isObject()); + if (flags) { + // FIXME This could be better, but JSC C API doesn't expose needed functionality. It is + // not possible to create / modify a property attribute via an array index. + JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(QString::number(property))); + JSObjectSetProperty(*m_engine, *this, propertyName.get(), value, flags, exception); + return; + } + JSObjectSetPropertyAtIndex(*m_engine, *this, property, value, exception); +} + +/*! + \internal + This method was created to unify access to the JSObjectSetPropertyAtIndex and the JSObjectSetProperty. +*/ +inline void QScriptValuePrivate::setProperty(JSStringRef property, JSValueRef value, JSPropertyAttributes flags, JSValueRef* exception) +{ + Q_ASSERT(isObject()); + JSObjectSetProperty(*m_engine, *this, property, value, flags, exception); +} + +/*! + \internal + This method was created to unify access to the JSObjectDeleteProperty and a teoretical JSObjectDeletePropertyAtIndex + which doesn't exist now. +*/ +inline void QScriptValuePrivate::deleteProperty(quint32 property, JSValueRef* exception) +{ + // FIXME It could be faster, we need a JSC C API for deleting array index properties. + JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(QString::number(property))); + JSObjectDeleteProperty(*m_engine, *this, propertyName.get(), exception); +} + +/*! + \internal + This method was created to unify access to the JSObjectDeleteProperty and a teoretical JSObjectDeletePropertyAtIndex. +*/ +inline void QScriptValuePrivate::deleteProperty(JSStringRef property, JSValueRef* exception) +{ + Q_ASSERT(isObject()); + JSObjectDeleteProperty(*m_engine, *this, property, exception); +} + +template<typename T> +inline void QScriptValuePrivate::setProperty(T name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags) +{ + if (!isObject()) + return; + + if (!value->isJSBased()) + value->assignEngine(engine()); + + JSValueRef exception = 0; + if (!value->isValid()) { + // Remove the property. + deleteProperty(name, &exception); + m_engine->setException(exception); + return; + } + if (m_engine != value->m_engine) { + qWarning("QScriptValue::setProperty() failed: cannot set value created in a different engine"); + return; + } + + setProperty(name, *value, QScriptConverter::toPropertyFlags(flags), &exception); + m_engine->setException(exception); +} + +inline QScriptValue::PropertyFlags QScriptValuePrivate::propertyFlags(const QString& name, const QScriptValue::ResolveFlags& mode) +{ + JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(name)); + return propertyFlags(propertyName.get(), mode); +} + +inline QScriptValue::PropertyFlags QScriptValuePrivate::propertyFlags(const QScriptStringPrivate* name, const QScriptValue::ResolveFlags& mode) +{ + return propertyFlags(*name, mode); +} + +inline QScriptValue::PropertyFlags QScriptValuePrivate::propertyFlags(JSStringRef name, const QScriptValue::ResolveFlags& mode) +{ + unsigned flags = 0; + if (!isObject()) + return QScriptValue::PropertyFlags(flags); + + // FIXME It could be faster and nicer, but new JSC C API should be created. + static JSStringRef objectName = QScriptConverter::toString("Object"); + static JSStringRef propertyDescriptorName = QScriptConverter::toString("getOwnPropertyDescriptor"); + + // FIXME This is dangerous if global object was modified (bug 41839). + JSValueRef exception = 0; + JSObjectRef globalObject = JSContextGetGlobalObject(*m_engine); + JSValueRef objectConstructor = JSObjectGetProperty(*m_engine, globalObject, objectName, &exception); + Q_ASSERT(JSValueIsObject(*m_engine, objectConstructor)); + JSValueRef propertyDescriptorGetter = JSObjectGetProperty(*m_engine, const_cast<JSObjectRef>(objectConstructor), propertyDescriptorName, &exception); + Q_ASSERT(JSValueIsObject(*m_engine, propertyDescriptorGetter)); + + JSValueRef arguments[] = { *this, JSValueMakeString(*m_engine, name) }; + JSObjectRef propertyDescriptor + = const_cast<JSObjectRef>(JSObjectCallAsFunction(*m_engine, + const_cast<JSObjectRef>(propertyDescriptorGetter), + /* thisObject */ 0, + /* argumentCount */ 2, + arguments, + &exception)); + if (exception) { + // Invalid property. + return QScriptValue::PropertyFlags(flags); + } + + if (!JSValueIsObject(*m_engine, propertyDescriptor)) { + // Property isn't owned by this object. + JSObjectRef proto; + if (mode == QScriptValue::ResolveLocal + || ((proto = const_cast<JSObjectRef>(JSObjectGetPrototype(*m_engine, *this))) && JSValueIsNull(*m_engine, proto))) { + return QScriptValue::PropertyFlags(flags); + } + QScriptValuePrivate p(engine(), proto); + return p.propertyFlags(name, QScriptValue::ResolvePrototype); + } + + static JSStringRef writableName = QScriptConverter::toString("writable"); + static JSStringRef configurableName = QScriptConverter::toString("configurable"); + static JSStringRef enumerableName = QScriptConverter::toString("enumerable"); + + bool readOnly = !JSValueToBoolean(*m_engine, JSObjectGetProperty(*m_engine, propertyDescriptor, writableName, &exception)); + if (!exception && readOnly) + flags |= QScriptValue::ReadOnly; + bool undeletable = !JSValueToBoolean(*m_engine, JSObjectGetProperty(*m_engine, propertyDescriptor, configurableName, &exception)); + if (!exception && undeletable) + flags |= QScriptValue::Undeletable; + bool skipInEnum = !JSValueToBoolean(*m_engine, JSObjectGetProperty(*m_engine, propertyDescriptor, enumerableName, &exception)); + if (!exception && skipInEnum) + flags |= QScriptValue::SkipInEnumeration; + + return QScriptValue::PropertyFlags(flags); +} + +QScriptValuePrivate* QScriptValuePrivate::call(const QScriptValuePrivate*, const QScriptValueList& args) +{ + switch (m_state) { + case JSValue: + if (refinedJSValue() != JSObject) + return new QScriptValuePrivate; + // Fall-through. + case JSObject: + { + // Convert all arguments and bind to the engine. + int argc = args.size(); + QVarLengthArray<JSValueRef, 8> argv(argc); + QScriptValueList::const_iterator i = args.constBegin(); + for (int j = 0; i != args.constEnd(); j++, i++) { + QScriptValuePrivate* value = QScriptValuePrivate::get(*i); + if (!value->assignEngine(engine())) { + qWarning("QScriptValue::call() failed: cannot call function with values created in a different engine"); + return new QScriptValuePrivate; + } + argv[j] = *value; + } + + // Make the call + JSValueRef exception = 0; + JSValueRef result = JSObjectCallAsFunction(*m_engine, *this, /* thisObject */ 0, argc, argv.constData(), &exception); + if (!result && exception) { + m_engine->setException(exception); + return new QScriptValuePrivate(engine(), exception); + } + if (result && !exception) + return new QScriptValuePrivate(engine(), result); + } + // this QSV is not a function <-- !result && !exception. Fall-through. + default: + return new QScriptValuePrivate; + } +} + +QScriptEnginePrivate* QScriptValuePrivate::engine() const +{ + // As long as m_engine is an autoinitializated pointer we can safely return it without + // checking current state. + return m_engine.data(); +} + +QScriptValuePrivate::operator JSValueRef() const +{ + Q_ASSERT(isJSBased()); + Q_ASSERT(u.m_value); + return u.m_value; +} + +QScriptValuePrivate::operator JSObjectRef() const +{ + Q_ASSERT(m_state == JSObject); + Q_ASSERT(u.m_object); + return u.m_object; +} + +/*! + \internal + Refines the state of this QScriptValuePrivate. Returns the new state. +*/ +QScriptValuePrivate::State QScriptValuePrivate::refinedJSValue() +{ + Q_ASSERT(m_state == JSValue); + if (!JSValueIsObject(*m_engine, *this)) { + m_state = JSPrimitive; + } else { + // Difference between JSValueRef and JSObjectRef is only in their type, binarywise it is the same. + // As m_value and m_object are stored in the u union, it is enough to change the m_state only. + m_state = JSObject; + } + return m_state; +} + +/*! + \internal + Returns true if QSV have an engine associated. +*/ +bool QScriptValuePrivate::isJSBased() const { return m_state >= JSValue; } + +/*! + \internal + Returns true if current value of QSV is placed in m_number. +*/ +bool QScriptValuePrivate::isNumberBased() const { return m_state == CNumber || m_state == CBool; } + +/*! + \internal + Returns true if current value of QSV is placed in m_string. +*/ +bool QScriptValuePrivate::isStringBased() const { return m_state == CString; } + +#endif // qscriptvalue_p_h |