/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qv4string_p.h" #include "qv4value_inl_p.h" #ifndef V4_BOOTSTRAP #include "qv4identifiertable_p.h" #include "qv4runtime_p.h" #include "qv4objectproto_p.h" #include "qv4stringobject_p.h" #endif #include using namespace QV4; static uint toArrayIndex(const QChar *ch, const QChar *end, bool *ok) { *ok = false; uint i = ch->unicode() - '0'; if (i > 9) return UINT_MAX; ++ch; // reject "01", "001", ... if (i == 0 && ch != end) return UINT_MAX; while (ch < end) { uint x = ch->unicode() - '0'; if (x > 9) return UINT_MAX; uint n = i*10 + x; if (n < i) // overflow return UINT_MAX; i = n; ++ch; } *ok = true; return i; } #ifndef V4_BOOTSTRAP static uint toArrayIndex(const char *ch, const char *end, bool *ok) { *ok = false; uint i = *ch - '0'; if (i > 9) return UINT_MAX; ++ch; // reject "01", "001", ... if (i == 0 && ch != end) return UINT_MAX; while (ch < end) { uint x = *ch - '0'; if (x > 9) return UINT_MAX; uint n = i*10 + x; if (n < i) // overflow return UINT_MAX; i = n; ++ch; } *ok = true; return i; } const ObjectVTable String::static_vtbl = { DEFINE_MANAGED_VTABLE_INT(String), 0, 0, get, getIndexed, put, putIndexed, query, queryIndexed, deleteProperty, deleteIndexedProperty, 0 /*getLookup*/, 0 /*setLookup*/, 0, 0 /*advanceIterator*/, }; void String::destroy(Managed *that) { static_cast(that)->~String(); } void String::markObjects(Managed *that, ExecutionEngine *e) { String *s = static_cast(that); if (s->d()->largestSubLength) { s->d()->left->mark(e); s->d()->right->mark(e); } } ReturnedValue String::get(Managed *m, String *name, bool *hasProperty) { ExecutionEngine *v4 = m->engine(); Scope scope(v4); ScopedString that(scope, static_cast(m)); if (name->equals(v4->id_length)) { if (hasProperty) *hasProperty = true; return Primitive::fromInt32(that->d()->text->size).asReturnedValue(); } PropertyAttributes attrs; Property *pd = v4->stringObjectClass->prototype->__getPropertyDescriptor__(name, &attrs); if (!pd || attrs.isGeneric()) { if (hasProperty) *hasProperty = false; return Primitive::undefinedValue().asReturnedValue(); } if (hasProperty) *hasProperty = true; return v4->stringObjectClass->prototype->getValue(that, pd, attrs); } ReturnedValue String::getIndexed(Managed *m, uint index, bool *hasProperty) { ExecutionEngine *engine = m->engine(); Scope scope(engine); ScopedString that(scope, static_cast(m)); if (index < static_cast(that->d()->text->size)) { if (hasProperty) *hasProperty = true; return Encode(engine->newString(that->toQString().mid(index, 1))); } PropertyAttributes attrs; Property *pd = engine->stringObjectClass->prototype->__getPropertyDescriptor__(index, &attrs); if (!pd || attrs.isGeneric()) { if (hasProperty) *hasProperty = false; return Primitive::undefinedValue().asReturnedValue(); } if (hasProperty) *hasProperty = true; return engine->stringObjectClass->prototype->getValue(that, pd, attrs); } void String::put(Managed *m, String *name, const ValueRef value) { Scope scope(m->engine()); if (scope.hasException()) return; ScopedString that(scope, static_cast(m)); Scoped o(scope, that->engine()->newStringObject(that)); o->put(name, value); } void String::putIndexed(Managed *m, uint index, const ValueRef value) { Scope scope(m->engine()); if (scope.hasException()) return; ScopedString that(scope, static_cast(m)); Scoped o(scope, that->engine()->newStringObject(that)); o->putIndexed(index, value); } PropertyAttributes String::query(const Managed *m, String *name) { uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return queryIndexed(m, idx); return Attr_Invalid; } PropertyAttributes String::queryIndexed(const Managed *m, uint index) { const String *that = static_cast(m); return (index < static_cast(that->d()->text->size)) ? Attr_NotConfigurable|Attr_NotWritable : Attr_Invalid; } bool String::deleteProperty(Managed *, String *) { return false; } bool String::deleteIndexedProperty(Managed *, uint) { return false; } bool String::isEqualTo(Managed *t, Managed *o) { if (t == o) return true; if (!o->internalClass()->vtable->isString) return false; String *that = static_cast(t); String *other = static_cast(o); if (that->hashValue() != other->hashValue()) return false; if (that->identifier() && that->identifier() == other->identifier()) return true; if (that->subtype() >= StringType_UInt && that->subtype() == other->subtype()) return true; return that->toQString() == other->toQString(); } String::String(ExecutionEngine *engine, const QString &text) : Managed(engine->stringClass) { Data *data = d(); data->text = const_cast(text).data_ptr(); data->text->ref.ref(); data->identifier = 0; data->stringHash = UINT_MAX; data->largestSubLength = 0; data->len = d()->text->size; setSubtype(StringType_Unknown); } String::String(ExecutionEngine *engine, String *l, String *r) : Managed(engine->stringClass) { setSubtype(StringType_Unknown); Data *data = d(); data->left = l; data->right = r; data->stringHash = UINT_MAX; data->largestSubLength = qMax(l->d()->largestSubLength, r->d()->largestSubLength); data->len = l->d()->len + r->d()->len; if (!l->d()->largestSubLength && l->d()->len > d()->largestSubLength) d()->largestSubLength = l->d()->len; if (!r->d()->largestSubLength && r->d()->len > d()->largestSubLength) d()->largestSubLength = r->d()->len; // make sure we don't get excessive depth in our strings if (d()->len > 256 && d()->len >= 2*d()->largestSubLength) simplifyString(); } uint String::toUInt(bool *ok) const { *ok = true; if (subtype() == StringType_Unknown) createHashValue(); if (subtype() >= StringType_UInt) return d()->stringHash; // ### this conversion shouldn't be required double d = RuntimeHelpers::stringToNumber(toQString()); uint l = (uint)d; if (d == l) return l; *ok = false; return UINT_MAX; } bool String::equals(String *other) const { if (this == other) return true; if (hashValue() != other->hashValue()) return false; if (identifier() && identifier() == other->identifier()) return true; if (subtype() >= StringType_UInt && subtype() == other->subtype()) return true; return toQString() == other->toQString(); } void String::makeIdentifierImpl() const { if (d()->largestSubLength) simplifyString(); Q_ASSERT(!d()->largestSubLength); engine()->identifierTable->identifier(this); } void String::simplifyString() const { Q_ASSERT(d()->largestSubLength); int l = length(); QString result(l, Qt::Uninitialized); QChar *ch = const_cast(result.constData()); recursiveAppend(ch); d()->text = result.data_ptr(); d()->text->ref.ref(); d()->identifier = 0; d()->largestSubLength = 0; } QChar *String::recursiveAppend(QChar *ch) const { if (d()->largestSubLength) { ch = d()->left->recursiveAppend(ch); ch = d()->right->recursiveAppend(ch); } else { memcpy(ch, d()->text->data(), d()->text->size*sizeof(QChar)); ch += d()->text->size; } return ch; } void String::createHashValue() const { if (d()->largestSubLength) simplifyString(); Q_ASSERT(!d()->largestSubLength); const QChar *ch = reinterpret_cast(d()->text->data()); const QChar *end = ch + d()->text->size; // array indices get their number as hash value bool ok; d()->stringHash = ::toArrayIndex(ch, end, &ok); if (ok) { setSubtype((d()->stringHash == UINT_MAX) ? StringType_UInt : StringType_ArrayIndex); return; } uint h = 0xffffffff; while (ch < end) { h = 31 * h + ch->unicode(); ++ch; } d()->stringHash = h; setSubtype(StringType_Regular); } uint String::createHashValue(const QChar *ch, int length) { const QChar *end = ch + length; // array indices get their number as hash value bool ok; uint stringHash = ::toArrayIndex(ch, end, &ok); if (ok) return stringHash; uint h = 0xffffffff; while (ch < end) { h = 31 * h + ch->unicode(); ++ch; } return h; } uint String::createHashValue(const char *ch, int length) { const char *end = ch + length; // array indices get their number as hash value bool ok; uint stringHash = ::toArrayIndex(ch, end, &ok); if (ok) return stringHash; uint h = 0xffffffff; while (ch < end) { if ((uchar)(*ch) >= 0x80) return UINT_MAX; h = 31 * h + *ch; ++ch; } return h; } uint String::getLength(const Managed *m) { return static_cast(m)->length(); } #endif // V4_BOOTSTRAP uint String::toArrayIndex(const QString &str) { bool ok; return ::toArrayIndex(str.constData(), str.constData() + str.length(), &ok); }