// 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 #include "qv4string_p.h" #include "qv4value_p.h" #include "qv4identifiertable_p.h" #include "qv4runtime_p.h" #include #include #include using namespace QV4; void Heap::StringOrSymbol::markObjects(Heap::Base *that, MarkStack *markStack) { StringOrSymbol *s = static_cast(that); Heap::StringOrSymbol *id = s->identifier.asStringOrSymbol(); if (id) id->mark(markStack); } void Heap::String::markObjects(Heap::Base *that, MarkStack *markStack) { StringOrSymbol::markObjects(that, markStack); String *s = static_cast(that); if (s->subtype < StringType_Complex) return; ComplexString *cs = static_cast(s); if (cs->subtype == StringType_AddedString) { cs->left->mark(markStack); cs->right->mark(markStack); } else { Q_ASSERT(cs->subtype == StringType_SubString); cs->left->mark(markStack); } } DEFINE_MANAGED_VTABLE(StringOrSymbol); DEFINE_MANAGED_VTABLE(String); bool String::virtualIsEqualTo(Managed *t, Managed *o) { if (t == o) return true; if (!o->vtable()->isString) return false; return static_cast(t)->isEqualTo(static_cast(o)); } void Heap::String::init(const QString &t) { QString mutableText(t); StringOrSymbol::init(mutableText.data_ptr()); subtype = String::StringType_Unknown; } void Heap::ComplexString::init(String *l, String *r) { StringOrSymbol::init(); subtype = String::StringType_AddedString; left = l; right = r; len = left->length() + right->length(); if (left->subtype >= StringType_Complex) largestSubLength = static_cast(left)->largestSubLength; else largestSubLength = left->length(); if (right->subtype >= StringType_Complex) largestSubLength = qMax(largestSubLength, static_cast(right)->largestSubLength); else largestSubLength = qMax(largestSubLength, right->length()); // make sure we don't get excessive depth in our strings if (len > 256 && len >= 2*largestSubLength) simplifyString(); } void Heap::ComplexString::init(Heap::String *ref, int from, int len) { Q_ASSERT(ref->length() >= from + len); StringOrSymbol::init(); subtype = String::StringType_SubString; left = ref; this->from = from; this->len = len; } void Heap::StringOrSymbol::destroy() { if (subtype < Heap::String::StringType_AddedString) { internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage( qptrdiff(-text()->size) * qptrdiff(sizeof(QChar))); } text().~QStringPrivate(); Base::destroy(); } uint String::toUInt(bool *ok) const { *ok = true; if (subtype() >= Heap::String::StringType_Unknown) d()->createHashValue(); if (subtype() == Heap::String::StringType_ArrayIndex) return d()->stringHash; // required for UINT_MAX or numbers starting with a leading 0 double d = RuntimeHelpers::stringToNumber(toQString()); uint l = (uint)d; if (d == l) return l; *ok = false; return UINT_MAX; } void String::createPropertyKeyImpl() const { if (d()->subtype >= Heap::String::StringType_AddedString) d()->simplifyString(); Q_ASSERT(d()->subtype < Heap::String::StringType_AddedString); engine()->identifierTable->asPropertyKey(this); } void Heap::String::simplifyString() const { Q_ASSERT(subtype >= StringType_AddedString); int l = length(); QString result(l, Qt::Uninitialized); QChar *ch = const_cast(result.constData()); append(this, ch); text() = result.data_ptr(); const ComplexString *cs = static_cast(this); identifier = PropertyKey::invalid(); cs->left = cs->right = nullptr; internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage( qptrdiff(text().size) * qptrdiff(sizeof(QChar))); subtype = StringType_Unknown; } bool Heap::String::startsWithUpper() const { if (subtype == StringType_AddedString) return static_cast(this)->left->startsWithUpper(); const Heap::String *str = this; int offset = 0; if (subtype == StringType_SubString) { const ComplexString *cs = static_cast(this); if (!cs->len) return false; // simplification here is not ideal, but hopefully not a common case. if (cs->left->subtype >= Heap::String::StringType_Complex) cs->left->simplifyString(); str = cs->left; offset = cs->from; } Q_ASSERT(str->subtype < Heap::String::StringType_Complex); return str->text().size > offset && QChar::isUpper(str->text().data()[offset]); } void Heap::String::append(const String *data, QChar *ch) { // in-order visitation with explicit stack // where leaf nodes are "real" strings that get appended to ch enum StatusTag : bool { NotVisited, Visited }; using Pointer = QTaggedPointer; std::vector worklist; worklist.reserve(32); worklist.push_back(Pointer(data)); while (!worklist.empty()) { Pointer item = worklist.back(); if (item.tag() == Visited) { Q_ASSERT(item->subtype == StringType_AddedString); const ComplexString *cs = static_cast(item.data()); worklist.pop_back(); worklist.push_back(Pointer(cs->right)); continue; } if (item->subtype == StringType_AddedString) { // we need to keep the node in the worklist, as we still need to handle "right" worklist.back().setTag(Visited); const ComplexString *cs = static_cast(item.data()); worklist.push_back(Pointer(cs->left)); } else if (item->subtype == StringType_SubString) { worklist.pop_back(); const ComplexString *cs = static_cast(item.data()); memcpy(ch, cs->left->toQString().constData() + cs->from, cs->len*sizeof(QChar)); ch += cs->len; } else { worklist.pop_back(); memcpy(static_cast(ch), item->text().data(), item->text().size * sizeof(QChar)); ch += item->text().size; } } } void Heap::StringOrSymbol::createHashValue() const { if (subtype >= StringType_AddedString) { Q_ASSERT(internalClass->vtable->isString); static_cast(this)->simplifyString(); } Q_ASSERT(subtype < StringType_AddedString); const QChar *ch = reinterpret_cast(text().data()); const QChar *end = ch + text().size; stringHash = QV4::String::calculateHashValue(ch, end, &subtype); } qint64 String::virtualGetLength(const Managed *m) { return static_cast(m)->d()->length(); }