#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;
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:
enum State {
Invalid = 0,
CString = 0x1000,
CNumber,
CBool,
CNull,
CUndefined,
JSValue = 0x2000, 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;
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;
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;
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;
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;
case JSPrimitive:
return JSValueIsUndefined(*m_engine, *this);
default:
return false;
}
}
bool QScriptValuePrivate::isError()
{
switch (m_state) {
case JSValue:
if (refinedJSValue() != JSObject)
return false;
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;
case JSObject:
return JSObjectIsFunction(*m_engine, *this);
default:
return false;
}
}
bool QScriptValuePrivate::isArray()
{
switch (m_state) {
case JSValue:
if (refinedJSValue() != JSObject)
return false;
case JSObject:
return m_engine->isArray(*this);
default:
return false;
}
}
bool QScriptValuePrivate::isDate()
{
switch (m_state) {
case JSValue:
if (refinedJSValue() != JSObject)
return false;
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(); }
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); 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; }
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; }
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();
if (qIsInf(result))
return 0;
return result;
}
quint32 QScriptValuePrivate::toUInt32() const
{
qsreal result = toInteger();
if (qIsInf(result))
return 0;
return result;
}
quint16 QScriptValuePrivate::toUInt16() const
{
return toInt32();
}
QScriptValuePrivate* QScriptValuePrivate::toObject(QScriptEnginePrivate* engine)
{
switch (m_state) {
case Invalid:
case CNull:
case CUndefined:
return new QScriptValuePrivate;
case CString:
{
JSObjectRef object = JSValueToObject(*engine, engine->makeJSValue(*u.m_string), 0);
Q_ASSERT(object);
return new QScriptValuePrivate(engine, object);
}
case CNumber:
{
JSObjectRef object = JSValueToObject(*engine, engine->makeJSValue(u.m_number), 0);
Q_ASSERT(object);
return new QScriptValuePrivate(engine, object);
}
case CBool:
{
JSObjectRef object = JSValueToObject(*engine, engine->makeJSValue(u.m_bool), 0);
Q_ASSERT(object);
return new QScriptValuePrivate(engine, object);
}
case JSValue:
if (refinedJSValue() != JSPrimitive)
break;
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;
}
QScriptValuePrivate* QScriptValuePrivate::toObject()
{
if (isJSBased())
return toObject(m_engine.data());
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);
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;
}
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()) {
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;
}
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);
}
inline JSValueRef QScriptValuePrivate::property(quint32 property, JSValueRef* exception)
{
return JSObjectGetPropertyAtIndex(*m_engine, *this, property, exception);
}
inline JSValueRef QScriptValuePrivate::property(JSStringRef property, JSValueRef* exception)
{
return JSObjectGetProperty(*m_engine, *this, property, exception);
}
inline bool QScriptValuePrivate::hasOwnProperty(quint32 property)
{
Q_ASSERT(isObject());
JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(QString::number(property)));
return hasOwnProperty(propertyName.get());
}
inline bool QScriptValuePrivate::hasOwnProperty(JSStringRef property)
{
Q_ASSERT(isObject());
return m_engine->objectHasOwnProperty(*this, property);
}
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);
}
inline void QScriptValuePrivate::setProperty(quint32 property, JSValueRef value, JSPropertyAttributes flags, JSValueRef* exception)
{
Q_ASSERT(isObject());
if (flags) {
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);
}
inline void QScriptValuePrivate::setProperty(JSStringRef property, JSValueRef value, JSPropertyAttributes flags, JSValueRef* exception)
{
Q_ASSERT(isObject());
JSObjectSetProperty(*m_engine, *this, property, value, flags, exception);
}
inline void QScriptValuePrivate::deleteProperty(quint32 property, JSValueRef* exception)
{
JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(QString::number(property)));
JSObjectDeleteProperty(*m_engine, *this, propertyName.get(), exception);
}
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()) {
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);
static JSStringRef objectName = QScriptConverter::toString("Object");
static JSStringRef propertyDescriptorName = QScriptConverter::toString("getOwnPropertyDescriptor");
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),
0,
2,
arguments,
&exception));
if (exception) {
return QScriptValue::PropertyFlags(flags);
}
if (!JSValueIsObject(*m_engine, propertyDescriptor)) {
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;
case JSObject:
{
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;
}
JSValueRef exception = 0;
JSValueRef result = JSObjectCallAsFunction(*m_engine, *this, 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);
}
default:
return new QScriptValuePrivate;
}
}
QScriptEnginePrivate* QScriptValuePrivate::engine() const
{
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;
}
QScriptValuePrivate::State QScriptValuePrivate::refinedJSValue()
{
Q_ASSERT(m_state == JSValue);
if (!JSValueIsObject(*m_engine, *this)) {
m_state = JSPrimitive;
} else {
m_state = JSObject;
}
return m_state;
}
bool QScriptValuePrivate::isJSBased() const { return m_state >= JSValue; }
bool QScriptValuePrivate::isNumberBased() const { return m_state == CNumber || m_state == CBool; }
bool QScriptValuePrivate::isStringBased() const { return m_state == CString; }
#endif // qscriptvalue_p_h