diff options
Diffstat (limited to 'src/qml/jsruntime')
48 files changed, 353 insertions, 226 deletions
diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index 4edfe2a763..dc17ea4e54 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -30,7 +30,7 @@ namespace Heap { Member(class, NoMark, quint64, mapped) DECLARE_HEAP_OBJECT(ArgumentsObject, Object) { - DECLARE_MARKOBJECTS(ArgumentsObject); + DECLARE_MARKOBJECTS(ArgumentsObject) enum { LengthPropertyIndex = 0, SymbolIteratorPropertyIndex = 1, diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h index cc322b3f01..b955618cbe 100644 --- a/src/qml/jsruntime/qv4arraybuffer_p.h +++ b/src/qml/jsruntime/qv4arraybuffer_p.h @@ -60,7 +60,10 @@ private: return *reinterpret_cast<QArrayDataPointer<char> *>(&arrayDataPointerStorage); } - std::aligned_storage_t<sizeof(QArrayDataPointer<char>), alignof(QArrayDataPointer<char>)> + template <typename T> + struct storage_t { alignas(T) unsigned char data[sizeof(T)]; }; + + storage_t<QArrayDataPointer<char>> arrayDataPointerStorage; bool isShared; }; diff --git a/src/qml/jsruntime/qv4arrayiterator_p.h b/src/qml/jsruntime/qv4arrayiterator_p.h index 2682dc79d0..ff00f99bea 100644 --- a/src/qml/jsruntime/qv4arrayiterator_p.h +++ b/src/qml/jsruntime/qv4arrayiterator_p.h @@ -33,7 +33,7 @@ namespace Heap { Member(class, NoMark, quint32, nextIndex) DECLARE_HEAP_OBJECT(ArrayIteratorObject, Object) { - DECLARE_MARKOBJECTS(ArrayIteratorObject); + DECLARE_MARKOBJECTS(ArrayIteratorObject) void init(Object *obj, QV4::ExecutionEngine *engine) { Object::init(); diff --git a/src/qml/jsruntime/qv4compilationunitmapper.cpp b/src/qml/jsruntime/qv4compilationunitmapper.cpp index 0479b8b8f1..61b0f1d497 100644 --- a/src/qml/jsruntime/qv4compilationunitmapper.cpp +++ b/src/qml/jsruntime/qv4compilationunitmapper.cpp @@ -29,6 +29,11 @@ public: s_staticUnits.insert(file, staticUnit); } + void remove(const QString &file) + { + s_staticUnits.remove(file); + } + private: QMutexLocker<QMutex> m_lock; @@ -69,4 +74,10 @@ CompiledData::Unit *CompilationUnitMapper::get( return data; } +void CompilationUnitMapper::invalidate(const QString &cacheFilePath) +{ + StaticUnitCache cache; + cache.remove(cacheFilePath); +} + QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4compilationunitmapper_p.h b/src/qml/jsruntime/qv4compilationunitmapper_p.h index 0d3ec4eeee..07952be62c 100644 --- a/src/qml/jsruntime/qv4compilationunitmapper_p.h +++ b/src/qml/jsruntime/qv4compilationunitmapper_p.h @@ -33,6 +33,7 @@ public: CompiledData::Unit *get( const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString); + static void invalidate(const QString &cacheFilePath); private: CompiledData::Unit *open( diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index 66f06ff96a..82a9472223 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -29,7 +29,7 @@ namespace Heap { Member(class, Pointer, Object *, activation) DECLARE_HEAP_OBJECT(ExecutionContext, Base) { - DECLARE_MARKOBJECTS(ExecutionContext); + DECLARE_MARKOBJECTS(ExecutionContext) enum ContextType { Type_GlobalContext = 0x1, @@ -68,7 +68,7 @@ Q_STATIC_ASSERT(offsetof(ExecutionContextData, activation) == offsetof(Execution Member(class, ValueArray, ValueArray, locals) DECLARE_HEAP_OBJECT(CallContext, ExecutionContext) { - DECLARE_MARKOBJECTS(CallContext); + DECLARE_MARKOBJECTS(CallContext) void init() { diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h index 73f31d8f8c..ab2e1e589a 100644 --- a/src/qml/jsruntime/qv4dataview_p.h +++ b/src/qml/jsruntime/qv4dataview_p.h @@ -33,7 +33,7 @@ struct DataViewCtor : FunctionObject { Member(class, NoMark, uint, byteOffset) DECLARE_HEAP_OBJECT(DataView, Object) { - DECLARE_MARKOBJECTS(DataView); + DECLARE_MARKOBJECTS(DataView) void init() { Object::init(); } }; diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 769b8c161a..1fb6d92183 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -343,7 +343,7 @@ static inline double ParseString(const QString &s, double localTZA) }; const QChar *ch = s.constData(); - const QChar *end = ch + s.length(); + const QChar *end = ch + s.size(); uint format = Year; int current = 0; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 17cea99fd8..30924dd76d 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -324,6 +324,9 @@ void ExecutionEngine::initializeStaticMembers() #elif defined(Q_OS_ANDROID) // In experiments, it started crashing at 1059. s_maxCallDepth = 1000; +#elif defined(Q_OS_WIN) + // We've seen crashes around 750. + s_maxCallDepth = 640; #else s_maxCallDepth = 1234; #endif @@ -391,12 +394,15 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) } } + // We allocate guard pages around our stacks. + const size_t guardPages = 2 * WTF::pageSize(); + memoryManager = new QV4::MemoryManager(this); // reserve space for the JS stack // we allow it to grow to a bit more than m_maxJSStackSize, as we can overshoot due to ScopedValues // allocated outside of JIT'ed methods. *jsStack = WTF::PageAllocation::allocate( - s_maxJSStackSize + 256*1024, WTF::OSAllocator::JSVMStackPages, + s_maxJSStackSize + 256*1024 + guardPages, WTF::OSAllocator::JSVMStackPages, /* writable */ true, /* executable */ false, /* includesGuardPages */ true); jsStackBase = (Value *)jsStack->base(); #ifdef V4_USE_VALGRIND @@ -405,9 +411,9 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsStackTop = jsStackBase; - *gcStack = WTF::PageAllocation::allocate(s_maxGCStackSize, WTF::OSAllocator::JSVMStackPages, - /* writable */ true, /* executable */ false, - /* includesGuardPages */ true); + *gcStack = WTF::PageAllocation::allocate( + s_maxGCStackSize + guardPages, WTF::OSAllocator::JSVMStackPages, + /* writable */ true, /* executable */ false, /* includesGuardPages */ true); exceptionValue = jsAlloca(1); *exceptionValue = Encode::undefined(); @@ -949,13 +955,13 @@ Heap::Object *ExecutionEngine::newObject(Heap::InternalClass *internalClass) Heap::String *ExecutionEngine::newString(const QString &s) { - return memoryManager->allocWithStringData<String>(s.length() * sizeof(QChar), s); + return memoryManager->allocWithStringData<String>(s.size() * sizeof(QChar), s); } Heap::String *ExecutionEngine::newIdentifier(const QString &text) { Scope scope(this); - ScopedString s(scope, memoryManager->allocWithStringData<String>(text.length() * sizeof(QChar), text)); + ScopedString s(scope, memoryManager->allocWithStringData<String>(text.size() * sizeof(QChar), text)); s->toPropertyKey(); return s->d(); } @@ -1511,6 +1517,12 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, QMet if (metaType == QMetaType::fromType<bool>()) return QVariant(value.toBoolean()); + if (metaType == QMetaType::fromType<double>()) + return QVariant(value.toNumber()); + + if (metaType == QMetaType::fromType<float>()) + return QVariant(float(value.toNumber())); + if (metaType == QMetaType::fromType<QJsonValue>()) return QVariant::fromValue(QV4::JsonObject::toJsonValue(value)); @@ -1842,23 +1854,17 @@ QV4::ReturnedValue ExecutionEngine::fromData( // directly against QList<QObject*>? const QList<QObject *> &list = *(const QList<QObject *>*)ptr; QV4::ScopedArrayObject a(scope, newArrayObject()); - a->arrayReserve(list.count()); + a->arrayReserve(list.size()); QV4::ScopedValue v(scope); - for (int ii = 0; ii < list.count(); ++ii) + for (int ii = 0; ii < list.size(); ++ii) a->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(this, list.at(ii)))); - a->setArrayLengthUnchecked(list.count()); + a->setArrayLengthUnchecked(list.size()); return a.asReturnedValue(); } else if (auto flags = metaType.flags(); flags & QMetaType::PointerToQObject) { - QV4::ReturnedValue ret = QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr)); - if (!flags.testFlag(QMetaType::IsConst)) - return ret; - QV4::ScopedValue v(scope, ret); - if (auto obj = v->as<Object>()) { - obj->setInternalClass(obj->internalClass()->cryopreserved()); - return obj->asReturnedValue(); - } else { - return ret; - } + if (flags.testFlag(QMetaType::IsConst)) + return QV4::QObjectWrapper::wrapConst(this, *reinterpret_cast<QObject* const *>(ptr)); + else + return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr)); } bool succeeded = false; @@ -1981,6 +1987,25 @@ int ExecutionEngine::maxGCStackSize() const return s_maxGCStackSize; } +/*! + \internal + Returns \a length converted to int if its safe to + pass to \c Scope::alloc. + Otherwise it throws a RangeError, and returns 0. + */ +int ExecutionEngine::safeForAllocLength(qint64 len64) +{ + if (len64 < 0ll || len64 > qint64(std::numeric_limits<int>::max())) { + throwRangeError(QStringLiteral("Invalid array length.")); + return 0; + } + if (len64 > qint64(this->jsStackLimit - this->jsStackTop)) { + throwRangeError(QStringLiteral("Array too large for apply().")); + return 0; + } + return len64; +} + ReturnedValue ExecutionEngine::global() { return globalObject->asReturnedValue(); @@ -2252,7 +2277,7 @@ int ExecutionEngine::consoleCountHelper(const QString &file, quint16 line, quint void ExecutionEngine::setExtensionData(int index, Deletable *data) { - if (m_extensionData.count() <= index) + if (m_extensionData.size() <= index) m_extensionData.resize(index + 1); if (m_extensionData.at(index)) diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index d00495eff0..c6b97273e3 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -650,6 +650,7 @@ public: int maxGCStackSize() const; bool checkStackLimits(); + int safeForAllocLength(qint64 len64); bool canJIT(Function *f = nullptr) { @@ -702,7 +703,7 @@ public: void setExtensionData(int, Deletable *); Deletable *extensionData(int index) const { - if (index < m_extensionData.count()) + if (index < m_extensionData.size()) return m_extensionData[index]; else return nullptr; @@ -736,6 +737,9 @@ public: QV4::ExecutionContext *ctxt, int argc, const QV4::Value *argv); private: + template<int Frames> + friend struct ExecutionEngineCallDepthRecorder; + QV4::ReturnedValue fromData(QMetaType type, const void *ptr, const QVariant *variant = nullptr); static void initializeStaticMembers(); @@ -772,12 +776,15 @@ private: #define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \ ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4); +template<int Frames = 1> struct ExecutionEngineCallDepthRecorder { ExecutionEngine *ee; - ExecutionEngineCallDepthRecorder(ExecutionEngine *e): ee(e) { ++ee->callDepth; } - ~ExecutionEngineCallDepthRecorder() { --ee->callDepth; } + ExecutionEngineCallDepthRecorder(ExecutionEngine *e): ee(e) { ee->callDepth += Frames; } + ~ExecutionEngineCallDepthRecorder() { ee->callDepth -= Frames; } + + bool hasOverflow() const { return ee->callDepth >= ExecutionEngine::s_maxCallDepth; } }; inline bool ExecutionEngine::checkStackLimits() diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index f3adadc887..eb449ba293 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -120,7 +120,7 @@ ReturnedValue ErrorObject::method_get_stack(const FunctionObject *b, const Value return v4->throwTypeError(); if (!This->d()->stack) { QString trace; - for (int i = 0; i < This->d()->stackTrace->count(); ++i) { + for (int i = 0; i < This->d()->stackTrace->size(); ++i) { if (i > 0) trace += QLatin1Char('\n'); const StackFrame &frame = This->d()->stackTrace->at(i); diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index c8ebe11f30..3d9069b587 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -31,7 +31,7 @@ namespace Heap { Member(class, Pointer, String *, stack) DECLARE_HEAP_OBJECT(ErrorObject, Object) { - DECLARE_MARKOBJECTS(ErrorObject); + DECLARE_MARKOBJECTS(ErrorObject) enum ErrorType { Error, EvalError, diff --git a/src/qml/jsruntime/qv4executableallocator.cpp b/src/qml/jsruntime/qv4executableallocator.cpp index f8b005e914..5a63230858 100644 --- a/src/qml/jsruntime/qv4executableallocator.cpp +++ b/src/qml/jsruntime/qv4executableallocator.cpp @@ -125,7 +125,7 @@ ExecutableAllocator::ExecutableAllocator() ExecutableAllocator::~ExecutableAllocator() { - for (ChunkOfPages *chunk : qAsConst(chunks)) { + for (ChunkOfPages *chunk : std::as_const(chunks)) { for (Allocation *allocation = chunk->firstAllocation; allocation; allocation = allocation->next) if (!allocation->free) allocation->invalidate(); diff --git a/src/qml/jsruntime/qv4executableallocator_p.h b/src/qml/jsruntime/qv4executableallocator_p.h index d6551599e7..7fd8a10cdb 100644 --- a/src/qml/jsruntime/qv4executableallocator_p.h +++ b/src/qml/jsruntime/qv4executableallocator_p.h @@ -79,8 +79,8 @@ public: }; // for debugging / unit-testing - int freeAllocationCount() const { return freeAllocations.count(); } - int chunkCount() const { return chunks.count(); } + int freeAllocationCount() const { return freeAllocations.size(); } + int chunkCount() const { return chunks.size(); } struct ChunkOfPages { diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index 3fe031501f..2affa4f6dd 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -297,7 +297,7 @@ void ExecutableCompilationUnit::unlink() delete [] runtimeLookups; runtimeLookups = nullptr; - for (QV4::Function *f : qAsConst(runtimeFunctions)) + for (QV4::Function *f : std::as_const(runtimeFunctions)) f->destroy(); runtimeFunctions.clear(); @@ -325,14 +325,14 @@ void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack) if (runtimeClasses[i]) runtimeClasses[i]->mark(markStack); } - for (QV4::Function *f : qAsConst(runtimeFunctions)) + for (QV4::Function *f : std::as_const(runtimeFunctions)) if (f && f->internalClass) f->internalClass->mark(markStack); - for (QV4::Heap::InternalClass *c : qAsConst(runtimeBlocks)) + for (QV4::Heap::InternalClass *c : std::as_const(runtimeBlocks)) if (c) c->mark(markStack); - for (QV4::Heap::Object *o : qAsConst(templateObjects)) + for (QV4::Heap::Object *o : std::as_const(templateObjects)) if (o) o->mark(markStack); @@ -819,8 +819,14 @@ bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorSt return CompiledData::SaveableUnitPointer(unitData()).saveToDisk<char>( [&unitUrl, errorString](const char *data, quint32 size) { - return CompiledData::SaveableUnitPointer::writeDataToFile(localCacheFilePath(unitUrl), data, - size, errorString); + const QString cachePath = localCacheFilePath(unitUrl); + if (CompiledData::SaveableUnitPointer::writeDataToFile( + cachePath, data, size, errorString)) { + CompilationUnitMapper::invalidate(cachePath); + return true; + } + + return false; }); } @@ -832,7 +838,7 @@ bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorSt bool ResolvedTypeReferenceMap::addToHash( QCryptographicHash *hash, QHash<quintptr, QByteArray> *checksums) const { - std::vector<int> keys (count()); + std::vector<int> keys (size()); int i = 0; for (auto it = constBegin(), end = constEnd(); it != end; ++it) { keys[i] = it.key(); @@ -864,7 +870,7 @@ QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Bind // This code must match that in the qsTr() implementation const QString &path = fileName(); int lastSlash = path.lastIndexOf(QLatin1Char('/')); - QStringView context = (lastSlash > -1) ? QStringView{path}.mid(lastSlash + 1, path.length() - lastSlash - 5) + QStringView context = (lastSlash > -1) ? QStringView{path}.mid(lastSlash + 1, path.size() - lastSlash - 5) : QStringView(); QByteArray contextUtf8 = context.toUtf8(); QByteArray comment = stringAt(translation.commentIndex).toUtf8(); diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 1be093f0a4..f756575ea8 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -111,7 +111,7 @@ Function::Function(ExecutionEngine *engine, const QQmlPrivate::AOTCompiledFuncti , aotFunction(aotFunction) { internalClass = engine->internalClasses(EngineBase::Class_CallContext); - nFormals = aotFunction->argumentTypes.length(); + nFormals = aotFunction->argumentTypes.size(); } Function::~Function() @@ -127,7 +127,7 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr QStringList parameterNames; // Resolve duplicate parameter names: - for (int i = 0, ei = parameters.count(); i != ei; ++i) { + for (int i = 0, ei = parameters.size(); i != ei; ++i) { const QByteArray ¶m = parameters.at(i); int duplicate = -1; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 186535cb82..b38c29a6c5 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -351,36 +351,31 @@ ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, cons if (!arr) return v4->throwTypeError(); - const qint64 len64 = arr->getLength(); - if (len64 < 0ll || len64 > qint64(std::numeric_limits<int>::max())) - return v4->throwRangeError(QStringLiteral("Invalid array length.")); - if (len64 > qint64(v4->jsStackLimit - v4->jsStackTop)) - return v4->throwRangeError(QStringLiteral("Array too large for apply().")); - - const uint len = uint(len64); - Scope scope(v4); + const int len = v4->safeForAllocLength(arr->getLength()); + CHECK_EXCEPTION(); + Value *arguments = scope.alloc<Scope::Uninitialized>(len); if (len) { if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast<ArgumentsObject>()->fullyCreated()) { QV4::ArgumentsObject *a = arr->cast<ArgumentsObject>(); - int l = qMin(len, (uint)a->d()->context->argc()); + int l = qMin(len, a->d()->context->argc()); memcpy(arguments, a->d()->context->args(), l*sizeof(Value)); - for (quint32 i = l; i < len; ++i) + for (int i = l; i < len; ++i) arguments[i] = Value::undefinedValue(); } else if (arr->arrayType() == Heap::ArrayData::Simple && !arr->protoHasArray()) { auto sad = static_cast<Heap::SimpleArrayData *>(arr->arrayData()); - uint alen = sad ? sad->values.size : 0; + int alen = sad ? sad->values.size : 0; if (alen > len) alen = len; - for (uint i = 0; i < alen; ++i) + for (int i = 0; i < alen; ++i) arguments[i] = sad->data(i); - for (quint32 i = alen; i < len; ++i) + for (int i = alen; i < len; ++i) arguments[i] = Value::undefinedValue(); } else { // need to init the arguments array, as the get() calls below can have side effects memset(arguments, 0, len*sizeof(Value)); - for (quint32 i = 0; i < len; ++i) + for (int i = 0; i < len; ++i) arguments[i] = arr->get(i); } } diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index b42c55f5a8..e32fce28ba 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -40,7 +40,7 @@ namespace Heap { Member(class, NoMark, bool, canBeTailCalled) DECLARE_HEAP_OBJECT(FunctionObject, Object) { - DECLARE_MARKOBJECTS(FunctionObject); + DECLARE_MARKOBJECTS(FunctionObject) enum { Index_ProtoConstructor = 0, Index_Prototype = 0, @@ -125,7 +125,7 @@ struct DefaultClassConstructorFunction : FunctionObject Member(class, Pointer, MemberData *, boundArgs) DECLARE_HEAP_OBJECT(BoundFunction, FunctionObject) { - DECLARE_MARKOBJECTS(BoundFunction); + DECLARE_MARKOBJECTS(BoundFunction) void init(QV4::ExecutionContext *scope, QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs); }; diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h index 4a84bec748..55ccc133aa 100644 --- a/src/qml/jsruntime/qv4generatorobject_p.h +++ b/src/qml/jsruntime/qv4generatorobject_p.h @@ -57,7 +57,7 @@ struct GeneratorPrototype : FunctionObject { Member(class, Pointer, ArrayObject *, jsFrame) DECLARE_HEAP_OBJECT(GeneratorObject, Object) { - DECLARE_MARKOBJECTS(GeneratorObject); + DECLARE_MARKOBJECTS(GeneratorObject) }; } diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index 0112347053..7ea90a2ddd 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -32,7 +32,7 @@ static QString escape(const QString &input) { QString output; output.reserve(input.size() * 3); - const int length = input.length(); + const int length = input.size(); for (int i = 0; i < length; ++i) { ushort uc = input.at(i).unicode(); if (uc < 0x100) { @@ -63,9 +63,9 @@ static QString escape(const QString &input) static QString unescape(const QString &input) { QString result; - result.reserve(input.length()); + result.reserve(input.size()); int i = 0; - const int length = input.length(); + const int length = input.size(); while (i < length) { QChar c = input.at(i++); if ((c == u'%') && (i + 1 < length)) { @@ -113,7 +113,7 @@ static QString encode(const QString &input, const char *unescapedSet, bool *ok) { *ok = true; QString output; - const int length = input.length(); + const int length = input.size(); int i = 0; while (i < length) { const QChar c = input.at(i); @@ -187,8 +187,8 @@ static QString decode(const QString &input, DecodeMode decodeMode, bool *ok) { *ok = true; QString output; - output.reserve(input.length()); - const int length = input.length(); + output.reserve(input.size()); + const int length = input.size(); int i = 0; const QChar percent = QLatin1Char('%'); while (i < length) { @@ -381,7 +381,7 @@ ReturnedValue GlobalFunctions::method_parseInt(const FunctionObject *b, const Va CHECK_EXCEPTION(); const QChar *pos = trimmed.constData(); - const QChar *end = pos + trimmed.length(); + const QChar *end = pos + trimmed.size(); int sign = 1; // 3 if (pos != end) { diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp index 5b0c2f25ee..96aa54018b 100644 --- a/src/qml/jsruntime/qv4identifiertable.cpp +++ b/src/qml/jsruntime/qv4identifiertable.cpp @@ -25,7 +25,7 @@ IdentifierTable::~IdentifierTable() { free(entriesByHash); free(entriesById); - for (const auto &h : qAsConst(idHashes)) + for (const auto &h : std::as_const(idHashes)) h->identifierTable = nullptr; } @@ -100,7 +100,7 @@ void IdentifierTable::addEntry(Heap::StringOrSymbol *str) Heap::String *IdentifierTable::insertString(const QString &s) { uint subtype; - uint hash = String::createHashValue(s.constData(), s.length(), &subtype); + uint hash = String::createHashValue(s.constData(), s.size(), &subtype); if (subtype == Heap::String::StringType_ArrayIndex) { Heap::String *str = engine->newString(s); str->stringHash = hash; @@ -133,7 +133,7 @@ Heap::Symbol *IdentifierTable::insertSymbol(const QString &s) Q_ASSERT(s.at(0) == QLatin1Char('@')); uint subtype; - uint hash = String::createHashValue(s.constData(), s.length(), &subtype); + uint hash = String::createHashValue(s.constData(), s.size(), &subtype); uint idx = hash % alloc; while (Heap::StringOrSymbol *e = entriesByHash[idx]) { if (e->stringHash == hash && e->toQString() == s) @@ -252,7 +252,7 @@ void IdentifierTable::sweep() PropertyKey IdentifierTable::asPropertyKey(const QString &s) { uint subtype; - const uint hash = String::createHashValue(s.constData(), s.length(), &subtype); + const uint hash = String::createHashValue(s.constData(), s.size(), &subtype); if (subtype == Heap::String::StringType_ArrayIndex) return PropertyKey::fromArrayIndex(hash); return resolveStringEntry(s, hash, subtype)->identifier; diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index 693cc436ef..57ff8c44ec 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -610,10 +610,32 @@ struct Stringify QString makeMember(const QString &key, const Value &v); }; +class [[nodiscard]] CallDepthAndCycleChecker +{ + Q_DISABLE_COPY_MOVE(CallDepthAndCycleChecker); + +public: + CallDepthAndCycleChecker(Stringify *stringify, Object *o) + : m_callDepthRecorder(stringify->v4) + { + if (stringify->stackContains(o)) { + stringify->v4->throwTypeError( + QStringLiteral("Cannot convert circular structure to JSON")); + } + + stringify->v4->checkStackLimits(); + } + + bool foundProblem() const { return m_callDepthRecorder.ee->hasException; } + +private: + ExecutionEngineCallDepthRecorder<1> m_callDepthRecorder; +}; + static QString quote(const QString &str) { QString product; - const int length = str.length(); + const int length = str.size(); product.reserve(length + 2); product += u'"'; for (int i = 0; i < length; ++i) { @@ -740,10 +762,9 @@ QString Stringify::makeMember(const QString &key, const Value &v) QString Stringify::JO(Object *o) { - if (stackContains(o)) { - v4->throwTypeError(); + CallDepthAndCycleChecker check(this, o); + if (check.foundProblem()) return QString(); - } Scope scope(v4); @@ -800,10 +821,9 @@ QString Stringify::JO(Object *o) QString Stringify::JA(Object *a) { - if (stackContains(a)) { - v4->throwTypeError(); + CallDepthAndCycleChecker check(this, a); + if (check.foundProblem()) return QString(); - } Scope scope(a->engine()); @@ -865,7 +885,7 @@ ReturnedValue JsonObject::method_parse(const FunctionObject *b, const Value *, c jtext = argv[0].toQString(); DEBUG << "parsing source = " << jtext; - JsonParser parser(v4, jtext.constData(), jtext.length()); + JsonParser parser(v4, jtext.constData(), jtext.size()); QJsonParseError error; ReturnedValue result = parser.parse(&error); if (error.error != QJsonParseError::NoError) { @@ -885,9 +905,10 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value if (o) { stringify.replacerFunction = o->as<FunctionObject>(); if (o->isArrayObject()) { - uint arrayLen = o->getLength(); + int arrayLen = scope.engine->safeForAllocLength(o->getLength()); + CHECK_EXCEPTION(); stringify.propertyList = static_cast<QV4::String *>(scope.alloc(arrayLen)); - for (uint i = 0; i < arrayLen; ++i) { + for (int i = 0; i < arrayLen; ++i) { Value *v = stringify.propertyList + i; *v = o->get(i); if (v->as<NumberObject>() || v->as<StringObject>() || v->isNumber()) @@ -895,7 +916,7 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value if (!v->isString()) { v->setM(nullptr); } else { - for (uint j = 0; j <i; ++j) { + for (int j = 0; j <i; ++j) { if (stringify.propertyList[j].m() == v->m()) { v->setM(nullptr); break; diff --git a/src/qml/jsruntime/qv4mapiterator_p.h b/src/qml/jsruntime/qv4mapiterator_p.h index 2269473d4c..97a72db85c 100644 --- a/src/qml/jsruntime/qv4mapiterator_p.h +++ b/src/qml/jsruntime/qv4mapiterator_p.h @@ -30,7 +30,7 @@ namespace Heap { Member(class, NoMark, quint32, mapNextIndex) DECLARE_HEAP_OBJECT(MapIteratorObject, Object) { - DECLARE_MARKOBJECTS(MapIteratorObject); + DECLARE_MARKOBJECTS(MapIteratorObject) void init(Object *obj, QV4::ExecutionEngine *engine) { Object::init(); diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h index adcd1e8cf9..672b058fef 100644 --- a/src/qml/jsruntime/qv4memberdata_p.h +++ b/src/qml/jsruntime/qv4memberdata_p.h @@ -27,7 +27,7 @@ namespace Heap { Member(class, ValueArray, ValueArray, values) DECLARE_HEAP_OBJECT(MemberData, Base) { - DECLARE_MARKOBJECTS(MemberData); + DECLARE_MARKOBJECTS(MemberData) }; Q_STATIC_ASSERT(std::is_trivial_v<MemberData>); diff --git a/src/qml/jsruntime/qv4module.cpp b/src/qml/jsruntime/qv4module.cpp index 779eb77a36..1e1a059dfa 100644 --- a/src/qml/jsruntime/qv4module.cpp +++ b/src/qml/jsruntime/qv4module.cpp @@ -195,7 +195,7 @@ struct ModuleNamespaceIterator : ObjectOwnPropertyKeyIterator PropertyKey ModuleNamespaceIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) { const Module *module = static_cast<const Module *>(o); - if (exportIndex < exportedNames.count()) { + if (exportIndex < exportedNames.size()) { if (attrs) *attrs = Attr_Data; Scope scope(module->engine()); diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index aec04b167d..6a39b9e27f 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -1104,7 +1104,7 @@ void Heap::ArrayObject::init(const QStringList &list) // The result is a new Array object with length equal to the length // of the QStringList, and the elements being the QStringList's // elements converted to JS Strings. - int len = list.count(); + int len = list.size(); a->arrayReserve(len); ScopedValue v(scope); for (int ii = 0; ii < len; ++ii) diff --git a/src/qml/jsruntime/qv4persistent_p.h b/src/qml/jsruntime/qv4persistent_p.h index e7b653b6f6..7e208bd4fd 100644 --- a/src/qml/jsruntime/qv4persistent_p.h +++ b/src/qml/jsruntime/qv4persistent_p.h @@ -69,7 +69,7 @@ public: PersistentValue(PersistentValue &&other) noexcept : val(std::exchange(other.val, nullptr)) {} void swap(PersistentValue &other) noexcept { qt_ptr_swap(val, other.val); } - QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(PersistentValue); + QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(PersistentValue) ~PersistentValue() { PersistentValueStorage::free(val); } PersistentValue &operator=(const WeakValue &other); diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp index 19da26989f..db33cd27f9 100644 --- a/src/qml/jsruntime/qv4profiling.cpp +++ b/src/qml/jsruntime/qv4profiling.cpp @@ -60,7 +60,7 @@ void Profiler::reportData() FunctionLocationHash locations; properties.reserve(m_data.size()); - for (const FunctionCall &call : qAsConst(m_data)) { + for (const FunctionCall &call : std::as_const(m_data)) { properties.append(call.properties()); Function *function = call.function(); SentMarker &marker = m_sentLocations[reinterpret_cast<quintptr>(function)]; diff --git a/src/qml/jsruntime/qv4promiseobject_p.h b/src/qml/jsruntime/qv4promiseobject_p.h index c0599987e5..1c11ebbfd6 100644 --- a/src/qml/jsruntime/qv4promiseobject_p.h +++ b/src/qml/jsruntime/qv4promiseobject_p.h @@ -34,7 +34,7 @@ class ReactionHandler : public QObject public: ReactionHandler(QObject *parent = nullptr); - virtual ~ReactionHandler() override; + ~ReactionHandler() override; void addReaction(ExecutionEngine *e, const Value *reaction, const Value *value); void addResolveThenable(ExecutionEngine *e, const PromiseObject *promise, const Object *thenable, const FunctionObject *then); diff --git a/src/qml/jsruntime/qv4propertykey.cpp b/src/qml/jsruntime/qv4propertykey.cpp index 8a3e1adc65..e07df07543 100644 --- a/src/qml/jsruntime/qv4propertykey.cpp +++ b/src/qml/jsruntime/qv4propertykey.cpp @@ -66,7 +66,7 @@ QV4::Heap::String *QV4::PropertyKey::asFunctionName(ExecutionEngine *engine, Fun QString str = s->toQString(); if (s->internalClass->vtable->isString) n += s->toQString(); - else if (str.length() > 1) + else if (str.size() > 1) n += QChar::fromLatin1('[') + QStringView{str}.mid(1) + QChar::fromLatin1(']'); } return engine->newString(n); diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index a89e7f6f2f..bd4b5c2585 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -34,7 +34,7 @@ namespace Heap { Member(class, Pointer, Module *, module) DECLARE_HEAP_OBJECT(QQmlContextWrapper, Object) { - DECLARE_MARKOBJECTS(QQmlContextWrapper); + DECLARE_MARKOBJECTS(QQmlContextWrapper) void init(QQmlRefPointer<QQmlContextData> context, QObject *scopeObject); void destroy(); @@ -47,7 +47,7 @@ DECLARE_HEAP_OBJECT(QQmlContextWrapper, Object) { #define QmlContextMembers(class, Member) DECLARE_HEAP_OBJECT(QmlContext, ExecutionContext) { - DECLARE_MARKOBJECTS(QmlContext); + DECLARE_MARKOBJECTS(QmlContext) QQmlContextWrapper *qml() { return static_cast<QQmlContextWrapper *>(activation.get()); } void init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml); diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index d5e6c1b84a..b9b552576c 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -99,15 +99,10 @@ static ReturnedValue loadProperty(ExecutionEngine *v4, QObject *object, if (property.isQObject()) { QObject *rv = nullptr; property.readProperty(object, &rv); - ReturnedValue ret = QObjectWrapper::wrap(v4, rv); - if (propMetaType.flags().testFlag(QMetaType::IsConst)) { - ScopedValue v(scope, ret); - if (auto obj = v->as<Object>()) { - obj->setInternalClass(obj->internalClass()->cryopreserved()); - return obj->asReturnedValue(); - } - } - return ret; + if (propMetaType.flags().testFlag(QMetaType::IsConst)) + return QObjectWrapper::wrapConst(v4, rv); + else + return QObjectWrapper::wrap(v4, rv); } if (property.isQList() && propMetaType.flags().testFlag(QMetaType::IsQmlList)) @@ -669,6 +664,29 @@ ReturnedValue QObjectWrapper::wrap_slowPath(ExecutionEngine *engine, QObject *ob } } +ReturnedValue QObjectWrapper::wrapConst_slowPath(ExecutionEngine *engine, QObject *object) +{ + const QObject *constObject = object; + + QQmlData *ddata = QQmlData::get(object, true); + + Scope scope(engine); + ScopedObject constWrapper(scope); + if (engine->m_multiplyWrappedQObjects && ddata->hasConstWrapper) + constWrapper = engine->m_multiplyWrappedQObjects->value(constObject); + + if (!constWrapper) { + constWrapper = create(engine, object); + constWrapper->setInternalClass(constWrapper->internalClass()->cryopreserved()); + if (!engine->m_multiplyWrappedQObjects) + engine->m_multiplyWrappedQObjects = new MultiplyWrappedQObjectMap; + engine->m_multiplyWrappedQObjects->insert(constObject, constWrapper->d()); + ddata->hasConstWrapper = true; + } + + return constWrapper.asReturnedValue(); +} + void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack) { if (QQmlData::wasDeleted(object)) @@ -683,6 +701,8 @@ void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack) ddata->jsWrapper.markOnce(markStack); else if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object) engine->m_multiplyWrappedQObjects->mark(object, markStack); + if (ddata->hasConstWrapper) + engine->m_multiplyWrappedQObjects->mark(static_cast<const QObject *>(object), markStack); } void QObjectWrapper::setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value) @@ -710,14 +730,13 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int p bool QObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b) { Q_ASSERT(a->as<QObjectWrapper>()); - QObjectWrapper *qobjectWrapper = static_cast<QObjectWrapper *>(a); - Object *o = b->as<Object>(); - if (o) { - if (QQmlTypeWrapper *qmlTypeWrapper = o->as<QQmlTypeWrapper>()) - return qmlTypeWrapper->toVariant().value<QObject*>() == qobjectWrapper->object(); - } + const QObjectWrapper *aobjectWrapper = static_cast<QObjectWrapper *>(a); + if (const QQmlTypeWrapper *qmlTypeWrapper = b->as<QQmlTypeWrapper>()) + return qmlTypeWrapper->object() == aobjectWrapper->object(); - return false; + // We can have a const and a non-const wrapper for the same object. + const QObjectWrapper *bobjectWrapper = b->as<QObjectWrapper>(); + return bobjectWrapper && aobjectWrapper->object() == bobjectWrapper->object(); } ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object) @@ -1176,7 +1195,7 @@ ReturnedValue QObjectWrapper::method_disconnect(const FunctionObject *b, const V static void markChildQObjectsRecursively(QObject *parent, MarkStack *markStack) { const QObjectList &children = parent->children(); - for (int i = 0; i < children.count(); ++i) { + for (int i = 0; i < children.size(); ++i) { QObject *child = children.at(i); if (!child) continue; @@ -1350,8 +1369,8 @@ static ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, QMe } } } - QVarLengthArray<void *, 9> argData(args.count()); - for (int ii = 0; ii < args.count(); ++ii) + QVarLengthArray<void *, 9> argData(args.size()); + for (int ii = 0; ii < args.size(); ++ii) argData[ii] = args[ii].dataPtr(); object.metacall(callType, index, argData.data()); @@ -1903,14 +1922,16 @@ bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const // Convert via QVariant below. // TODO: Can't we just do qobjectPtr = qmlTypeWrapper->object() instead? break; + } else if (QObject *obj = qmlTypeWrapper->object()) { + // attached object case + qobjectPtr = obj; + return true; } // If this is a plain type wrapper without an instance, - // then indeed it's an undefined parameter. - // (although we might interpret that as const QMetaObject *). - // TODO: But what if it's an attached object? + // then we got a namespace, and that's a type error type = QMetaType::UnknownType; - return true; + return false; } qobjectPtr = nullptr; @@ -2025,7 +2046,7 @@ bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const } const QQmlMetaObject mo = QQmlMetaType::rawMetaObjectForType(metaType); - if (!mo.isNull()) { + if (!mo.isNull() && v.metaType().flags().testFlag(QMetaType::PointerToQObject)) { QObject *obj = QQmlMetaType::toQObject(v); if (obj != nullptr && !QQmlMetaObject::canConvert(obj, mo)) { @@ -2095,11 +2116,11 @@ ReturnedValue CallArgument::toValue(ExecutionEngine *engine) QList<QObject *> &list = *qlistPtr; Scope scope(engine); ScopedArrayObject array(scope, engine->newArrayObject()); - array->arrayReserve(list.count()); + array->arrayReserve(list.size()); ScopedValue v(scope); - for (int ii = 0; ii < list.count(); ++ii) + for (int ii = 0; ii < list.size(); ++ii) array->arrayPut(ii, (v = QObjectWrapper::wrap(engine, list.at(ii)))); - array->setArrayLengthUnchecked(list.count()); + array->setArrayLengthUnchecked(list.size()); return array.asReturnedValue(); } @@ -2429,39 +2450,20 @@ void QmlSignalHandler::initProto(ExecutionEngine *engine) engine->jsObjects[ExecutionEngine::SignalHandlerProto] = o->d(); } -void MultiplyWrappedQObjectMap::insert(QObject *key, Heap::Object *value) -{ - QHash<QObject*, WeakValue>::operator[](key).set(value->internalClass->engine, value); - connect(key, SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*))); -} - - -MultiplyWrappedQObjectMap::Iterator MultiplyWrappedQObjectMap::erase(MultiplyWrappedQObjectMap::Iterator it) +MultiplyWrappedQObjectMap::Iterator MultiplyWrappedQObjectMap::erase( + MultiplyWrappedQObjectMap::Iterator it) { - disconnect(it.key(), SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*))); - return QHash<QObject*, WeakValue>::erase(it); -} - -void MultiplyWrappedQObjectMap::remove(QObject *key) -{ - Iterator it = find(key); - if (it == end()) - return; - erase(it); -} - -void MultiplyWrappedQObjectMap::mark(QObject *key, MarkStack *markStack) -{ - Iterator it = find(key); - if (it == end()) - return; - it->markOnce(markStack); + const QObjectBiPointer key = it.key(); + const QObject *obj = key.isT1() ? key.asT1() : key.asT2(); + disconnect(obj, &QObject::destroyed, this, &MultiplyWrappedQObjectMap::removeDestroyedObject); + return QHash<QObjectBiPointer, WeakValue>::erase(it); } void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object) { - QHash<QObject*, WeakValue>::remove(object); + QHash<QObjectBiPointer, WeakValue>::remove(object); + QHash<QObjectBiPointer, WeakValue>::remove(static_cast<const QObject *>(object)); } } // namespace QV4 diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 4477c3c836..5a0d837746 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -65,7 +65,7 @@ private: Member(class, NoMark, int, index) DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) { - DECLARE_MARKOBJECTS(QObjectMethod); + DECLARE_MARKOBJECTS(QObjectMethod) QQmlPropertyData *methods; int methodCount; @@ -140,6 +140,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object QObject *object, String *name, RevisionMode revisionMode, const Value &value); static ReturnedValue wrap(ExecutionEngine *engine, QObject *object); + static ReturnedValue wrapConst(ExecutionEngine *engine, QObject *object); static void markWrapper(QObject *object, MarkStack *markStack); using Object::get; @@ -181,6 +182,7 @@ protected: private: Q_NEVER_INLINE static ReturnedValue wrap_slowPath(ExecutionEngine *engine, QObject *object); + Q_NEVER_INLINE static ReturnedValue wrapConst_slowPath(ExecutionEngine *engine, QObject *object); }; inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object) @@ -197,6 +199,15 @@ inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *obje return wrap_slowPath(engine, object); } +// Unfortunately we still need a non-const QObject* here because QQmlData needs to register itself in QObjectPrivate. +inline ReturnedValue QObjectWrapper::wrapConst(ExecutionEngine *engine, QObject *object) +{ + if (Q_UNLIKELY(QQmlData::wasDeleted(object))) + return QV4::Encode::null(); + + return wrapConst_slowPath(engine, object); +} + template <typename ReversalFunctor> inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revertLookup) { @@ -292,23 +303,32 @@ struct Q_QML_EXPORT QmlSignalHandler : public QV4::Object static void initProto(ExecutionEngine *v4); }; +using QObjectBiPointer = QBiPointer<QObject, const QObject>; + class MultiplyWrappedQObjectMap : public QObject, - private QHash<QObject*, QV4::WeakValue> + private QHash<QObjectBiPointer, QV4::WeakValue> { Q_OBJECT public: - typedef QHash<QObject*, QV4::WeakValue>::ConstIterator ConstIterator; - typedef QHash<QObject*, QV4::WeakValue>::Iterator Iterator; + typedef QHash<QObjectBiPointer, QV4::WeakValue>::ConstIterator ConstIterator; + typedef QHash<QObjectBiPointer, QV4::WeakValue>::Iterator Iterator; + + using value_type = QHash<QObjectBiPointer, QV4::WeakValue>::value_type; - using value_type = QHash<QObject*, QV4::WeakValue>::value_type; + ConstIterator begin() const { return QHash<QObjectBiPointer, QV4::WeakValue>::constBegin(); } + Iterator begin() { return QHash<QObjectBiPointer, QV4::WeakValue>::begin(); } + ConstIterator end() const { return QHash<QObjectBiPointer, QV4::WeakValue>::constEnd(); } + Iterator end() { return QHash<QObjectBiPointer, QV4::WeakValue>::end(); } - ConstIterator begin() const { return QHash<QObject*, QV4::WeakValue>::constBegin(); } - Iterator begin() { return QHash<QObject*, QV4::WeakValue>::begin(); } - ConstIterator end() const { return QHash<QObject*, QV4::WeakValue>::constEnd(); } - Iterator end() { return QHash<QObject*, QV4::WeakValue>::end(); } + template<typename Pointer> + void insert(Pointer key, Heap::Object *value) + { + QHash<QObjectBiPointer, WeakValue>::operator[](key).set(value->internalClass->engine, value); + connect(key, SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*))); + } - void insert(QObject *key, Heap::Object *value); - ReturnedValue value(QObject *key) const + template<typename Pointer> + ReturnedValue value(Pointer key) const { ConstIterator it = find(key); return it == end() @@ -317,8 +337,24 @@ public: } Iterator erase(Iterator it); - void remove(QObject *key); - void mark(QObject *key, MarkStack *markStack); + + template<typename Pointer> + void remove(Pointer key) + { + Iterator it = find(key); + if (it == end()) + return; + erase(it); + } + + template<typename Pointer> + void mark(Pointer key, MarkStack *markStack) + { + Iterator it = find(key); + if (it == end()) + return; + it->markOnce(markStack); + } private Q_SLOTS: void removeDestroyedObject(QObject*); diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp index abcc60726a..179663a0e1 100644 --- a/src/qml/jsruntime/qv4reflect.cpp +++ b/src/qml/jsruntime/qv4reflect.cpp @@ -40,7 +40,10 @@ struct CallArgs { static CallArgs createListFromArrayLike(Scope &scope, const Object *o) { - int len = o->getLength(); + int len = scope.engine->safeForAllocLength(o->getLength()); + if (scope.engine->hasException) + return {nullptr, 0}; + Value *arguments = scope.alloc(len); for (int i = 0; i < len; ++i) { diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp index 0c039967ba..be7ff77603 100644 --- a/src/qml/jsruntime/qv4regexp.cpp +++ b/src/qml/jsruntime/qv4regexp.cpp @@ -49,7 +49,7 @@ uint RegExp::match(const QString &string, int start, uint *matchOffsets) uint ret = JSC::Yarr::offsetNoMatch; #if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS) char buffer[8192]; - ret = uint(priv->jitCode->execute(s.characters16(), start, s.length(), + ret = uint(priv->jitCode->execute(s.characters16(), start, s.size(), (int*)matchOffsets, buffer, 8192).start); #else ret = uint(priv->jitCode->execute(s.characters16(), start, s.length(), @@ -74,18 +74,18 @@ uint RegExp::match(const QString &string, int start, uint *matchOffsets) } #endif // ENABLE(YARR_JIT) - return JSC::Yarr::interpret(byteCode(), s.characters16(), string.length(), start, matchOffsets); + return JSC::Yarr::interpret(byteCode(), s.characters16(), string.size(), start, matchOffsets); } QString RegExp::getSubstitution(const QString &matched, const QString &str, int position, const Value *captures, int nCaptures, const QString &replacement) { QString result; - int matchedLength = matched.length(); - Q_ASSERT(position >= 0 && position <= str.length()); + int matchedLength = matched.size(); + Q_ASSERT(position >= 0 && position <= str.size()); int tailPos = position + matchedLength; int seenDollar = -1; - for (int i = 0; i < replacement.length(); ++i) { + for (int i = 0; i < replacement.size(); ++i) { QChar ch = replacement.at(i); if (seenDollar >= 0) { if (ch.unicode() == '$') { @@ -98,7 +98,7 @@ QString RegExp::getSubstitution(const QString &matched, const QString &str, int result += str.mid(tailPos); } else if (ch.unicode() >= '0' && ch.unicode() <= '9') { int n = ch.unicode() - '0'; - if (i + 1 < replacement.length()) { + if (i + 1 < replacement.size()) { ch = replacement.at(i + 1); if (ch.unicode() >= '0' && ch.unicode() <= '9') { n = n*10 + (ch.unicode() - '0'); diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 365593e207..0fab40a281 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -48,7 +48,7 @@ void Heap::RegExpObject::init(QV4::RegExp *value) static QString minimalPattern(const QString &pattern) { QString ecmaPattern; - int len = pattern.length(); + int len = pattern.size(); ecmaPattern.reserve(len); int i = 0; const QChar *wc = pattern.unicode(); @@ -146,7 +146,7 @@ ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *s Scope scope(engine); int offset = (global() || sticky()) ? lastIndex() : 0; - if (offset < 0 || offset > s.length()) { + if (offset < 0 || offset > s.size()) { setLastIndex(0); RETURN_RESULT(Encode::null()); } @@ -170,7 +170,7 @@ ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *s int len = value()->captureCount(); array->arrayReserve(len); ScopedValue v(scope); - int strlen = s.length(); + int strlen = s.size(); for (int i = 0; i < len; ++i) { int start = matchOffsets[i * 2]; int end = matchOffsets[i * 2 + 1]; @@ -232,7 +232,7 @@ uint parseFlags(Scope &scope, const QV4::Value *f) if (scope.hasException()) return flags; QString str = s->toQString(); - for (int i = 0; i < str.length(); ++i) { + for (int i = 0; i < str.size(); ++i) { if (str.at(i) == QLatin1Char('g') && !(flags & CompiledData::RegExp::RegExp_Global)) { flags |= CompiledData::RegExp::RegExp_Global; } else if (str.at(i) == QLatin1Char('i') && !(flags & CompiledData::RegExp::RegExp_IgnoreCase)) { @@ -382,7 +382,7 @@ ReturnedValue RegExpPrototype::execFirstMatch(const FunctionObject *b, const Val QString s = str->toQString(); int offset = r->lastIndex(); - if (offset < 0 || offset > s.length()) { + if (offset < 0 || offset > s.size()) { r->setLastIndex(0); RETURN_RESULT(Encode::null()); } @@ -518,7 +518,7 @@ ReturnedValue RegExpPrototype::method_get_ignoreCase(const FunctionObject *f, co static int advanceStringIndex(int index, const QString &str, bool unicode) { if (unicode) { - if (index < str.length() - 1 && + if (index < str.size() - 1 && str.at(index).isHighSurrogate() && str.at(index + 1).isLowSurrogate()) ++index; @@ -607,7 +607,7 @@ ReturnedValue RegExpPrototype::method_replace(const FunctionObject *f, const Val if (scope.hasException()) return Encode::undefined(); - int lengthS = s->toQString().length(); + int lengthS = s->toQString().size(); ScopedString replaceValue(scope); ScopedFunctionObject replaceFunction(scope, (argc > 1 ? argv[1] : Value::undefinedValue())); @@ -659,7 +659,7 @@ ReturnedValue RegExpPrototype::method_replace(const FunctionObject *f, const Val if (scope.hasException()) return Encode::undefined(); QString m = matchString->toQString(); - int matchLength = m.length(); + int matchLength = m.size(); v = resultObject->get(scope.engine->id_index()); int position = v->toInt32(); position = qMax(qMin(position, lengthS), 0); @@ -786,7 +786,7 @@ ReturnedValue RegExpPrototype::method_split(const FunctionObject *f, const Value return A->asReturnedValue(); QString S = s->toQString(); - int size = S.length(); + int size = S.size(); if (size == 0) { ScopedValue z(scope, exec(scope.engine, splitter, s)); if (z->isNull()) diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index 2f278f70a7..3171367351 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -56,7 +56,7 @@ DECLARE_HEAP_OBJECT(RegExpObject, Object) { Member(class, NoMark, int, lastMatchEnd) DECLARE_HEAP_OBJECT(RegExpCtor, FunctionObject) { - DECLARE_MARKOBJECTS(RegExpCtor); + DECLARE_MARKOBJECTS(RegExpCtor) void init(QV4::ExecutionContext *scope); void clearLastMatch(); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index bd81c56bbd..f07f7e38a1 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -144,7 +144,7 @@ struct RuntimeCounters::Data { } std::sort(lines.begin(), lines.end(), Line::less); outs << lines.size() << " counters:" << endl; - for (const Line &line : qAsConst(lines)) + for (const Line &line : std::as_const(lines)) outs << qSetFieldWidth(10) << line.count << qSetFieldWidth(0) << " | " << line.func << " | " << pretty(line.tag1) @@ -217,7 +217,7 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) *result = qdtoa(num, &decpt, &sign); if (decpt <= ecma_shortest_low || decpt > ecma_shortest_high) { - if (result->length() > 1) + if (result->size() > 1) result->insert(1, dot); result->append(QLatin1Char('e')); if (decpt > 0) @@ -225,10 +225,10 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) result->append(QString::number(decpt - 1)); } else if (decpt <= 0) { result->prepend(QLatin1String("0.") + QString(-decpt, zero)); - } else if (decpt < result->length()) { + } else if (decpt < result->size()) { result->insert(decpt, dot); } else { - result->append(QString(decpt - result->length(), zero)); + result->append(QString(decpt - result->size(), zero)); } if (sign && num) @@ -392,7 +392,7 @@ double RuntimeHelpers::stringToNumber(const QString &string) // libdoubleconversion sources. The same maximum value would be represented by roughly 3.5 times // as many binary digits. const int excessiveLength = 16 * 1024; - if (string.length() > excessiveLength) + if (string.size() > excessiveLength) return qQNaN(); const QStringView s = QStringView(string).trimmed(); @@ -642,7 +642,7 @@ static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engin ScopedObject o(scope, object); if (!o) { if (const String *str = object.as<String>()) { - if (idx >= (uint)str->toQString().length()) { + if (idx >= (uint)str->toQString().size()) { return Encode::undefined(); } const QString s = str->toQString().mid(idx, 1); @@ -1561,6 +1561,11 @@ static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc) if (done->booleanValue()) break; ++argCount; + constexpr auto safetyMargin = 100; // leave some space on the stack for actual work with the elements + if (qint64(scope.engine->jsStackLimit - scope.engine->jsStackTop) < safetyMargin) { + scope.engine->throwRangeError(QLatin1String("Too many elements in array to use it with the spread operator")); + return { nullptr, 0 }; + } v = scope.alloc<Scope::Uninitialized>(); } } diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index 09e8b60c91..eee7fd8414 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -88,6 +88,10 @@ struct Scope { /* Be careful when using Uninitialized, the stack has to be fully initialized before calling into the memory manager again */ Uninitialized }; + + template <AllocMode mode = Undefined> + Value *alloc(qint64 nValues) const = delete; // use safeForAllocLength + template <AllocMode mode = Undefined> QML_NEARLY_ALWAYS_INLINE Value *alloc(int nValues) const { @@ -408,7 +412,7 @@ struct ScopedProperty { ScopedProperty(Scope &scope) { - property = reinterpret_cast<Property*>(scope.alloc(sizeof(Property) / sizeof(Value))); + property = reinterpret_cast<Property*>(scope.alloc(int(sizeof(Property) / sizeof(Value)))); } Property *operator->() { return property; } diff --git a/src/qml/jsruntime/qv4setiterator_p.h b/src/qml/jsruntime/qv4setiterator_p.h index e7ab85accb..37f912e01a 100644 --- a/src/qml/jsruntime/qv4setiterator_p.h +++ b/src/qml/jsruntime/qv4setiterator_p.h @@ -30,7 +30,7 @@ namespace Heap { Member(class, NoMark, quint32, setNextIndex) DECLARE_HEAP_OBJECT(SetIteratorObject, Object) { - DECLARE_MARKOBJECTS(SetIteratorObject); + DECLARE_MARKOBJECTS(SetIteratorObject) void init(Object *obj, QV4::ExecutionEngine *engine) { Object::init(); diff --git a/src/qml/jsruntime/qv4stringiterator.cpp b/src/qml/jsruntime/qv4stringiterator.cpp index 99f10c55b4..9cb2711efb 100644 --- a/src/qml/jsruntime/qv4stringiterator.cpp +++ b/src/qml/jsruntime/qv4stringiterator.cpp @@ -35,7 +35,7 @@ ReturnedValue StringIteratorPrototype::method_next(const FunctionObject *b, cons quint32 index = thisObject->d()->nextIndex; QString str = s->toQString(); - quint32 len = str.length(); + quint32 len = str.size(); if (index >= len) { thisObject->d()->iteratedString.set(scope.engine, nullptr); diff --git a/src/qml/jsruntime/qv4stringiterator_p.h b/src/qml/jsruntime/qv4stringiterator_p.h index a445381ba6..742b8a895d 100644 --- a/src/qml/jsruntime/qv4stringiterator_p.h +++ b/src/qml/jsruntime/qv4stringiterator_p.h @@ -30,7 +30,7 @@ namespace Heap { Member(class, NoMark, quint32, nextIndex) DECLARE_HEAP_OBJECT(StringIteratorObject, Object) { - DECLARE_MARKOBJECTS(StringIteratorObject); + DECLARE_MARKOBJECTS(StringIteratorObject) void init(String *str, QV4::ExecutionEngine *engine) { Object::init(); diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 5e1d764aed..bec4132b5f 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -51,7 +51,7 @@ void Heap::StringObject::init(const QV4::String *str) Heap::String *Heap::StringObject::getIndex(uint index) const { QString str = string->toQString(); - if (index >= (uint)str.length()) + if (index >= (uint)str.size()) return nullptr; return internalClass->engine->newString(str.mid(index, 1)); } @@ -67,7 +67,7 @@ bool StringObject::virtualDeleteProperty(Managed *m, PropertyKey id) if (id.isArrayIndex()) { StringObject *o = static_cast<StringObject *>(m); uint index = id.asArrayIndex(); - if (index < static_cast<uint>(o->d()->string->toQString().length())) + if (index < static_cast<uint>(o->d()->string->toQString().size())) return false; } return Object::virtualDeleteProperty(m, id); @@ -83,7 +83,7 @@ struct StringObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator PropertyKey StringObjectOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd, PropertyAttributes *attrs) { const StringObject *s = static_cast<const StringObject *>(o); - uint slen = s->d()->string->toQString().length(); + uint slen = s->d()->string->toQString().size(); if (arrayIndex < slen) { uint index = arrayIndex; ++arrayIndex; @@ -119,7 +119,7 @@ PropertyAttributes StringObject::virtualGetOwnProperty(const Managed *m, Propert if (id.isArrayIndex()) { const uint index = id.asArrayIndex(); const auto s = static_cast<const StringObject *>(m); - if (index < uint(s->d()->string->toQString().length())) { + if (index < uint(s->d()->string->toQString().size())) { if (p) p->value = s->getIndex(index); return Attr_NotConfigurable|Attr_NotWritable; @@ -338,7 +338,7 @@ ReturnedValue StringPrototype::method_charAt(const FunctionObject *b, const Valu pos = (int) argv[0].toInteger(); QString result; - if (pos >= 0 && pos < str.length()) + if (pos >= 0 && pos < str.size()) result += str.at(pos); return Encode(v4->newString(result)); @@ -356,7 +356,7 @@ ReturnedValue StringPrototype::method_charCodeAt(const FunctionObject *b, const pos = (int) argv[0].toInteger(); - if (pos >= 0 && pos < str.length()) + if (pos >= 0 && pos < str.size()) RETURN_RESULT(Encode(str.at(pos).unicode())); return Encode(qt_qnan()); @@ -419,11 +419,11 @@ ReturnedValue StringPrototype::method_endsWith(const FunctionObject *b, const Va if (v4->hasException) return Encode::undefined(); - int pos = value.length(); + int pos = value.size(); if (argc > 1) pos = (int) argv[1].toInteger(); - if (pos == value.length()) + if (pos == value.size()) RETURN_RESULT(Encode(value.endsWith(searchString))); QStringView stringToSearch = QStringView{value}.left(pos); @@ -447,7 +447,7 @@ ReturnedValue StringPrototype::method_indexOf(const FunctionObject *b, const Val int index = -1; if (! value.isEmpty()) - index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); + index = value.indexOf(searchString, qMin(qMax(pos, 0), value.size())); return Encode(index); } @@ -470,7 +470,7 @@ ReturnedValue StringPrototype::method_includes(const FunctionObject *b, const Va const Value &posArg = argv[1]; pos = (int) posArg.toInteger(); if (!posArg.isInteger() && posArg.isNumber() && qIsInf(posArg.toNumber())) - pos = value.length(); + pos = value.size(); } if (pos == 0) @@ -497,8 +497,8 @@ ReturnedValue StringPrototype::method_lastIndexOf(const FunctionObject *b, const else position = std::trunc(position); - int pos = std::trunc(qMin(qMax(position, 0.0), double(value.length()))); - if (!searchString.isEmpty() && pos == value.length()) + int pos = std::trunc(qMin(qMax(position, 0.0), double(value.size()))); + if (!searchString.isEmpty() && pos == value.size()) --pos; if (searchString.isNull() && pos == 0) RETURN_RESULT(Encode(-1)); @@ -607,12 +607,12 @@ ReturnedValue StringPrototype::method_padEnd(const FunctionObject *f, const Valu return s->asReturnedValue(); QString padded = s->toQString(); - int oldLength = padded.length(); + int oldLength = padded.size(); int toFill = maxLen - oldLength; padded.resize(maxLen); QChar *ch = padded.data() + oldLength; while (toFill) { - int copy = qMin(fillString.length(), toFill); + int copy = qMin(fillString.size(), toFill); memcpy(ch, fillString.constData(), copy*sizeof(QChar)); toFill -= copy; ch += copy; @@ -646,13 +646,13 @@ ReturnedValue StringPrototype::method_padStart(const FunctionObject *f, const Va return s->asReturnedValue(); QString original = s->toQString(); - int oldLength = original.length(); + int oldLength = original.size(); int toFill = maxLen - oldLength; QString padded; padded.resize(maxLen); QChar *ch = padded.data(); while (toFill) { - int copy = qMin(fillString.length(), toFill); + int copy = qMin(fillString.size(), toFill); memcpy(ch, fillString.constData(), copy*sizeof(QChar)); toFill -= copy; ch += copy; @@ -682,9 +682,9 @@ ReturnedValue StringPrototype::method_repeat(const FunctionObject *b, const Valu static void appendReplacementString(QString *result, const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) { - result->reserve(result->length() + replaceValue.length()); - for (int i = 0; i < replaceValue.length(); ++i) { - if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.length() - 1) { + result->reserve(result->size() + replaceValue.size()); + for (int i = 0; i < replaceValue.size(); ++i) { + if (replaceValue.at(i) == QLatin1Char('$') && i < replaceValue.size() - 1) { ushort ch = replaceValue.at(i + 1).unicode(); uint substStart = JSC::Yarr::offsetNoMatch; uint substEnd = JSC::Yarr::offsetNoMatch; @@ -703,12 +703,12 @@ static void appendReplacementString(QString *result, const QString &input, const skip = 1; } else if (ch == '\'') { substStart = matchOffsets[1]; - substEnd = input.length(); + substEnd = input.size(); skip = 1; } else if (ch >= '0' && ch <= '9') { uint capture = ch - '0'; skip = 1; - if (i < replaceValue.length() - 2) { + if (i < replaceValue.size() - 2) { ch = replaceValue.at(i + 2).unicode(); if (ch >= '0' && ch <= '9') { uint c = capture*10 + ch - '0'; @@ -793,7 +793,7 @@ ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Val if (idx != -1) { numStringMatches = 1; matchOffsets[0] = idx; - matchOffsets[1] = idx + searchString.length(); + matchOffsets[1] = idx + searchString.size(); } } @@ -802,7 +802,7 @@ ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Val ScopedValue replaceValue(scope, argc > 1 ? argv[1] : Value::undefinedValue()); ScopedFunctionObject searchCallback(scope, replaceValue); if (!!searchCallback) { - result.reserve(string.length() + 10*numStringMatches); + result.reserve(string.size() + 10*numStringMatches); ScopedValue entry(scope); Value *arguments = scope.alloc(numCaptures + 2); int lastEnd = 0; @@ -832,7 +832,7 @@ ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Val result += QStringView{string}.mid(lastEnd); } else { QString newString = replaceValue->toQString(); - result.reserve(string.length() + numStringMatches*newString.size()); + result.reserve(string.size() + numStringMatches*newString.size()); int lastEnd = 0; for (int i = 0; i < numStringMatches; ++i) { @@ -975,7 +975,7 @@ ReturnedValue StringPrototype::method_split(const FunctionObject *b, const Value } else { QString separator = separatorValue->toQString(); if (separator.isEmpty()) { - for (uint i = 0; i < qMin(limit, uint(text.length())); ++i) + for (uint i = 0; i < qMin(limit, uint(text.size())); ++i) array->push_back((s = scope.engine->newString(text.mid(i, 1)))); return array.asReturnedValue(); } @@ -1033,7 +1033,7 @@ ReturnedValue StringPrototype::method_substr(const FunctionObject *b, const Valu if (argc > 1) length = argv[1].toInteger(); - double count = value.length(); + double count = value.size(); if (start < 0) start = qMax(count + start, 0.0); @@ -1051,7 +1051,7 @@ ReturnedValue StringPrototype::method_substring(const FunctionObject *b, const V if (v4->hasException) return QV4::Encode::undefined(); - int length = value.length(); + int length = value.size(); double start = 0; double end = length; @@ -1124,11 +1124,11 @@ ReturnedValue StringPrototype::method_trim(const FunctionObject *b, const Value const QChar *chars = s.constData(); int start, end; - for (start = 0; start < s.length(); ++start) { + for (start = 0; start < s.size(); ++start) { if (!chars[start].isSpace() && chars[start].unicode() != 0xfeff) break; } - for (end = s.length() - 1; end >= start; --end) { + for (end = s.size() - 1; end >= start; --end) { if (!chars[end].isSpace() && chars[end].unicode() != 0xfeff) break; } diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index e0c0e37815..451a989ef4 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -28,7 +28,7 @@ namespace Heap { Member(class, Pointer, String *, string) DECLARE_HEAP_OBJECT(StringObject, Object) { - DECLARE_MARKOBJECTS(StringObject); + DECLARE_MARKOBJECTS(StringObject) enum { LengthPropertyIndex = 0 diff --git a/src/qml/jsruntime/qv4symbol_p.h b/src/qml/jsruntime/qv4symbol_p.h index b85ddb3b36..e56510bd69 100644 --- a/src/qml/jsruntime/qv4symbol_p.h +++ b/src/qml/jsruntime/qv4symbol_p.h @@ -36,7 +36,7 @@ struct Symbol : StringOrSymbol { Member(class, Pointer, Symbol *, symbol) DECLARE_HEAP_OBJECT(SymbolObject, Object) { - DECLARE_MARKOBJECTS(SymbolObject); + DECLARE_MARKOBJECTS(SymbolObject) void init(const QV4::Symbol *s); }; diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index d7e9d7466c..0284dceb7b 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -80,7 +80,7 @@ namespace Heap { Member(class, NoMark, uint, arrayType) DECLARE_HEAP_OBJECT(TypedArray, Object) { - DECLARE_MARKOBJECTS(TypedArray); + DECLARE_MARKOBJECTS(TypedArray) using Type = TypedArrayType; void init(Type t); diff --git a/src/qml/jsruntime/qv4urlobject.cpp b/src/qml/jsruntime/qv4urlobject.cpp index bc59b7a839..7f4a078d13 100644 --- a/src/qml/jsruntime/qv4urlobject.cpp +++ b/src/qml/jsruntime/qv4urlobject.cpp @@ -131,7 +131,7 @@ void UrlObject::setUrl(const QUrl &url) d()->port.set(engine(), engine()->newString(url.port() == -1 ? QLatin1String("") : QString::number(url.port()))); - d()->protocol.set(engine(), engine()->newString(url.scheme())); + d()->protocol.set(engine(), engine()->newString(url.scheme() + QLatin1Char(':'))); d()->search.set(engine(), engine()->newString(url.query())); d()->username.set(engine(), engine()->newString(url.userName())); @@ -186,15 +186,23 @@ bool UrlObject::setPort(QString port) return true; } -bool UrlObject::setProtocol(QString protocol) +bool UrlObject::setProtocol(QString protocolOrScheme) { QUrl url = toQUrl(); - url.setScheme(protocol); + // If there is one or several ':' in the protocolOrScheme, + // everything from the first colon is removed. + + qsizetype firstColonPos = protocolOrScheme.indexOf(QLatin1Char(':')); + + if (firstColonPos != -1) + protocolOrScheme.truncate(firstColonPos); + + url.setScheme(protocolOrScheme); if (!url.isValid()) return false; - d()->protocol.set(engine(), engine()->newString(url.scheme())); + d()->protocol.set(engine(), engine()->newString(url.scheme() + QLatin1Char(':'))); d()->href.set(engine(), engine()->newString(url.toString())); updateOrigin(); diff --git a/src/qml/jsruntime/qv4urlobject_p.h b/src/qml/jsruntime/qv4urlobject_p.h index 3b210547b3..e4a3ba073b 100644 --- a/src/qml/jsruntime/qv4urlobject_p.h +++ b/src/qml/jsruntime/qv4urlobject_p.h @@ -41,7 +41,7 @@ namespace Heap { DECLARE_HEAP_OBJECT(UrlObject, Object) { - DECLARE_MARKOBJECTS(UrlObject); + DECLARE_MARKOBJECTS(UrlObject) void init() { Object::init(); } }; @@ -59,7 +59,7 @@ struct UrlCtor : FunctionObject DECLARE_HEAP_OBJECT(UrlSearchParamsObject, Object) { - DECLARE_MARKOBJECTS(UrlSearchParamsObject); + DECLARE_MARKOBJECTS(UrlSearchParamsObject) void init() { Object::init(); } }; |