aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime/qv4value_p.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsruntime/qv4value_p.h')
-rw-r--r--src/qml/jsruntime/qv4value_p.h647
1 files changed, 363 insertions, 284 deletions
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index b93fcbe4bd..4b16114174 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -38,360 +38,439 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-#ifndef QMLJS_VALUE_H
-#define QMLJS_VALUE_H
-
-#include <cmath> // this HAS to come
+#ifndef QV4VALUE_P_H
+#define QV4VALUE_P_H
#include <QtCore/QString>
-#include <QtCore/qnumeric.h>
#include "qv4global_p.h"
-#include "qv4string_p.h"
-#include <QtCore/QDebug>
-#include "qv4managed_p.h"
-#include "qv4engine_p.h"
-#include <private/qtqmlglobal_p.h>
-
-//#include <wtf/MathExtras.h>
-
-#include "qv4value_def_p.h"
QT_BEGIN_NAMESPACE
namespace QV4 {
-inline bool Value::isString() const
-{
- if (!isManaged())
- return false;
- return managed() && managed()->internalClass->vtable->isString;
-}
-inline bool Value::isObject() const
-{
- if (!isManaged())
- return false;
- return managed() && managed()->internalClass->vtable->isObject;
-}
+typedef uint Bool;
-inline bool Value::isPrimitive() const
+template <typename T>
+struct Returned : private T
{
- return !isObject();
-}
+ static Returned<T> *create(T *t) { return static_cast<Returned<T> *>(t); }
+ T *getPointer() { return this; }
+ template<typename X>
+ static T *getPointer(Returned<X> *x) { return x->getPointer(); }
+ template<typename X>
+ Returned<X> *as() { return Returned<X>::create(Returned<X>::getPointer(this)); }
+ using T::asReturnedValue;
+};
-inline ExecutionEngine *Value::engine() const
+struct Q_QML_EXPORT Value
{
- Managed *m = asManaged();
- return m ? m->engine() : 0;
-}
+ /*
+ We use two different ways of encoding JS values. One for 32bit and one for 64bit systems.
-inline void Value::mark(ExecutionEngine *e) const
-{
- if (!val)
- return;
- Managed *m = asManaged();
- if (m)
- m->mark(e);
-}
+ In both cases, we 8 bytes for a value and different variant of NaN boxing. A Double NaN (actually -qNaN)
+ is indicated by a number that has the top 13 bits set. THe other values are usually set to 0 by the
+ processor, and are thus free for us to store other data. We keep pointers in there for managed objects,
+ and encode the other types using the free space given to use by the unused bits for NaN values. This also
+ works for pointers on 64 bit systems, as they all currently only have 48 bits of addressable memory.
-inline Primitive Primitive::nullValue()
-{
- Primitive v;
+ On 32bit, we store doubles as doubles. All other values, have the high 32bits set to a value that
+ will make the number a NaN. The Masks below are used for encoding the other types.
+
+ On 64 bit, we xor Doubles with (0xffff8000 << 32). Thas has the effect that no doubles will get encoded
+ with the 13 highest bits all 0. We are now using special values for bits 14-17 to encode our values. These
+ can be used, as the highest valid pointer on a 64 bit system is 2^48-1.
+
+ If they are all 0, we have a pointer to a Managed object. If bit 14 is set we have an integer.
+ This makes testing for pointers and numbers very fast (we have a number if any of the highest 14 bits is set).
+
+ Bit 15-17 is then used to encode other immediates.
+ */
+
+
+ union {
+ quint64 val;
#if QT_POINTER_SIZE == 8
- v.val = quint64(_Null_Type) << Tag_Shift;
+ Managed *m;
+ Object *o;
+ String *s;
#else
- v.tag = _Null_Type;
- v.int_32 = 0;
+ double dbl;
+#endif
+ struct {
+#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN
+ uint tag;
+#endif
+ union {
+ uint uint_32;
+ int int_32;
+#if QT_POINTER_SIZE == 4
+ Managed *m;
+ Object *o;
+ String *s;
+#endif
+ };
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ uint tag;
+#endif
+ };
+ };
+
+#if QT_POINTER_SIZE == 4
+ enum Masks {
+ NaN_Mask = 0x7ff80000,
+ NotDouble_Mask = 0x7ffc0000,
+ Type_Mask = 0xffff8000,
+ Immediate_Mask = NotDouble_Mask | 0x00008000,
+ IsNullOrUndefined_Mask = Immediate_Mask | 0x20000,
+ Tag_Shift = 32
+ };
+ enum ValueType {
+ Undefined_Type = Immediate_Mask | 0x00000,
+ Null_Type = Immediate_Mask | 0x10000,
+ Boolean_Type = Immediate_Mask | 0x20000,
+ Integer_Type = Immediate_Mask | 0x30000,
+ Managed_Type = NotDouble_Mask | 0x00000,
+ Empty_Type = NotDouble_Mask | 0x30000
+ };
+
+ enum ImmediateFlags {
+ ConvertibleToInt = Immediate_Mask | 0x1
+ };
+
+ enum ValueTypeInternal {
+ _Null_Type = Null_Type | ConvertibleToInt,
+ _Boolean_Type = Boolean_Type | ConvertibleToInt,
+ _Integer_Type = Integer_Type | ConvertibleToInt,
+
+ };
+#else
+ static const quint64 NaNEncodeMask = 0xffff800000000000ll;
+ static const quint64 IsInt32Mask = 0x0002000000000000ll;
+ static const quint64 IsDoubleMask = 0xfffc000000000000ll;
+ static const quint64 IsNumberMask = IsInt32Mask|IsDoubleMask;
+ static const quint64 IsNullOrUndefinedMask = 0x0000800000000000ll;
+ static const quint64 IsNullOrBooleanMask = 0x0001000000000000ll;
+ static const quint64 IsConvertibleToIntMask = IsInt32Mask|IsNullOrBooleanMask;
+
+ enum Masks {
+ NaN_Mask = 0x7ff80000,
+ Type_Mask = 0xffff8000,
+ IsDouble_Mask = 0xfffc0000,
+ Immediate_Mask = 0x00018000,
+ IsNullOrUndefined_Mask = 0x00008000,
+ IsNullOrBoolean_Mask = 0x00010000,
+ Tag_Shift = 32
+ };
+ enum ValueType {
+ Undefined_Type = IsNullOrUndefined_Mask,
+ Null_Type = IsNullOrUndefined_Mask|IsNullOrBoolean_Mask,
+ Boolean_Type = IsNullOrBoolean_Mask,
+ Integer_Type = 0x20000|IsNullOrBoolean_Mask,
+ Managed_Type = 0,
+ Empty_Type = Undefined_Type | 0x4000
+ };
+ enum {
+ IsDouble_Shift = 64-14,
+ IsNumber_Shift = 64-15,
+ IsConvertibleToInt_Shift = 64-16,
+ IsManaged_Shift = 64-17
+ };
+
+
+ enum ValueTypeInternal {
+ _Null_Type = Null_Type,
+ _Boolean_Type = Boolean_Type,
+ _Integer_Type = Integer_Type
+ };
#endif
- return v;
-}
-
-inline Primitive Primitive::fromBoolean(bool b)
-{
- Primitive v;
- v.tag = _Boolean_Type;
- v.int_32 = (bool)b;
- return v;
-}
-inline Primitive Primitive::fromDouble(double d)
-{
- Primitive v;
- v.setDouble(d);
- return v;
-}
+ inline unsigned type() const {
+ return tag & Type_Mask;
+ }
-inline Primitive Primitive::fromInt32(int i)
-{
- Primitive v;
- v.tag = _Integer_Type;
- v.int_32 = i;
- return v;
-}
+ // used internally in property
+ inline bool isEmpty() const { return tag == Empty_Type; }
-inline Primitive Primitive::fromUInt32(uint i)
-{
- Primitive v;
- if (i < INT_MAX) {
- v.tag = _Integer_Type;
- v.int_32 = (int)i;
- } else {
- v.setDouble(i);
+ inline bool isUndefined() const { return tag == Undefined_Type; }
+ inline bool isNull() const { return tag == _Null_Type; }
+ inline bool isBoolean() const { return tag == _Boolean_Type; }
+#if QT_POINTER_SIZE == 8
+ inline bool isInteger() const { return (val >> IsNumber_Shift) == 1; }
+ inline bool isDouble() const { return (val >> IsDouble_Shift); }
+ inline bool isNumber() const { return (val >> IsNumber_Shift); }
+ inline bool isManaged() const { return !(val >> IsManaged_Shift); }
+ inline bool isNullOrUndefined() const { return ((val >> IsManaged_Shift) & ~2) == 1; }
+ inline bool integerCompatible() const { return ((val >> IsConvertibleToInt_Shift) & ~2) == 1; }
+ static inline bool integerCompatible(Value a, Value b) {
+ return a.integerCompatible() && b.integerCompatible();
}
- return v;
-}
-
-inline double Value::toNumber() const
-{
- if (integerCompatible())
- return int_32;
- if (isDouble())
+ static inline bool bothDouble(Value a, Value b) {
+ return a.isDouble() && b.isDouble();
+ }
+ double doubleValue() const {
+ Q_ASSERT(isDouble());
+ union {
+ quint64 i;
+ double d;
+ } v;
+ v.i = val ^ NaNEncodeMask;
+ return v.d;
+ }
+ void setDouble(double d) {
+ union {
+ quint64 i;
+ double d;
+ } v;
+ v.d = d;
+ val = v.i ^ NaNEncodeMask;
+ Q_ASSERT(isDouble());
+ }
+ bool isNaN() const { return (tag & 0x7fff8000) == 0x00078000; }
+#else
+ inline bool isInteger() const { return tag == _Integer_Type; }
+ inline bool isDouble() const { return (tag & NotDouble_Mask) != NotDouble_Mask; }
+ inline bool isNumber() const { return tag == _Integer_Type || (tag & NotDouble_Mask) != NotDouble_Mask; }
+ inline bool isManaged() const { return tag == Managed_Type; }
+ inline bool isNullOrUndefined() const { return (tag & IsNullOrUndefined_Mask) == Undefined_Type; }
+ inline bool integerCompatible() const { return (tag & ConvertibleToInt) == ConvertibleToInt; }
+ static inline bool integerCompatible(Value a, Value b) {
+ return ((a.tag & b.tag) & ConvertibleToInt) == ConvertibleToInt;
+ }
+ static inline bool bothDouble(Value a, Value b) {
+ return ((a.tag | b.tag) & NotDouble_Mask) != NotDouble_Mask;
+ }
+ double doubleValue() const { return dbl; }
+ void setDouble(double d) { dbl = d; }
+ bool isNaN() const { return (tag & QV4::Value::NotDouble_Mask) == QV4::Value::NaN_Mask; }
+#endif
+ inline bool isString() const;
+ inline bool isObject() const;
+ inline bool isInt32() {
+ if (tag == _Integer_Type)
+ return true;
+ if (isDouble()) {
+ double d = doubleValue();
+ int i = (int)d;
+ if (i == d) {
+ int_32 = i;
+ tag = _Integer_Type;
+ return true;
+ }
+ }
+ return false;
+ }
+ double asDouble() const {
+ if (tag == _Integer_Type)
+ return int_32;
return doubleValue();
- return toNumberImpl();
-}
+ }
-inline int Value::toInt32() const
-{
- if (integerCompatible())
+ bool booleanValue() const {
+ return int_32;
+ }
+ int integerValue() const {
return int_32;
- double d;
- if (isDouble())
- d = doubleValue();
- else
- d = toNumberImpl();
+ }
- const double D32 = 4294967296.0;
- const double D31 = D32 / 2.0;
+ String *stringValue() const {
+ return s;
+ }
+ Object *objectValue() const {
+ return o;
+ }
+ Managed *managed() const {
+ return m;
+ }
- if ((d >= -D31 && d < D31))
- return static_cast<int>(d);
+ quint64 rawValue() const {
+ return val;
+ }
- return Primitive::toInt32(d);
-}
+ static inline Value fromManaged(Managed *o);
+
+ int toUInt16() const;
+ inline int toInt32() const;
+ inline unsigned int toUInt32() const;
+
+ inline bool toBoolean() const;
+ double toInteger() const;
+ inline double toNumber() const;
+ double toNumberImpl() const;
+ QString toQStringNoThrow() const;
+ QString toQString() const;
+ String *toString(ExecutionContext *ctx) const;
+ Object *toObject(ExecutionContext *ctx) const;
+
+ inline bool isPrimitive() const;
+ inline bool tryIntegerConversion() {
+ bool b = integerCompatible();
+ if (b)
+ tag = _Integer_Type;
+ return b;
+ }
-inline unsigned int Value::toUInt32() const
-{
- return (unsigned int)toInt32();
-}
+ inline String *asString() const;
+ inline Managed *asManaged() const;
+ inline Object *asObject() const;
+ inline FunctionObject *asFunctionObject() const;
+ inline NumberObject *asNumberObject() const;
+ inline StringObject *asStringObject() const;
+ inline DateObject *asDateObject() const;
+ inline ArrayObject *asArrayObject() const;
+ inline ErrorObject *asErrorObject() const;
+ template<typename T> inline T *as() const;
-inline bool Value::toBoolean() const
-{
- switch (type()) {
- case Value::Undefined_Type:
- case Value::Null_Type:
- return false;
- case Value::Boolean_Type:
- case Value::Integer_Type:
- return (bool)int_32;
- case Value::Managed_Type:
- if (isString())
- return stringValue()->toQString().length() > 0;
- return true;
- default: // double
- return doubleValue() && !std::isnan(doubleValue());
- }
-}
+ inline uint asArrayIndex() const;
+ inline uint asArrayLength(bool *ok) const;
-inline uint Value::asArrayIndex() const
-{
-#if QT_POINTER_SIZE == 8
- if (!isNumber())
- return UINT_MAX;
- if (isInteger())
- return int_32 >= 0 ? (uint)int_32 : UINT_MAX;
-#else
- if (isInteger() && int_32 >= 0)
- return (uint)int_32;
- if (!isDouble())
- return UINT_MAX;
-#endif
- double d = doubleValue();
- uint idx = (uint)d;
- if (idx != d)
- return UINT_MAX;
- return idx;
-}
+ inline ExecutionEngine *engine() const;
-inline uint Value::asArrayLength(bool *ok) const
-{
- *ok = true;
- if (integerCompatible() && int_32 >= 0)
- return (uint)int_32;
- if (isDouble()) {
- double d = doubleValue();
- uint idx = (uint)d;
- if (idx != d) {
- *ok = false;
- return UINT_MAX;
- }
- return idx;
- }
- if (isString())
- return stringValue()->toUInt(ok);
+ ReturnedValue asReturnedValue() const { return val; }
+ static Value fromReturnedValue(ReturnedValue val) { Value v; v.val = val; return v; }
+ Value &operator=(ReturnedValue v) { val = v; return *this; }
+ template <typename T>
+ inline Value &operator=(Returned<T> *t);
- uint idx = toUInt32();
- double d = toNumber();
- if (d != idx) {
- *ok = false;
- return UINT_MAX;
- }
- return idx;
-}
+ // Section 9.12
+ bool sameValue(Value other) const;
-inline Object *Value::asObject() const
-{
- return isObject() ? objectValue() : 0;
-}
+ inline void mark(ExecutionEngine *e) const;
+};
-inline FunctionObject *Value::asFunctionObject() const
+inline Managed *Value::asManaged() const
{
- return isObject() ? managed()->asFunctionObject() : 0;
+ if (isManaged())
+ return managed();
+ return 0;
}
-inline NumberObject *Value::asNumberObject() const
+inline String *Value::asString() const
{
- return isObject() ? managed()->asNumberObject() : 0;
+ if (isString())
+ return stringValue();
+ return 0;
}
-inline StringObject *Value::asStringObject() const
+struct Q_QML_EXPORT Primitive : public Value
{
- return isObject() ? managed()->asStringObject() : 0;
-}
+ inline static Primitive emptyValue();
+ static inline Primitive fromBoolean(bool b);
+ static inline Primitive fromInt32(int i);
+ inline static Primitive undefinedValue();
+ static inline Primitive nullValue();
+ static inline Primitive fromDouble(double d);
+ static inline Primitive fromUInt32(uint i);
+
+ static double toInteger(double fromNumber);
+ static int toInt32(double value);
+ static unsigned int toUInt32(double value);
+
+ inline operator ValueRef();
+ Value asValue() const { return *this; }
+};
-inline DateObject *Value::asDateObject() const
+inline Primitive Primitive::undefinedValue()
{
- return isObject() ? managed()->asDateObject() : 0;
+ Primitive v;
+#if QT_POINTER_SIZE == 8
+ v.val = quint64(Undefined_Type) << Tag_Shift;
+#else
+ v.tag = Undefined_Type;
+ v.int_32 = 0;
+#endif
+ return v;
}
-inline ArrayObject *Value::asArrayObject() const
+inline Primitive Primitive::emptyValue()
{
- return isObject() ? managed()->asArrayObject() : 0;
+ Primitive v;
+ v.tag = Value::Empty_Type;
+ v.uint_32 = 0;
+ return v;
}
-inline ErrorObject *Value::asErrorObject() const
+inline Value Value::fromManaged(Managed *m)
{
- return isObject() ? managed()->asErrorObject() : 0;
+ if (!m)
+ return QV4::Primitive::undefinedValue();
+ Value v;
+#if QT_POINTER_SIZE == 8
+ v.m = m;
+#else
+ v.tag = Managed_Type;
+ v.m = m;
+#endif
+ return v;
}
-template<typename T>
-inline T *Value::as() const { Managed *m = isObject() ? managed() : 0; return m ? m->as<T>() : 0; }
-
-struct Q_QML_PRIVATE_EXPORT PersistentValuePrivate
+struct SafeValue : public Value
{
- PersistentValuePrivate(ReturnedValue v, ExecutionEngine *engine = 0, bool weak = false);
- virtual ~PersistentValuePrivate();
- SafeValue value;
- uint refcount;
- bool weak;
- QV4::ExecutionEngine *engine;
- PersistentValuePrivate **prev;
- PersistentValuePrivate *next;
-
- void init();
- void removeFromList();
- void ref() { ++refcount; }
- void deref();
- PersistentValuePrivate *detach(const ReturnedValue value, bool weak = false);
-
- bool checkEngine(QV4::ExecutionEngine *otherEngine) {
- if (!engine) {
- Q_ASSERT(!value.isObject());
- engine = otherEngine;
- }
- return (engine == otherEngine);
+ SafeValue &operator =(const ScopedValue &v);
+ template<typename T>
+ SafeValue &operator=(Returned<T> *t);
+ SafeValue &operator=(ReturnedValue v) {
+ val = v;
+ return *this;
}
-};
-
-class Q_QML_EXPORT PersistentValue
-{
-public:
- PersistentValue() : d(0) {}
- PersistentValue(const PersistentValue &other);
- PersistentValue &operator=(const PersistentValue &other);
-
- PersistentValue(const ValueRef val);
- PersistentValue(ReturnedValue val);
template<typename T>
- PersistentValue(Returned<T> *obj);
+ SafeValue &operator=(T *t) {
+ val = Value::fromManaged(t).val;
+ return *this;
+ }
+
template<typename T>
- PersistentValue(const Referenced<T> obj);
- PersistentValue &operator=(const ValueRef other);
- PersistentValue &operator =(ReturnedValue other);
+ SafeValue &operator=(const Scoped<T> &t);
+ SafeValue &operator=(const ValueRef v);
+ SafeValue &operator=(const Value &v) {
+ val = v.val;
+ return *this;
+ }
template<typename T>
- PersistentValue &operator=(Returned<T> *obj);
+ inline Returned<T> *as();
template<typename T>
- PersistentValue &operator=(const Referenced<T> obj);
- ~PersistentValue();
+ inline Referenced<T> asRef();
+};
- ReturnedValue value() const {
- return (d ? d->value.asReturnedValue() : Primitive::undefinedValue().asReturnedValue());
+template <typename T>
+struct Safe : public SafeValue
+{
+ template<typename X>
+ Safe &operator =(X *x) {
+ val = Value::fromManaged(x).val;
}
+ Safe &operator =(T *t);
+ Safe &operator =(const Scoped<T> &v);
+ Safe &operator =(const Referenced<T> &v);
+ Safe &operator =(Returned<T> *t);
- ExecutionEngine *engine() {
- if (!d)
- return 0;
- if (d->engine)
- return d->engine;
- Managed *m = d->value.asManaged();
- return m ? m->engine() : 0;
- }
+ Safe &operator =(const Safe<T> &t);
- bool isUndefined() const { return !d || d->value.isUndefined(); }
- bool isNullOrUndefined() const { return !d || d->value.isNullOrUndefined(); }
- void clear() {
- *this = PersistentValue();
- }
+ bool operator!() const { return !managed(); }
+
+ T *operator->() { return static_cast<T *>(managed()); }
+ const T *operator->() const { return static_cast<T *>(managed()); }
+ T *getPointer() const { return static_cast<T *>(managed()); }
+ Returned<T> *ret() const;
-private:
- friend struct ValueRef;
- PersistentValuePrivate *d;
+ void mark(ExecutionEngine *e) { if (managed()) managed()->mark(e); }
};
+typedef Safe<String> SafeString;
+typedef Safe<Object> SafeObject;
-class Q_QML_EXPORT WeakValue
+template<typename T>
+T *value_cast(const Value &v)
{
-public:
- WeakValue() : d(0) {}
- WeakValue(const ValueRef val);
- WeakValue(const WeakValue &other);
- WeakValue(ReturnedValue val);
- template<typename T>
- WeakValue(Returned<T> *obj);
- WeakValue &operator=(const WeakValue &other);
- WeakValue &operator=(const ValueRef other);
- WeakValue &operator =(const ReturnedValue &other);
- template<typename T>
- WeakValue &operator=(Returned<T> *obj);
-
- ~WeakValue();
-
- ReturnedValue value() const {
- return (d ? d->value.asReturnedValue() : Primitive::undefinedValue().asReturnedValue());
- }
-
- ExecutionEngine *engine() {
- if (!d)
- return 0;
- if (d->engine)
- return d->engine;
- Managed *m = d->value.asManaged();
- return m ? m->engine() : 0;
- }
+ return v.as<T>();
+}
- bool isUndefined() const { return !d || d->value.isUndefined(); }
- bool isNullOrUndefined() const { return !d || d->value.isNullOrUndefined(); }
- void clear() {
- *this = WeakValue();
- }
+template<typename T>
+ReturnedValue value_convert(ExecutionContext *ctx, const Value &v);
- void markOnce(ExecutionEngine *e);
-private:
- friend struct ValueRef;
- PersistentValuePrivate *d;
-};
-} // namespace QV4
+}
QT_END_NAMESPACE
-#endif
+#endif // QV4VALUE_DEF_P_H