blob: 3d27cc2c1f9fa407b867f7950c5e5b68abbb16ec [file] [log] [blame]
/*
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/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 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 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(quint32 arrayIndex, 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 bool inherits(const char*);
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 inherits("Error");
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;
}
}
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:
JSRetainPtr<JSStringRef> ptr(Adopt, JSValueToStringCopy(*m_engine, *this, /* exception */ 0));
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:
return JSValueToNumber(*m_engine, *this, /* exception */ 0);
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");
JSObjectRef object = JSValueToObject(*m_engine, *this, /* exception */ 0);
if (object)
return new QScriptValuePrivate(m_engine.constData(), object);
}
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;
}
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 ((m_state == other->m_state) && !isJSBased()) {
if (isNumberBased())
return u.m_number == other->u.m_number;
Q_ASSERT(isStringBased());
return *u.m_string == *(other->u.m_string);
}
if (!isJSBased() && !other->isJSBased())
return false;
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;
}
}
return JSValueIsEqual(*m_engine, *this, *other, /* exception */ 0);
}
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;
return JSValueIsInstanceOfConstructor(*m_engine, *this, *other, /* exception */ 0);
}
/*!
Tries to assign \a engine to this value. Returns true on success; otherwise returns false.
*/
bool QScriptValuePrivate::assignEngine(QScriptEnginePrivate* 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)
{
if (!isObject())
return new QScriptValuePrivate;
if (mode & QScriptValue::ResolveLocal) {
qWarning("QScriptValue::property(): ResolveLocal not supported yet.");
return new QScriptValuePrivate;
}
JSRetainPtr<JSStringRef> nameRef(Adopt, QScriptConverter::toString(name));
QScriptValuePrivate* result = new QScriptValuePrivate(m_engine.constData(), JSObjectGetProperty(*m_engine, *this, nameRef.get(), /* exception */ 0));
return result;
}
inline QScriptValuePrivate* QScriptValuePrivate::property(quint32 arrayIndex, const QScriptValue::ResolveFlags& mode)
{
if (!isObject())
return new QScriptValuePrivate;
if (mode & QScriptValue::ResolveLocal) {
qWarning("QScriptValue::property(): ResolveLocal not supported yet.");
return new QScriptValuePrivate;
}
return new QScriptValuePrivate(m_engine.constData(), JSObjectGetPropertyAtIndex(*m_engine, *this, arrayIndex, /* exception */ 0));
}
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)
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
Returns true if QSV is created from constructor with the given \a name, it has to be a
built-in type.
*/
bool QScriptValuePrivate::inherits(const char* name)
{
Q_ASSERT(isJSBased());
JSObjectRef globalObject = JSContextGetGlobalObject(*m_engine);
JSStringRef errorAttrName = QScriptConverter::toString(name);
JSValueRef error = JSObjectGetProperty(*m_engine, globalObject, errorAttrName, /* exception */ 0);
JSStringRelease(errorAttrName);
return JSValueIsInstanceOfConstructor(*m_engine, *this, JSValueToObject(*m_engine, 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(*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