diff options
Diffstat (limited to 'src/qml/jsruntime/qv4value_p.h')
-rw-r--r-- | src/qml/jsruntime/qv4value_p.h | 647 |
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 |