// 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 QV4STRING_H #define QV4STRING_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 "qv4managed_p.h" #include #include "qv4enginebase_p.h" #include QT_BEGIN_NAMESPACE namespace QV4 { struct ExecutionEngine; struct PropertyKey; namespace Heap { struct Q_QML_EXPORT StringOrSymbol : Base { enum StringType { StringType_Symbol, StringType_Regular, StringType_ArrayIndex, StringType_Unknown, StringType_AddedString, StringType_SubString, StringType_Complex = StringType_AddedString }; void init() { Base::init(); new (&textStorage) QStringPrivate; } void init(QStringPrivate text) { Base::init(); new (&textStorage) QStringPrivate(std::move(text)); } mutable struct { alignas(QStringPrivate) unsigned char data[sizeof(QStringPrivate)]; } textStorage; mutable PropertyKey identifier; mutable uint subtype; mutable uint stringHash; static void markObjects(Heap::Base *that, MarkStack *markStack); void destroy(); QStringPrivate &text() const { return *reinterpret_cast(&textStorage); } inline QString toQString() const { QStringPrivate dd = text(); return QString(std::move(dd)); } void createHashValue() const; inline unsigned hashValue() const { if (subtype >= StringType_Unknown) createHashValue(); Q_ASSERT(subtype < StringType_Complex); return stringHash; } }; struct Q_QML_EXPORT String : StringOrSymbol { static void markObjects(Heap::Base *that, MarkStack *markStack); const VTable *vtable() const { return internalClass->vtable; } void init(const QString &text); void simplifyString() const; int length() const; std::size_t retainedTextSize() const { return subtype >= StringType_Complex ? 0 : (std::size_t(text().size) * sizeof(QChar)); } inline QString toQString() const { if (subtype >= StringType_Complex) simplifyString(); return StringOrSymbol::toQString(); } inline bool isEqualTo(const String *other) const { if (this == other) return true; if (hashValue() != other->hashValue()) return false; Q_ASSERT(subtype < StringType_Complex); if (identifier.isValid() && identifier == other->identifier) return true; if (subtype == Heap::String::StringType_ArrayIndex && other->subtype == Heap::String::StringType_ArrayIndex) return true; return toQString() == other->toQString(); } bool startsWithUpper() const; private: static void append(const String *data, QChar *ch); }; Q_STATIC_ASSERT(std::is_trivial_v); struct ComplexString : String { void init(String *l, String *n); void init(String *ref, int from, int len); mutable String *left; mutable String *right; union { mutable int largestSubLength; int from; }; int len; }; Q_STATIC_ASSERT(std::is_trivial_v); inline int String::length() const { // TODO: ensure that our strings never actually grow larger than INT_MAX return subtype < StringType_AddedString ? int(text().size) : static_cast(this)->len; } } struct Q_QML_EXPORT StringOrSymbol : public Managed { V4_MANAGED(StringOrSymbol, Managed) V4_NEEDS_DESTROY enum { IsStringOrSymbol = true }; private: inline void createPropertyKey() const; public: PropertyKey propertyKey() const { Q_ASSERT(d()->identifier.isValid()); return d()->identifier; } PropertyKey toPropertyKey() const; inline QString toQString() const { return d()->toQString(); } }; struct Q_QML_EXPORT String : public StringOrSymbol { V4_MANAGED(String, StringOrSymbol) Q_MANAGED_TYPE(String) V4_INTERNALCLASS(String) enum { IsString = true }; uchar subtype() const { return d()->subtype; } void setSubtype(uchar subtype) const { d()->subtype = subtype; } bool equals(String *other) const { return d()->isEqualTo(other->d()); } inline bool isEqualTo(const String *other) const { return d()->isEqualTo(other->d()); } inline bool lessThan(const String *other) { return toQString() < other->toQString(); } inline QString toQString() const { return d()->toQString(); } inline unsigned hashValue() const { return d()->hashValue(); } uint toUInt(bool *ok) const; // slow path Q_NEVER_INLINE void createPropertyKeyImpl() const; static uint createHashValue(const QChar *ch, int length, uint *subtype) { const QChar *end = ch + length; return calculateHashValue(ch, end, subtype); } static uint createHashValueDisallowingArrayIndex(const QChar *ch, int length, uint *subtype) { const QChar *end = ch + length; return calculateHashValue(ch, end, subtype); } static uint createHashValue(const char *ch, int length, uint *subtype) { const char *end = ch + length; return calculateHashValue(ch, end, subtype); } bool startsWithUpper() const { return d()->startsWithUpper(); } protected: static bool virtualIsEqualTo(Managed *that, Managed *o); static qint64 virtualGetLength(const Managed *m); public: enum IndicesBehavior {Default, DisallowArrayIndex}; template static inline uint calculateHashValue(const T *ch, const T* end, uint *subtype) { // array indices get their number as hash value uint h = UINT_MAX; if constexpr (Behavior != DisallowArrayIndex) { h = stringToArrayIndex(ch, end); if (h != UINT_MAX) { if (subtype) *subtype = Heap::StringOrSymbol::StringType_ArrayIndex; return h; } } while (ch < end) { h = 31 * h + charToUInt(ch); ++ch; } if (subtype) *subtype = (ch != end && charToUInt(ch) == '@') ? Heap::StringOrSymbol::StringType_Symbol : Heap::StringOrSymbol::StringType_Regular; return h; } }; struct ComplexString : String { typedef QV4::Heap::ComplexString Data; QV4::Heap::ComplexString *d_unchecked() const { return static_cast(m()); } QV4::Heap::ComplexString *d() const { QV4::Heap::ComplexString *dptr = d_unchecked(); dptr->_checkIsInitialized(); return dptr; } }; inline void StringOrSymbol::createPropertyKey() const { Q_ASSERT(!d()->identifier.isValid()); Q_ASSERT(isString()); static_cast(this)->createPropertyKeyImpl(); } inline PropertyKey StringOrSymbol::toPropertyKey() const { if (!d()->identifier.isValid()) createPropertyKey(); return d()->identifier; } template<> inline const StringOrSymbol *Value::as() const { return isManaged() && m()->internalClass->vtable->isStringOrSymbol ? static_cast(this) : nullptr; } template<> inline const String *Value::as() const { return isManaged() && m()->internalClass->vtable->isString ? static_cast(this) : nullptr; } template<> inline ReturnedValue value_convert(ExecutionEngine *e, const Value &v) { return v.toString(e)->asReturnedValue(); } } QT_END_NAMESPACE #endif