// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QV4VALUE_P_H #define QV4VALUE_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include "qv4global_p.h" #include #include #include #include #include QT_BEGIN_NAMESPACE namespace QV4 { namespace Heap { struct Base; } struct Q_QML_EXPORT Value : public StaticValue { using ManagedPtr = Managed *; Value() = default; constexpr Value(quint64 val) : StaticValue(val) {} static constexpr Value fromStaticValue(StaticValue staticValue) { return {staticValue._val}; } inline bool isString() const; inline bool isStringOrSymbol() const; inline bool isSymbol() const; inline bool isObject() const; inline bool isFunctionObject() const; QML_NEARLY_ALWAYS_INLINE String *stringValue() const { if (!isString()) return nullptr; return reinterpret_cast(const_cast(this)); } QML_NEARLY_ALWAYS_INLINE StringOrSymbol *stringOrSymbolValue() const { if (!isStringOrSymbol()) return nullptr; return reinterpret_cast(const_cast(this)); } QML_NEARLY_ALWAYS_INLINE Symbol *symbolValue() const { if (!isSymbol()) return nullptr; return reinterpret_cast(const_cast(this)); } QML_NEARLY_ALWAYS_INLINE Object *objectValue() const { if (!isObject()) return nullptr; return reinterpret_cast(const_cast(this)); } QML_NEARLY_ALWAYS_INLINE ManagedPtr managed() const { if (!isManaged()) return nullptr; return reinterpret_cast(const_cast(this)); } QML_NEARLY_ALWAYS_INLINE Value::HeapBasePtr heapObject() const { return isManagedOrUndefined() ? m() : nullptr; } static inline Value fromHeapObject(HeapBasePtr m) { Value v; v.setM(m); return v; } int toUInt16() const; inline int toInt32() const; inline unsigned int toUInt32() const; qint64 toLength() const; inline qint64 toIndex() const; bool toBoolean() const { if (integerCompatible()) return static_cast(int_32()); return toBooleanImpl(*this); } static bool toBooleanImpl(Value val); double toInteger() const; inline ReturnedValue convertedToNumber() const; inline double toNumber() const; static double toNumberImpl(Value v); double toNumberImpl() const { return toNumberImpl(*this); } QString toQStringNoThrow() const; QString toQString() const; QString toQString(bool *ok) const; Heap::String *toString(ExecutionEngine *e) const { if (isString()) return reinterpret_cast(m()); return toString(e, *this); } QV4::PropertyKey toPropertyKey(ExecutionEngine *e) const; static Heap::String *toString(ExecutionEngine *e, Value val); Heap::Object *toObject(ExecutionEngine *e) const { if (isObject()) return reinterpret_cast(m()); return toObject(e, *this); } static Heap::Object *toObject(ExecutionEngine *e, Value val); inline bool isPrimitive() const; template const T *as() const { if (!isManaged()) return nullptr; Q_ASSERT(m()->internalClass->vtable); #if !defined(QT_NO_QOBJECT_CHECK) static_cast(this)->qt_check_for_QMANAGED_macro(static_cast(this)); #endif const VTable *vt = m()->internalClass->vtable; while (vt) { if (vt == T::staticVTable()) return static_cast(this); vt = vt->parent; } return nullptr; } template T *as() { if (isManaged()) return const_cast(const_cast(this)->as()); else return nullptr; } template inline T *cast() { return static_cast(managed()); } template inline const T *cast() const { return static_cast(managed()); } uint asArrayLength(bool *ok) const; static constexpr Value fromReturnedValue(ReturnedValue val) { return fromStaticValue(StaticValue::fromReturnedValue(val)); } // As per ES specs bool sameValue(Value other) const; bool sameValueZero(Value other) const; inline void mark(MarkStack *markStack); static double toInteger(double d) { return StaticValue::toInteger(d); } static int toInt32(double d) { return StaticValue::toInt32(d); } static unsigned int toUInt32(double d) { return StaticValue::toUInt32(d); } inline static constexpr Value emptyValue() { return fromStaticValue(StaticValue::emptyValue()); } static inline constexpr Value fromBoolean(bool b) { return fromStaticValue(StaticValue::fromBoolean(b)); } static inline constexpr Value fromInt32(int i) { return fromStaticValue(StaticValue::fromInt32(i)); } inline static constexpr Value undefinedValue() { return fromStaticValue(StaticValue::undefinedValue()); } static inline constexpr Value nullValue() { return fromStaticValue(StaticValue::nullValue()); } static inline Value fromDouble(double d) { return fromStaticValue(StaticValue::fromDouble(d)); } static inline Value fromUInt32(uint i) { return fromStaticValue(StaticValue::fromUInt32(i)); } Value &operator =(const ScopedValue &v); Value &operator=(ReturnedValue v) { StaticValue::operator=(v); return *this; } Value &operator=(ManagedPtr m) { if (!m) { setM(nullptr); } else { _val = reinterpret_cast(m)->_val; } return *this; } Value &operator=(HeapBasePtr o) { setM(o); return *this; } template Value &operator=(const Scoped &t); }; Q_STATIC_ASSERT(std::is_trivial_v); Q_STATIC_ASSERT(sizeof(Value) == sizeof(StaticValue)); template<> inline StaticValue &StaticValue::operator=(const Value &value) { _val = value._val; return *this; } template inline StaticValue &StaticValue::operator=(const Managed &m) { *static_cast(this) = m; return *this; } template<> inline Value &StaticValue::asValue() { return *static_cast(this); } template<> inline const Value &StaticValue::asValue() const { return *static_cast(this); } template<> inline Value *CallData::argValues() { return static_cast(static_cast(args)); } template<> inline const Value *CallData::argValues() const { return static_cast(static_cast(args)); } template inline Encode::Encode(HeapBase *o) { val = Value::fromHeapObject(o).asReturnedValue(); } inline void Value::mark(MarkStack *markStack) { HeapBasePtr o = heapObject(); if (o) o->mark(markStack); } inline bool Value::isString() const { HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isString; } bool Value::isStringOrSymbol() const { HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isStringOrSymbol; } bool Value::isSymbol() const { HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isStringOrSymbol && !b->internalClass->vtable->isString; } inline bool Value::isObject() const { HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isObject; } inline bool Value::isFunctionObject() const { HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isFunctionObject; } inline bool Value::isPrimitive() const { return !isObject(); } inline double Value::toNumber() const { if (isInteger()) return int_32(); if (isDouble()) return doubleValue(); return toNumberImpl(); } inline ReturnedValue Value::convertedToNumber() const { if (isInteger() || isDouble()) return asReturnedValue(); Value v; v.setDouble(toNumberImpl()); return v.asReturnedValue(); } inline ReturnedValue Heap::Base::asReturnedValue() const { return Value::fromHeapObject(const_cast(this)).asReturnedValue(); } // For source compat with older code in other modules using Primitive = Value; template ReturnedValue value_convert(ExecutionEngine *e, const Value &v); inline int Value::toInt32() const { if (Q_LIKELY(integerCompatible())) return int_32(); if (Q_LIKELY(isDouble())) return QJSNumberCoercion::toInteger(doubleValue()); return QJSNumberCoercion::toInteger(toNumberImpl()); } inline unsigned int Value::toUInt32() const { return static_cast(toInt32()); } inline qint64 Value::toLength() const { if (Q_LIKELY(integerCompatible())) return int_32() < 0 ? 0 : int_32(); double i = Value::toInteger(isDouble() ? doubleValue() : toNumberImpl()); if (i <= 0) return 0; if (i > (static_cast(1) << 53) - 1) return (static_cast(1) << 53) - 1; return static_cast(i); } inline qint64 Value::toIndex() const { qint64 idx; if (Q_LIKELY(integerCompatible())) { idx = int_32(); } else { idx = static_cast(Value::toInteger(isDouble() ? doubleValue() : toNumberImpl())); } if (idx > (static_cast(1) << 53) - 1) idx = -1; return idx; } inline double Value::toInteger() const { if (integerCompatible()) return int_32(); return Value::toInteger(isDouble() ? doubleValue() : toNumberImpl()); } template struct HeapValue : Value { static constexpr size_t offset = o; HeapBasePtr base() { HeapBasePtr base = reinterpret_cast(this) - (offset/sizeof(Heap::Base)); Q_ASSERT(base->inUse()); return base; } void set(EngineBase *e, const Value &newVal) { WriteBarrier::write(e, base(), data_ptr(), newVal.asReturnedValue()); } void set(EngineBase *e, HeapBasePtr b) { WriteBarrier::write(e, base(), data_ptr(), b->asReturnedValue()); } }; template struct ValueArray { static constexpr size_t offset = o; uint size; uint alloc; Value values[1]; Value::HeapBasePtr base() { Value::HeapBasePtr base = reinterpret_cast(this) - (offset/sizeof(Heap::Base)); Q_ASSERT(base->inUse()); return base; } void set(EngineBase *e, uint index, Value v) { WriteBarrier::write(e, base(), values[index].data_ptr(), v.asReturnedValue()); } void set(EngineBase *e, uint index, Value::HeapBasePtr b) { WriteBarrier::write(e, base(), values[index].data_ptr(), Value::fromHeapObject(b).asReturnedValue()); } inline const Value &operator[] (uint index) const { Q_ASSERT(index < alloc); return values[index]; } inline const Value *data() const { return values; } void mark(MarkStack *markStack) { for (Value *v = values, *end = values + alloc; v < end; ++v) v->mark(markStack); } }; // It's really important that the offset of values in this structure is // constant across all architecture, otherwise JIT cross-compiled code will // have wrong offsets between host and target. Q_STATIC_ASSERT(offsetof(ValueArray<0>, values) == 8); class OptionalReturnedValue { ReturnedValue value; public: OptionalReturnedValue() : value(Value::emptyValue().asReturnedValue()) {} explicit OptionalReturnedValue(ReturnedValue v) : value(v) { Q_ASSERT(!Value::fromReturnedValue(v).isEmpty()); } ReturnedValue operator->() const { return value; } ReturnedValue operator*() const { return value; } explicit operator bool() const { return !Value::fromReturnedValue(value).isEmpty(); } }; } QT_END_NAMESPACE #endif // QV4VALUE_DEF_P_H