diff options
author | Morten Johan Sørvig <morten.sorvig@qt.io> | 2018-05-11 10:24:18 +0200 |
---|---|---|
committer | Morten Johan Sørvig <morten.sorvig@qt.io> | 2018-05-11 11:13:04 +0200 |
commit | 809d305f938177cfb8488dc7fbfc28bc8eef9d20 (patch) | |
tree | d43688a9a3ef61af95c91bae3de8b00e6466c817 /src/qml/qml | |
parent | b742bf9415b42c6e34fab91d2f407eb23dc8e0da (diff) | |
parent | 1e82f11629e5572783e5bfc36f24ad10c235ca53 (diff) |
Merge remote-tracking branch 'origin/5.11.0' into wip/webassembly
Change-Id: Ice58db1687c0cfbd5a19e84ca9fa81b8d3fa7959
Diffstat (limited to 'src/qml/qml')
31 files changed, 521 insertions, 315 deletions
diff --git a/src/qml/qml/ftw/qqmlthread.cpp b/src/qml/qml/ftw/qqmlthread.cpp index a36a599f24..9c44e18b87 100644 --- a/src/qml/qml/ftw/qqmlthread.cpp +++ b/src/qml/qml/ftw/qqmlthread.cpp @@ -57,6 +57,7 @@ public: void run() override; + inline QMutex &mutex() { return _mutex; } inline void lock() { _mutex.lock(); } inline void unlock() { _mutex.unlock(); } inline void wait() { _wait.wait(&_mutex); } @@ -269,6 +270,11 @@ bool QQmlThread::isShutdown() const return d->m_shutdown; } +QMutex &QQmlThread::mutex() +{ + return d->mutex(); +} + void QQmlThread::lock() { d->lock(); diff --git a/src/qml/qml/ftw/qqmlthread_p.h b/src/qml/qml/ftw/qqmlthread_p.h index 0ed12a2972..b5c580fe8b 100644 --- a/src/qml/qml/ftw/qqmlthread_p.h +++ b/src/qml/qml/ftw/qqmlthread_p.h @@ -59,6 +59,7 @@ QT_BEGIN_NAMESPACE class QThread; +class QMutex; class QQmlThreadPrivate; class QQmlThread @@ -71,6 +72,7 @@ public: void shutdown(); bool isShutdown() const; + QMutex &mutex(); void lock(); void unlock(); void wakeOne(); diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index ca3bff43a4..30a18440a8 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -51,6 +51,7 @@ #include <private/qqmlvaluetypewrapper_p.h> #include <private/qv4qobjectwrapper_p.h> #include <private/qv4variantobject_p.h> +#include <private/qv4jscall_p.h> #include <QVariant> #include <QtCore/qdebug.h> @@ -97,6 +98,21 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScr return b; } +QQmlSourceLocation QQmlBinding::sourceLocation() const +{ + if (m_sourceLocation) + return *m_sourceLocation; + return QQmlJavaScriptExpression::sourceLocation(); +} + +void QQmlBinding::setSourceLocation(const QQmlSourceLocation &location) +{ + if (m_sourceLocation) + delete m_sourceLocation; + m_sourceLocation = new QQmlSourceLocation(location); +} + + QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, QQmlContextData *ctxt, const QString &url, quint16 lineNumber) { @@ -128,6 +144,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, QV4::Function QQmlBinding::~QQmlBinding() { + delete m_sourceLocation; } void QQmlBinding::setNotifyOnValueChanged(bool v) @@ -171,6 +188,28 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags) setUpdatingFlag(false); } +QV4::ReturnedValue QQmlBinding::evaluate(bool *isUndefined) +{ + QV4::ExecutionEngine *v4 = context()->engine->handle(); + int argc = 0; + const QV4::Value *argv = nullptr; + const QV4::Value *thisObject = nullptr; + QV4::BoundFunction *b = nullptr; + if ((b = static_cast<QV4::BoundFunction *>(m_boundFunction.valueRef()))) { + QV4::Heap::MemberData *args = b->boundArgs(); + if (args) { + argc = args->values.size; + argv = args->values.data(); + } + thisObject = &b->d()->boundThis; + } + QV4::Scope scope(v4); + QV4::JSCallData jsCall(scope, argc, argv, thisObject); + + return QQmlJavaScriptExpression::evaluate(jsCall.callData(), isUndefined); +} + + // QQmlBindingBinding is for target properties which are of type "binding" (instead of, say, int or // double). The reason for being is that GenericBinding::fastWrite needs a compile-time constant // expression for the switch for the compiler to generate the optimal code, but @@ -203,7 +242,7 @@ protected: bool isUndefined = false; - QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined)); + QV4::ScopedValue result(scope, evaluate(&isUndefined)); bool error = false; if (!watcher.wasDeleted() && isAddedToObject() && !hasError()) @@ -302,9 +341,14 @@ public: { setCompilationUnit(compilationUnit); m_binding = binding; - setSourceLocation(QQmlSourceLocation(compilationUnit->fileName(), binding->valueLocation.line, binding->valueLocation.column)); } + QQmlSourceLocation sourceLocation() const override final + { + return QQmlSourceLocation(m_compilationUnit->fileName(), m_binding->valueLocation.line, m_binding->valueLocation.column); + } + + void doUpdate(const DeleteWatcher &watcher, QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) override final { @@ -490,6 +534,7 @@ void QQmlBinding::refresh() void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags) { + const bool wasEnabled = enabledFlag(); setEnabledFlag(e); setNotifyOnValueChanged(e); @@ -499,7 +544,7 @@ void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags) m_nextBinding.clearFlag2(); } - if (e) + if (e && !wasEnabled) update(flags); } @@ -514,13 +559,13 @@ void QQmlBinding::setTarget(const QQmlProperty &prop) setTarget(prop.object(), pd->core, &pd->valueTypeData); } -void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType) +bool QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType) { m_target = object; if (!object) { m_targetIndex = QQmlPropertyIndex(); - return; + return false; } int coreIndex = core.coreIndex(); @@ -530,9 +575,10 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const int aValueTypeIndex; if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) { + // can't resolve id (yet) m_target = nullptr; m_targetIndex = QQmlPropertyIndex(); - return; + return false; } if (valueTypeIndex == -1) valueTypeIndex = aValueTypeIndex; @@ -541,7 +587,7 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const if (!data || !data->propertyCache) { m_target = nullptr; m_targetIndex = QQmlPropertyIndex(); - return; + return false; } QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); Q_ASSERT(propertyData); @@ -557,6 +603,8 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const data->propertyCache = QQmlEnginePrivate::get(context()->engine)->cache(m_target->metaObject()); data->propertyCache->addref(); } + + return true; } void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 19ec3f5d4f..a1295bd0ac 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -72,6 +72,8 @@ class Q_QML_PRIVATE_EXPORT QQmlBinding : public QQmlJavaScriptExpression, { friend class QQmlAbstractBinding; public: + typedef QExplicitlySharedDataPointer<QQmlBinding> Ptr; + static QQmlBinding *create(const QQmlPropertyData *, const QQmlScriptString &, QObject *, QQmlContext *); static QQmlBinding *create(const QQmlPropertyData *, const QString &, QObject *, QQmlContextData *, const QString &url = QString(), quint16 lineNumber = 0); @@ -82,7 +84,7 @@ public: ~QQmlBinding() override; void setTarget(const QQmlProperty &); - void setTarget(QObject *, const QQmlPropertyData &, const QQmlPropertyData *valueType); + bool setTarget(QObject *, const QQmlPropertyData &, const QQmlPropertyData *valueType); void setNotifyOnValueChanged(bool); @@ -102,6 +104,12 @@ public: QString expressionIdentifier() const override; void expressionChanged() override; + QQmlSourceLocation sourceLocation() const override; + void setSourceLocation(const QQmlSourceLocation &location); + void setBoundFunction(QV4::BoundFunction *boundFunction) { + m_boundFunction.set(boundFunction->engine(), *boundFunction); + } + /** * This method returns a snapshot of the currently tracked dependencies of * this binding. The dependencies can change upon reevaluation. This method is @@ -121,6 +129,8 @@ protected: bool slowWrite(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData, const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags); + QV4::ReturnedValue evaluate(bool *isUndefined); + private: inline bool updatingFlag() const; inline void setUpdatingFlag(bool); @@ -128,6 +138,9 @@ private: inline void setEnabledFlag(bool); static QQmlBinding *newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property); + + QQmlSourceLocation *m_sourceLocation = nullptr; // used for Qt.binding() created functions + QV4::PersistentValue m_boundFunction; // used for Qt.binding() that are created from a bound function object }; bool QQmlBinding::updatingFlag() const diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 060706ac50..d5117c8cec 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -110,6 +110,12 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, m_index(index), m_target(target) { + // If the function is marked as having a nested function, then the user wrote: + // onSomeSignal: function() { /*....*/ } + // So take that nested function: + if (auto closure = function->nestedFunction()) + function = closure; + setupFunction(scope, function); init(ctxt, scopeObject); } @@ -122,6 +128,12 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, // It's important to call init first, because m_index gets remapped in case of cloned signals. init(ctxt, scope); + // If the function is marked as having a nested function, then the user wrote: + // onSomeSignal: function() { /*....*/ } + // So take that nested function: + if (auto closure = runtimeFunction->nestedFunction()) + runtimeFunction = closure; + QV4::ExecutionEngine *engine = ctxt->engine->handle(); QList<QByteArray> signalParameters = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).parameterNames(); diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 3174bbecd3..fe4768db15 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -929,8 +929,7 @@ void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionS if (0 == enginePriv->inProgressCreations) { while (enginePriv->erroredBindings) { - enginePriv->warning(enginePriv->erroredBindings); - enginePriv->erroredBindings->removeError(); + enginePriv->warning(enginePriv->erroredBindings->removeError()); } } } diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index 6e43bc735f..5dd3278b4c 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -593,6 +593,14 @@ void QQmlContextData::invalidate() parent = nullptr; } +void QQmlContextData::clearContextRecursively() +{ + clearContext(); + + for (auto ctxIt = childContexts; ctxIt; ctxIt = ctxIt->nextChild) + ctxIt->clearContextRecursively(); +} + void QQmlContextData::clearContext() { emitDestruction(); diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h index ff36d6c9a8..5dfee48848 100644 --- a/src/qml/qml/qqmlcontext_p.h +++ b/src/qml/qml/qqmlcontext_p.h @@ -115,6 +115,7 @@ public: QQmlContextData(QQmlContext *); void emitDestruction(); void clearContext(); + void clearContextRecursively(); void invalidate(); inline bool isValid() const { diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index 20b96d2c4b..59fefde893 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -208,12 +208,12 @@ public: QQmlData**prevContextObject; inline bool hasBindingBit(int) const; - void clearBindingBit(int); - void setBindingBit(QObject *obj, int); + inline void setBindingBit(QObject *obj, int); + inline void clearBindingBit(int); inline bool hasPendingBindingBit(int index) const; - void setPendingBindingBit(QObject *obj, int); - void clearPendingBindingBit(int); + inline void setPendingBindingBit(QObject *obj, int); + inline void clearPendingBindingBit(int); quint16 lineNumber; quint16 columnNumber; @@ -304,6 +304,27 @@ private: const BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; return bits[offset] & bitFlagForBit(bit); } + + Q_ALWAYS_INLINE void clearBit(int bit) + { + uint offset = QQmlData::offsetForBit(bit); + if (bindingBitsArraySize > offset) { + BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; + bits[offset] &= ~QQmlData::bitFlagForBit(bit); + } + } + + Q_ALWAYS_INLINE void setBit(QObject *obj, int bit) + { + uint offset = QQmlData::offsetForBit(bit); + BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; + if (Q_UNLIKELY(bindingBitsArraySize <= offset)) + bits = growBits(obj, bit); + bits[offset] |= QQmlData::bitFlagForBit(bit); + } + + Q_NEVER_INLINE BindingBitsType *growBits(QObject *obj, int bit); + Q_DISABLE_COPY(QQmlData); }; @@ -356,6 +377,20 @@ bool QQmlData::hasBindingBit(int coreIndex) const return hasBitSet(coreIndex * 2); } +void QQmlData::setBindingBit(QObject *obj, int coreIndex) +{ + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); + setBit(obj, coreIndex * 2); +} + +void QQmlData::clearBindingBit(int coreIndex) +{ + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); + clearBit(coreIndex * 2); +} + bool QQmlData::hasPendingBindingBit(int coreIndex) const { Q_ASSERT(coreIndex >= 0); @@ -364,6 +399,20 @@ bool QQmlData::hasPendingBindingBit(int coreIndex) const return hasBitSet(coreIndex * 2 + 1); } +void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex) +{ + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); + setBit(obj, coreIndex * 2 + 1); +} + +void QQmlData::clearPendingBindingBit(int coreIndex) +{ + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); + clearBit(coreIndex * 2 + 1); +} + void QQmlData::flushPendingBinding(QObject *o, QQmlPropertyIndex propertyIndex) { QQmlData *data = QQmlData::get(o, false); diff --git a/src/qml/qml/qqmldirparser_p.h b/src/qml/qml/qqmldirparser_p.h index 95370398ad..820c40238d 100644 --- a/src/qml/qml/qqmldirparser_p.h +++ b/src/qml/qml/qqmldirparser_p.h @@ -63,8 +63,6 @@ class QQmlError; class QQmlEngine; class Q_QML_PRIVATE_EXPORT QQmlDirParser { - Q_DISABLE_COPY(QQmlDirParser) - public: QQmlDirParser(); ~QQmlDirParser(); diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 4054d2f0be..7e11177caa 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -88,7 +88,9 @@ #include <private/qqmllistmodel_p.h> #include <private/qqmlplatform_p.h> #include <private/qquickpackage_p.h> +#if QT_CONFIG(qml_delegate_model) #include <private/qqmldelegatemodel_p.h> +#endif #include <private/qqmlobjectmodel_p.h> #include <private/qquickworkerscript_p.h> #include <private/qqmlinstantiator_p.h> @@ -109,12 +111,6 @@ Q_DECLARE_METATYPE(QQmlProperty) QT_BEGIN_NAMESPACE -typedef QQmlData::BindingBitsType BindingBitsType; -enum { - BitsPerType = QQmlData::BitsPerType, - InlineBindingArraySize = QQmlData::InlineBindingArraySize -}; - void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor) { QQmlEnginePrivate::registerBaseTypes(uri, versionMajor, versionMinor); @@ -240,8 +236,10 @@ void QQmlEnginePrivate::registerQtQuick2Types(const char *uri, int versionMajor, qmlRegisterCustomType<QQmlListModel>(uri, versionMajor, versionMinor, "ListModel", new QQmlListModelParser); // Now in QtQml.Models, here for compatibility qmlRegisterType<QQuickWorkerScript>(uri, versionMajor, versionMinor, "WorkerScript"); qmlRegisterType<QQuickPackage>(uri, versionMajor, versionMinor, "Package"); +#if QT_CONFIG(qml_delegate_model) qmlRegisterType<QQmlDelegateModel>(uri, versionMajor, versionMinor, "VisualDataModel"); qmlRegisterType<QQmlDelegateModelGroup>(uri, versionMajor, versionMinor, "VisualDataGroup"); +#endif qmlRegisterType<QQmlObjectModel>(uri, versionMajor, versionMinor, "VisualItemModel"); } @@ -253,6 +251,9 @@ void QQmlEnginePrivate::defineQtQuick2Module() // register the QtQuick2 types which are implemented in the QtQml module. registerQtQuick2Types("QtQuick",2,0); qmlRegisterUncreatableType<QQmlLocale>("QtQuick", 2, 0, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); + + // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward + qmlRegisterModule("QtQuick", 2, QT_VERSION_MINOR); } bool QQmlEnginePrivate::designerMode() @@ -952,6 +953,9 @@ void QQmlEnginePrivate::init() registerBaseTypes("QtQml", 2, 0); // import which provides language building blocks. qmlRegisterUncreatableType<QQmlLocale>("QtQml", 2, 2, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); + // Auto-increment the import to stay in sync with ALL future QtQml minor versions from 5.11 onward + qmlRegisterModule("QtQml", 2, QT_VERSION_MINOR); + QQmlData::init(); baseModulesUninitialized = false; } @@ -1104,7 +1108,9 @@ QQmlEngine::~QQmlEngine() void QQmlEngine::clearComponentCache() { Q_D(QQmlEngine); + d->typeLoader.lock(); d->typeLoader.clearCache(); + d->typeLoader.unlock(); } /*! @@ -1884,66 +1890,27 @@ void QQmlData::parentChanged(QObject *object, QObject *parent) } } -static void QQmlData_setBit(QQmlData *data, QObject *obj, int bit) +QQmlData::BindingBitsType *QQmlData::growBits(QObject *obj, int bit) { - uint offset = QQmlData::offsetForBit(bit); - BindingBitsType *bits = (data->bindingBitsArraySize == InlineBindingArraySize) ? data->bindingBitsValue : data->bindingBits; - if (Q_UNLIKELY(data->bindingBitsArraySize <= offset)) { - int props = QQmlMetaObject(obj).propertyCount(); - Q_ASSERT(bit < 2 * props); - - uint arraySize = (2 * static_cast<uint>(props) + BitsPerType - 1) / BitsPerType; - Q_ASSERT(arraySize > InlineBindingArraySize && arraySize > data->bindingBitsArraySize); + BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; + int props = QQmlMetaObject(obj).propertyCount(); + Q_ASSERT(bit < 2 * props); + Q_UNUSED(bit); // .. for Q_NO_DEBUG mode when the assert above expands to empty - BindingBitsType *newBits = static_cast<BindingBitsType *>(malloc(arraySize*sizeof(BindingBitsType))); - memcpy(newBits, bits, data->bindingBitsArraySize * sizeof(BindingBitsType)); - memset(newBits + data->bindingBitsArraySize, 0, sizeof(BindingBitsType) * (arraySize - data->bindingBitsArraySize)); + uint arraySize = (2 * static_cast<uint>(props) + BitsPerType - 1) / BitsPerType; + Q_ASSERT(arraySize > 1); + Q_ASSERT(arraySize <= 0xffff); // max for bindingBitsArraySize - if (data->bindingBitsArraySize > InlineBindingArraySize) - free(bits); - data->bindingBits = newBits; - bits = newBits; - data->bindingBitsArraySize = arraySize; - } - Q_ASSERT(offset < data->bindingBitsArraySize); - bits[offset] |= QQmlData::bitFlagForBit(bit); -} + BindingBitsType *newBits = static_cast<BindingBitsType *>(malloc(arraySize*sizeof(BindingBitsType))); + memcpy(newBits, bits, bindingBitsArraySize * sizeof(BindingBitsType)); + memset(newBits + bindingBitsArraySize, 0, sizeof(BindingBitsType) * (arraySize - bindingBitsArraySize)); -static void QQmlData_clearBit(QQmlData *data, int bit) -{ - uint offset = QQmlData::offsetForBit(bit); - if (data->bindingBitsArraySize > offset) { - BindingBitsType *bits = (data->bindingBitsArraySize == InlineBindingArraySize) ? data->bindingBitsValue : data->bindingBits; - bits[offset] &= ~QQmlData::bitFlagForBit(bit); - } -} - -void QQmlData::clearBindingBit(int coreIndex) -{ - Q_ASSERT(coreIndex >= 0); - Q_ASSERT(coreIndex <= 0xffff); - QQmlData_clearBit(this, coreIndex * 2); -} - -void QQmlData::setBindingBit(QObject *obj, int coreIndex) -{ - Q_ASSERT(coreIndex >= 0); - Q_ASSERT(coreIndex <= 0xffff); - QQmlData_setBit(this, obj, coreIndex * 2); -} - -void QQmlData::clearPendingBindingBit(int coreIndex) -{ - Q_ASSERT(coreIndex >= 0); - Q_ASSERT(coreIndex <= 0xffff); - QQmlData_clearBit(this, coreIndex * 2 + 1); -} - -void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex) -{ - Q_ASSERT(coreIndex >= 0); - Q_ASSERT(coreIndex <= 0xffff); - QQmlData_setBit(this, obj, coreIndex * 2 + 1); + if (bindingBitsArraySize > InlineBindingArraySize) + free(bits); + bindingBits = newBits; + bits = newBits; + bindingBitsArraySize = arraySize; + return bits; } QQmlData *QQmlData::createQQmlData(QObjectPrivate *priv) @@ -2029,11 +1996,6 @@ void QQmlEnginePrivate::warning(const QList<QQmlError> &errors) dumpwarning(errors); } -void QQmlEnginePrivate::warning(QQmlDelayedError *error) -{ - warning(error->error()); -} - void QQmlEnginePrivate::warning(QQmlEngine *engine, const QQmlError &error) { if (engine) @@ -2050,14 +2012,6 @@ void QQmlEnginePrivate::warning(QQmlEngine *engine, const QList<QQmlError> &erro dumpwarning(error); } -void QQmlEnginePrivate::warning(QQmlEngine *engine, QQmlDelayedError *error) -{ - if (engine) - QQmlEnginePrivate::get(engine)->warning(error); - else - dumpwarning(error->error()); -} - void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QQmlError &error) { if (engine) diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index d6110c6699..da52e01793 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -231,10 +231,8 @@ public: void sendExit(int retCode = 0); void warning(const QQmlError &); void warning(const QList<QQmlError> &); - void warning(QQmlDelayedError *); static void warning(QQmlEngine *, const QQmlError &); static void warning(QQmlEngine *, const QList<QQmlError> &); - static void warning(QQmlEngine *, QQmlDelayedError *); static void warning(QQmlEnginePrivate *, const QQmlError &); static void warning(QQmlEnginePrivate *, const QList<QQmlError> &); diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index 59cc9bb09f..27d3acb9b7 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -252,6 +252,11 @@ QV4::ReturnedValue QQmlExpressionPrivate::v4value(bool *isUndefined) if (!expressionFunctionValid) { createQmlBinding(context(), scopeObject(), expression, url, line); expressionFunctionValid = true; + if (hasError()) { + if (isUndefined) + *isUndefined = true; + return QV4::Encode::undefined(); + } } return evaluate(isUndefined); diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 92cecf9f0d..fe7e5d5a33 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -318,17 +318,17 @@ public: QQmlImportDatabase *database, QString *outQmldirFilePath, QString *outUrl); - static bool validateQmldirVersion(const QQmlTypeLoaderQmldirContent *qmldir, const QString &uri, int vmaj, int vmin, + static bool validateQmldirVersion(const QQmlTypeLoaderQmldirContent &qmldir, const QString &uri, int vmaj, int vmin, QList<QQmlError> *errors); bool importExtension(const QString &absoluteFilePath, const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database, - const QQmlTypeLoaderQmldirContent *qmldir, + const QQmlTypeLoaderQmldirContent &qmldir, QList<QQmlError> *errors); bool getQmldirContent(const QString &qmldirIdentifier, const QString &uri, - const QQmlTypeLoaderQmldirContent **qmldir, QList<QQmlError> *errors); + QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors); QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database); @@ -668,14 +668,14 @@ bool QQmlImports::resolveType(const QHashedStringRef &type, return false; } -bool QQmlImportInstance::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent *qmldir, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors) +bool QQmlImportInstance::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent &qmldir, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors) { Q_ASSERT(resolvedUrl.endsWith(Slash)); url = resolvedUrl; - qmlDirComponents = qmldir->components(); + qmlDirComponents = qmldir.components(); - const QQmlDirScripts &scripts = qmldir->scripts(); + const QQmlDirScripts &scripts = qmldir.scripts(); if (!scripts.isEmpty()) { // Verify that we haven't imported these scripts already for (QList<QQmlImportInstance *>::const_iterator it = nameSpace->imports.constBegin(); @@ -1068,26 +1068,26 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database, - const QQmlTypeLoaderQmldirContent *qmldir, + const QQmlTypeLoaderQmldirContent &qmldir, QList<QQmlError> *errors) { - Q_ASSERT(qmldir); + Q_ASSERT(qmldir.hasContent()); if (qmlImportTrace()) qDebug().nospace() << "QQmlImports(" << qPrintable(base) << ")::importExtension: " << "loaded " << qmldirFilePath; - if (designerSupportRequired && !qmldir->designerSupported()) { + if (designerSupportRequired && !qmldir.designerSupported()) { if (errors) { QQmlError error; - error.setDescription(QQmlImportDatabase::tr("module does not support the designer \"%1\"").arg(qmldir->typeNamespace())); + error.setDescription(QQmlImportDatabase::tr("module does not support the designer \"%1\"").arg(qmldir.typeNamespace())); error.setUrl(QUrl::fromLocalFile(qmldirFilePath)); errors->prepend(error); } return false; } - int qmldirPluginCount = qmldir->plugins().count(); + int qmldirPluginCount = qmldir.plugins().count(); if (qmldirPluginCount == 0) return true; @@ -1098,7 +1098,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, // listed plugin inside qmldir. And for this reason, mixing dynamic and static plugins inside a // single module is not recommended. - QString typeNamespace = qmldir->typeNamespace(); + QString typeNamespace = qmldir.typeNamespace(); QString qmldirPath = qmldirFilePath; int slash = qmldirPath.lastIndexOf(Slash); if (slash > 0) @@ -1108,7 +1108,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, int staticPluginsFound = 0; #if defined(QT_SHARED) - const auto qmldirPlugins = qmldir->plugins(); + const auto qmldirPlugins = qmldir.plugins(); for (const QQmlDirParser::Plugin &plugin : qmldirPlugins) { QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name); if (!resolvedFilePath.isEmpty()) { @@ -1174,7 +1174,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, if (qmldirPluginCount > 1 && staticPluginsFound > 0) error.setDescription(QQmlImportDatabase::tr("could not resolve all plugins for module \"%1\"").arg(uri)); else - error.setDescription(QQmlImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(qmldir->plugins()[dynamicPluginsFound].name)); + error.setDescription(QQmlImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(qmldir.plugins()[dynamicPluginsFound].name)); error.setUrl(QUrl::fromLocalFile(qmldirFilePath)); errors->prepend(error); } @@ -1187,17 +1187,17 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, } bool QQmlImportsPrivate::getQmldirContent(const QString &qmldirIdentifier, const QString &uri, - const QQmlTypeLoaderQmldirContent **qmldir, QList<QQmlError> *errors) + QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors) { Q_ASSERT(errors); Q_ASSERT(qmldir); *qmldir = typeLoader->qmldirContent(qmldirIdentifier); - if (*qmldir) { + if ((*qmldir).hasContent()) { // Ensure that parsing was successful - if ((*qmldir)->hasError()) { + if ((*qmldir).hasError()) { QUrl url = QUrl::fromLocalFile(qmldirIdentifier); - const QList<QQmlError> qmldirErrors = (*qmldir)->errors(uri); + const QList<QQmlError> qmldirErrors = (*qmldir).errors(uri); for (int i = 0; i < qmldirErrors.size(); ++i) { QQmlError error = qmldirErrors.at(i); error.setUrl(url); @@ -1322,14 +1322,14 @@ bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQ return false; } -bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoaderQmldirContent *qmldir, const QString &uri, int vmaj, int vmin, +bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoaderQmldirContent &qmldir, const QString &uri, int vmaj, int vmin, QList<QQmlError> *errors) { int lowest_min = INT_MAX; int highest_min = INT_MIN; typedef QQmlDirComponents::const_iterator ConstIterator; - const QQmlDirComponents &components = qmldir->components(); + const QQmlDirComponents &components = qmldir.components(); ConstIterator cend = components.constEnd(); for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) { @@ -1353,7 +1353,7 @@ bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoaderQmldirContent } typedef QList<QQmlDirParser::Script>::const_iterator SConstIterator; - const QQmlDirScripts &scripts = qmldir->scripts(); + const QQmlDirScripts &scripts = qmldir.scripts(); SConstIterator send = scripts.constEnd(); for (SConstIterator sit = scripts.constBegin(); sit != send; ++sit) { @@ -1445,14 +1445,14 @@ bool QQmlImportsPrivate::addLibraryImport(const QString& uri, const QString &pre Q_ASSERT(inserted); if (!incomplete) { - const QQmlTypeLoaderQmldirContent *qmldir = nullptr; + QQmlTypeLoaderQmldirContent qmldir; if (!qmldirIdentifier.isEmpty()) { if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors)) return false; - if (qmldir) { - if (!importExtension(qmldir->pluginLocation(), uri, vmaj, vmin, database, qmldir, errors)) + if (qmldir.hasContent()) { + if (!importExtension(qmldir.pluginLocation(), uri, vmaj, vmin, database, qmldir, errors)) return false; if (!inserted->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors)) @@ -1470,7 +1470,7 @@ bool QQmlImportsPrivate::addLibraryImport(const QString& uri, const QString &pre error.setDescription(QQmlImportDatabase::tr("module \"%1\" is not installed").arg(uri)); errors->prepend(error); return false; - } else if ((vmaj >= 0) && (vmin >= 0) && qmldir) { + } else if ((vmaj >= 0) && (vmin >= 0) && qmldir.hasContent()) { // Verify that the qmldir content is valid for this version if (!validateQmldirVersion(qmldir, uri, vmaj, vmin, errors)) return false; @@ -1564,12 +1564,12 @@ bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix Q_ASSERT(inserted); if (!incomplete && !qmldirIdentifier.isEmpty()) { - const QQmlTypeLoaderQmldirContent *qmldir = nullptr; + QQmlTypeLoaderQmldirContent qmldir; if (!getQmldirContent(qmldirIdentifier, importUri, &qmldir, errors)) return false; - if (qmldir) { - if (!importExtension(qmldir->pluginLocation(), importUri, vmaj, vmin, database, qmldir, errors)) + if (qmldir.hasContent()) { + if (!importExtension(qmldir.pluginLocation(), importUri, vmaj, vmin, database, qmldir, errors)) return false; if (!inserted->setQmldirContent(url, qmldir, nameSpace, errors)) @@ -1588,14 +1588,14 @@ bool QQmlImportsPrivate::updateQmldirContent(const QString &uri, const QString & Q_ASSERT(nameSpace); if (QQmlImportInstance *import = nameSpace->findImport(uri)) { - const QQmlTypeLoaderQmldirContent *qmldir = nullptr; + QQmlTypeLoaderQmldirContent qmldir; if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors)) return false; - if (qmldir) { + if (qmldir.hasContent()) { int vmaj = import->majversion; int vmin = import->minversion; - if (!importExtension(qmldir->pluginLocation(), uri, vmaj, vmin, database, qmldir, errors)) + if (!importExtension(qmldir.pluginLocation(), uri, vmaj, vmin, database, qmldir, errors)) return false; if (import->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors)) { diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index b70bb5253c..2437979ef8 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -85,7 +85,7 @@ struct QQmlImportInstance QQmlDirComponents qmlDirComponents; // a copy of the components listed in the qmldir QQmlDirScripts qmlDirScripts; // a copy of the scripts in the qmldir - bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent *qmldir, + bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent &qmldir, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors); static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin); diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index 4546a4423f..df168960c6 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -367,10 +367,8 @@ finishIncubate: enginePriv->inProgressCreations--; if (0 == enginePriv->inProgressCreations) { - while (enginePriv->erroredBindings) { - enginePriv->warning(enginePriv->erroredBindings); - enginePriv->erroredBindings->removeError(); - } + while (enginePriv->erroredBindings) + enginePriv->warning(enginePriv->erroredBindings->removeError()); } } else if (!creator.isNull()) { vmeGuard.guard(creator.data()); @@ -575,10 +573,8 @@ void QQmlIncubator::clear() enginePriv->inProgressCreations--; if (0 == enginePriv->inProgressCreations) { - while (enginePriv->erroredBindings) { - enginePriv->warning(enginePriv->erroredBindings); - enginePriv->erroredBindings->removeError(); - } + while (enginePriv->erroredBindings) + enginePriv->warning(enginePriv->erroredBindings->removeError()); } } diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 74148e3ca4..93ec9421ed 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -97,8 +97,7 @@ QQmlJavaScriptExpression::QQmlJavaScriptExpression() m_context(nullptr), m_prevExpression(nullptr), m_nextExpression(nullptr), - m_v4Function(nullptr), - m_sourceLocation(nullptr) + m_v4Function(nullptr) { } @@ -115,8 +114,6 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression() clearError(); if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion. m_scopeObject.asT2()->_s = nullptr; - - delete m_sourceLocation; } void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v) @@ -137,20 +134,11 @@ void QQmlJavaScriptExpression::resetNotifyOnValueChanged() QQmlSourceLocation QQmlJavaScriptExpression::sourceLocation() const { - if (m_sourceLocation) - return *m_sourceLocation; if (m_v4Function) return m_v4Function->sourceLocation(); return QQmlSourceLocation(); } -void QQmlJavaScriptExpression::setSourceLocation(const QQmlSourceLocation &location) -{ - if (m_sourceLocation) - delete m_sourceLocation; - m_sourceLocation = new QQmlSourceLocation(location); -} - void QQmlJavaScriptExpression::setContext(QQmlContextData *context) { if (m_prevExpression) { @@ -451,15 +439,11 @@ void QQmlJavaScriptExpression::createQmlBinding(QQmlContextData *ctxt, QObject * QV4::Script script(v4, qmlContext, code, filename, line); script.parse(); if (v4->hasException) { - QQmlError error = v4->catchExceptionAsQmlError(); - if (error.description().isEmpty()) - error.setDescription(QLatin1String("Exception occurred during function evaluation")); - if (error.line() == -1) - error.setLine(line); - if (error.url().isEmpty()) - error.setUrl(QUrl::fromLocalFile(filename)); - error.setObject(qmlScope); - ep->warning(error); + QQmlDelayedError *error = delayedError(); + error->catchJavaScriptException(v4); + error->setErrorObject(qmlScope); + if (!error->addError(ep)) + ep->warning(error->error()); return; } setupFunction(qmlContext, script.vmFunction); diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index a028850074..01af3b89ca 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -63,16 +63,18 @@ class QQmlDelayedError { public: inline QQmlDelayedError() : nextError(nullptr), prevError(nullptr) {} - inline ~QQmlDelayedError() { removeError(); } + inline ~QQmlDelayedError() { (void)removeError(); } bool addError(QQmlEnginePrivate *); - inline void removeError() { - if (!prevError) return; - if (nextError) nextError->prevError = prevError; - *prevError = nextError; - nextError = nullptr; - prevError = nullptr; + Q_REQUIRED_RESULT inline QQmlError removeError() { + if (prevError) { + if (nextError) nextError->prevError = prevError; + *prevError = nextError; + nextError = nullptr; + prevError = nullptr; + } + return m_error; } inline bool isValid() const { return m_error.isValid(); } @@ -114,8 +116,7 @@ public: inline QObject *scopeObject() const; inline void setScopeObject(QObject *v); - QQmlSourceLocation sourceLocation() const; - void setSourceLocation(const QQmlSourceLocation &location); + virtual QQmlSourceLocation sourceLocation() const; bool isValid() const { return context() != nullptr; } @@ -186,7 +187,6 @@ private: QV4::PersistentValue m_qmlScope; QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit; QV4::Function *m_v4Function; - QQmlSourceLocation *m_sourceLocation; // used for Qt.binding() created functions }; class QQmlPropertyCapture diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 7754f0fddc..8fda7f6f77 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -51,6 +51,7 @@ #include <QtCore/qbitarray.h> #include <QtCore/qreadwritelock.h> #include <QtCore/private/qmetaobject_p.h> +#include <QtCore/qloggingcategory.h> #include <qmetatype.h> #include <qobjectdefs.h> @@ -63,6 +64,8 @@ #include <ctype.h> #include "qqmlcomponent.h" +Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) + QT_BEGIN_NAMESPACE struct QQmlMetaTypeData @@ -2539,18 +2542,46 @@ QList<QQmlType> QQmlMetaType::qmlSingletonTypes() return retn; } -const QV4::CompiledData::Unit *QQmlMetaType::findCachedCompilationUnit(const QUrl &uri) +const QV4::CompiledData::Unit *QQmlMetaType::findCachedCompilationUnit(const QUrl &uri, CachedUnitLookupError *status) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); for (const auto lookup : qAsConst(data->lookupCachedQmlUnit)) { - if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) + if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) { + QString error; + if (!unit->qmlData->verifyHeader(QDateTime(), &error)) { + qCDebug(DBG_DISK_CACHE) << "Error loading pre-compiled file " << uri << ":" << error; + if (status) + *status = CachedUnitLookupError::VersionMismatch; + return nullptr; + } + if (status) + *status = CachedUnitLookupError::NoError; return unit->qmlData; + } } + + if (status) + *status = CachedUnitLookupError::NoUnitFound; + return nullptr; } +void QQmlMetaType::prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) +{ + QMutexLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + data->lookupCachedQmlUnit.prepend(handler); +} + +void QQmlMetaType::removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) +{ + QMutexLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + data->lookupCachedQmlUnit.removeAll(handler); +} + /*! Returns the pretty QML type name (e.g. 'Item' instead of 'QtQuickItem') for the given object. */ diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 07bef526ba..cd7afc8a01 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -133,7 +133,17 @@ public: static QList<QQmlPrivate::AutoParentFunction> parentFunctions(); - static const QV4::CompiledData::Unit *findCachedCompilationUnit(const QUrl &uri); + enum class CachedUnitLookupError { + NoError, + NoUnitFound, + VersionMismatch + }; + + static const QV4::CompiledData::Unit *findCachedCompilationUnit(const QUrl &uri, CachedUnitLookupError *status); + + // used by tst_qqmlcachegen.cpp + static void prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler); + static void removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler); static bool namespaceContainsRegistrations(const QString &, int majorVersion); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 36e56a01f8..7051fb51da 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -55,6 +55,7 @@ #include <private/qqmlvaluetypeproxybinding_p.h> #include <private/qqmldebugconnector_p.h> #include <private/qqmldebugserviceinterfaces_p.h> +#include <private/qjsvalue_p.h> QT_USE_NAMESPACE @@ -780,7 +781,7 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) qSwap(_currentList, savedList); } -bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) +bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProperty, const QV4::CompiledData::Binding *binding) { if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { Q_ASSERT(stringAt(qmlUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty()); @@ -802,7 +803,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } // ### resolve this at compile time - if (property && property->propType() == qMetaTypeId<QQmlScriptString>()) { + if (bindingProperty && bindingProperty->propType() == qMetaTypeId<QQmlScriptString>()) { QQmlScriptString ss(binding->valueAsScriptString(qmlUnit), context->asQQmlContext(), _scopeObject); ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid; ss.d.data()->lineNumber = binding->location.line; @@ -815,7 +816,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QQmlPropertyData::RemoveBindingOnAliasWrite; int propertyWriteStatus = -1; void *argv[] = { &ss, nullptr, &propertyWriteStatus, &propertyWriteFlags }; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); return true; } @@ -826,7 +827,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; } - if (!property) // ### error + if (!bindingProperty) // ### error return true; if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty) { @@ -838,20 +839,20 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con const QQmlPropertyData *valueTypeProperty = nullptr; QObject *bindingTarget = _bindingTarget; - if (QQmlValueTypeFactory::isValueType(property->propType())) { - valueType = QQmlValueTypeFactory::valueType(property->propType()); + if (QQmlValueTypeFactory::isValueType(bindingProperty->propType())) { + valueType = QQmlValueTypeFactory::valueType(bindingProperty->propType()); if (!valueType) { recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex))); return false; } - valueType->read(_qobject, property->coreIndex()); + valueType->read(_qobject, bindingProperty->coreIndex()); groupObject = valueType; - valueTypeProperty = property; + valueTypeProperty = bindingProperty; } else { void *argv[1] = { &groupObject }; - QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, bindingProperty->coreIndex(), argv); if (!groupObject) { recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex))); return false; @@ -864,21 +865,21 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; if (valueType) - valueType->write(_qobject, property->coreIndex(), QQmlPropertyData::BypassInterceptor); + valueType->write(_qobject, bindingProperty->coreIndex(), QQmlPropertyData::BypassInterceptor); return true; } } - if (_ddata->hasBindingBit(property->coreIndex()) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) + if (_ddata->hasBindingBit(bindingProperty->coreIndex()) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) && !_valueTypeProperty) - QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(property->coreIndex())); + QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex())); if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->containsTranslations()) { if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; - int signalIndex = _propertyCache->methodIndexToSignalIndex(property->coreIndex()); + int signalIndex = _propertyCache->methodIndexToSignalIndex(bindingProperty->coreIndex()); QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine); QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_bindingTarget, signalIndex, context, _scopeObject, runtimeFunction, currentQmlContext()); @@ -890,34 +891,44 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con // the point property (_qobjectForBindings) and after evaluating the expression, // the result is written to a value type virtual property, that contains the sub-index // of the "x" property. - QQmlBinding *qmlBinding; - const QQmlPropertyData *prop = property; + QQmlBinding::Ptr qmlBinding; + const QQmlPropertyData *targetProperty = bindingProperty; const QQmlPropertyData *subprop = nullptr; if (_valueTypeProperty) { - prop = _valueTypeProperty; - subprop = property; + targetProperty = _valueTypeProperty; + subprop = bindingProperty; } if (binding->containsTranslations()) { qmlBinding = QQmlBinding::createTranslationBinding(compilationUnit, binding, _scopeObject, context); } else { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; - qmlBinding = QQmlBinding::create(prop, runtimeFunction, _scopeObject, context, currentQmlContext()); + qmlBinding = QQmlBinding::create(targetProperty, runtimeFunction, _scopeObject, context, currentQmlContext()); } - qmlBinding->setTarget(_bindingTarget, *prop, subprop); - sharedState->allCreatedBindings.push(QQmlAbstractBinding::Ptr(qmlBinding)); + auto bindingTarget = _bindingTarget; + auto valueTypeProperty = _valueTypeProperty; + auto assignBinding = [qmlBinding, bindingTarget, targetProperty, subprop, bindingProperty, valueTypeProperty](QQmlObjectCreatorSharedState *sharedState) -> bool { + if (!qmlBinding->setTarget(bindingTarget, *targetProperty, subprop) && targetProperty->isAlias()) + return false; - if (property->isAlias()) { - QQmlPropertyPrivate::setBinding(qmlBinding, QQmlPropertyPrivate::DontEnable); - } else { - qmlBinding->addToObject(); + sharedState->allCreatedBindings.push(qmlBinding); + + if (bindingProperty->isAlias()) { + QQmlPropertyPrivate::setBinding(qmlBinding.data(), QQmlPropertyPrivate::DontEnable); + } else { + qmlBinding->addToObject(); - if (!_valueTypeProperty) { - QQmlData *targetDeclarativeData = QQmlData::get(_bindingTarget); - Q_ASSERT(targetDeclarativeData); - targetDeclarativeData->setPendingBindingBit(_bindingTarget, property->coreIndex()); + if (!valueTypeProperty) { + QQmlData *targetDeclarativeData = QQmlData::get(bindingTarget); + Q_ASSERT(targetDeclarativeData); + targetDeclarativeData->setPendingBindingBit(bindingTarget, bindingProperty->coreIndex()); + } } - } + + return true; + }; + if (!assignBinding(sharedState.data())) + pendingAliasBindings.push_back(assignBinding); } return true; } @@ -934,9 +945,9 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QObject *target = createdSubObject->parent(); QQmlProperty prop; if (_valueTypeProperty) - prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, property, context); + prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, bindingProperty, context); else - prop = QQmlPropertyPrivate::restore(target, *property, nullptr, context); + prop = QQmlPropertyPrivate::restore(target, *bindingProperty, nullptr, context); vs->setTarget(prop); return true; } @@ -946,8 +957,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QObject *target = createdSubObject->parent(); QQmlPropertyIndex propertyIndex; - if (property->isAlias()) { - QQmlPropertyIndex originalIndex(property->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1); + if (bindingProperty->isAlias()) { + QQmlPropertyIndex originalIndex(bindingProperty->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1); QQmlPropertyIndex propIndex; QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex); QQmlData *data = QQmlData::get(target); @@ -964,9 +975,9 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } else { QQmlProperty prop; if (_valueTypeProperty) - prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, property, context); + prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, bindingProperty, context); else - prop = QQmlPropertyPrivate::restore(target, *property, nullptr, context); + prop = QQmlPropertyPrivate::restore(target, *bindingProperty, nullptr, context); vi->setTarget(prop); propertyIndex = QQmlPropertyPrivate::propertyIndex(prop); } @@ -982,8 +993,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con // Assigning object to signal property? if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) { - if (!property->isFunction()) { - recordError(binding->valueLocation, tr("Cannot assign an object to signal property %1").arg(property->name(_qobject))); + if (!bindingProperty->isFunction()) { + recordError(binding->valueLocation, tr("Cannot assign an object to signal property %1").arg(bindingProperty->name(_qobject))); return false; } QMetaMethod method = QQmlMetaType::defaultMethod(createdSubObject); @@ -992,7 +1003,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; } - QMetaMethod signalMethod = _qobject->metaObject()->method(property->coreIndex()); + QMetaMethod signalMethod = _qobject->metaObject()->method(bindingProperty->coreIndex()); if (!QMetaObject::checkConnectArgs(signalMethod, method)) { recordError(binding->valueLocation, tr("Cannot connect mismatched signal/slot %1 %vs. %2") @@ -1001,7 +1012,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; } - QQmlPropertyPrivate::connect(_qobject, property->coreIndex(), createdSubObject, method.methodIndex()); + QQmlPropertyPrivate::connect(_qobject, bindingProperty->coreIndex(), createdSubObject, method.methodIndex()); return true; } @@ -1010,32 +1021,43 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con int propertyWriteStatus = -1; void *argv[] = { nullptr, nullptr, &propertyWriteStatus, &propertyWriteFlags }; - if (const char *iid = QQmlMetaType::interfaceIId(property->propType())) { + if (const char *iid = QQmlMetaType::interfaceIId(bindingProperty->propType())) { void *ptr = createdSubObject->qt_metacast(iid); if (ptr) { argv[0] = &ptr; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); } else { recordError(binding->location, tr("Cannot assign object to interface property")); return false; } - } else if (property->propType() == QMetaType::QVariant) { - if (property->isVarProperty()) { + } else if (bindingProperty->propType() == QMetaType::QVariant) { + if (bindingProperty->isVarProperty()) { QV4::Scope scope(v4); QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(engine->handle(), createdSubObject)); - _vmeMetaObject->setVMEProperty(property->coreIndex(), wrappedObject); + _vmeMetaObject->setVMEProperty(bindingProperty->coreIndex(), wrappedObject); } else { QVariant value = QVariant::fromValue(createdSubObject); argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); } - } else if (property->isQList()) { + } else if (bindingProperty->propType() == qMetaTypeId<QJSValue>()) { + QV4::Scope scope(v4); + QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(engine->handle(), createdSubObject)); + if (bindingProperty->isVarProperty()) { + _vmeMetaObject->setVMEProperty(bindingProperty->coreIndex(), wrappedObject); + } else { + QJSValue value; + QJSValuePrivate::setValue(&value, v4, wrappedObject); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); + } + } else if (bindingProperty->isQList()) { Q_ASSERT(_currentList.object); void *itemToAdd = createdSubObject; const char *iid = nullptr; - int listItemType = QQmlEnginePrivate::get(engine)->listType(property->propType()); + int listItemType = QQmlEnginePrivate::get(engine)->listType(bindingProperty->propType()); if (listItemType != -1) iid = QQmlMetaType::interfaceIId(listItemType); if (iid) @@ -1051,17 +1073,17 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } else { // pointer compatibility was tested in QQmlPropertyValidator at type compile time argv[0] = &createdSubObject; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); } return true; } - if (property->isQList()) { + if (bindingProperty->isQList()) { recordError(binding->location, tr("Cannot assign primitives to lists")); return false; } - setPropertyValue(property, binding); + setPropertyValue(bindingProperty, binding); return true; } @@ -1274,12 +1296,33 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo qSwap(_qmlContext, qmlContext); - bool result = populateInstance(index, instance, /*binding target*/instance, /*value type property*/nullptr); + bool ok = populateInstance(index, instance, /*binding target*/instance, /*value type property*/nullptr); + if (ok) { + if (isContextObject && !pendingAliasBindings.empty()) { + bool processedAtLeastOneBinding = false; + do { + processedAtLeastOneBinding = false; + for (std::vector<PendingAliasBinding>::iterator it = pendingAliasBindings.begin(); + it != pendingAliasBindings.end(); ) { + if ((*it)(sharedState.data())) { + it = pendingAliasBindings.erase(it); + processedAtLeastOneBinding = true; + } else { + ++it; + } + } + } while (processedAtLeastOneBinding && pendingAliasBindings.empty()); + Q_ASSERT(pendingAliasBindings.empty()); + } + } else { + // an error occurred, so we can't setup the pending alias bindings + pendingAliasBindings.clear(); + } qSwap(_qmlContext, qmlContext); qSwap(_scopeObject, scopeObject); - return result ? instance : nullptr; + return ok ? instance : nullptr; } QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt) diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 399a5f6d4a..67a5bdd827 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -161,6 +161,9 @@ private: QV4::QmlContext *_qmlContext; friend struct QQmlObjectCreatorRecursionWatcher; + + typedef std::function<bool(QQmlObjectCreatorSharedState *sharedState)> PendingAliasBinding; + std::vector<PendingAliasBinding> pendingAliasBindings; }; struct QQmlObjectCreatorRecursionWatcher diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index ebf296b29d..c4487f91a3 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -877,6 +877,7 @@ void QQmlPropertyPrivate::findAliasTarget(QObject *object, QQmlPropertyIndex bin void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, QQmlPropertyData::WriteFlags writeFlags) { Q_ASSERT(binding); + Q_ASSERT(binding->targetObject()); QObject *object = binding->targetObject(); const QQmlPropertyIndex index = binding->targetPropertyIndex(); diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 51a191a41f..b78a2ddd20 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -81,6 +81,7 @@ template <typename T> class QQmlPropertyCacheAliasCreator; // We have this somewhat awful split between RawData and Data so that RawData can be // used in unions. In normal code, you should always use Data which initializes RawData // to an invalid state on construction. +// ### We should be able to remove this split nowadays class QQmlPropertyRawData { public: @@ -273,20 +274,20 @@ public: private: Flags _flags; - qint16 _coreIndex; - quint16 _propType; + qint16 _coreIndex = 0; + quint16 _propType = 0; // The notify index is in the range returned by QObjectPrivate::signalIndex(). // This is different from QMetaMethod::methodIndex(). - qint16 _notifyIndex; - qint16 _overrideIndex; + qint16 _notifyIndex = 0; + qint16 _overrideIndex = 0; - quint8 _revision; - quint8 _typeMinorVersion; - qint16 _metaObjectOffset; + quint8 _revision = 0; + quint8 _typeMinorVersion = 0; + qint16 _metaObjectOffset = 0; - QQmlPropertyCacheMethodArguments *_arguments; - StaticMetaCallFunction _staticMetaCallFunction; + QQmlPropertyCacheMethodArguments *_arguments = nullptr; + StaticMetaCallFunction _staticMetaCallFunction = nullptr; friend class QQmlPropertyData; friend class QQmlPropertyCache; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 81bda2c3bb..ed742c3681 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -984,16 +984,6 @@ void QQmlTypeLoader::setProfiler(QQmlProfiler *profiler) } #endif -void QQmlTypeLoader::lock() -{ - m_thread->lock(); -} - -void QQmlTypeLoader::unlock() -{ - m_thread->unlock(); -} - struct PlainLoader { void loadThread(QQmlTypeLoader *loader, QQmlDataBlob *blob) const { @@ -1276,6 +1266,7 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data) QML_MEMORY_SCOPE_URL(blob->url()); QQmlDataBlob::SourceCodeData d; d.inlineSourceCode = QString::fromUtf8(data); + d.hasInlineSourceCode = true; setData(blob, d); } @@ -1387,8 +1378,8 @@ bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QV4::Compile if (!importQualifier.isEmpty()) { // Does this library contain any qualified scripts? QUrl libraryUrl(qmldirUrl); - const QQmlTypeLoaderQmldirContent *qmldir = typeLoader()->qmldirContent(qmldirIdentifier); - const auto qmldirScripts = qmldir->scripts(); + const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirIdentifier); + const auto qmldirScripts = qmldir.scripts(); for (const QQmlDirParser::Script &script : qmldirScripts) { QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl); @@ -1435,8 +1426,8 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL if (!importQualifier.isEmpty()) { // Does this library contain any qualified scripts? QUrl libraryUrl(qmldirUrl); - const QQmlTypeLoaderQmldirContent *qmldir = typeLoader()->qmldirContent(qmldirFilePath); - const auto qmldirScripts = qmldir->scripts(); + const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirFilePath); + const auto qmldirScripts = qmldir.scripts(); for (const QQmlDirParser::Script &script : qmldirScripts) { QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl); @@ -1601,6 +1592,7 @@ QString QQmlTypeLoaderQmldirContent::typeNamespace() const void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QString &content) { + m_hasContent = true; m_location = location; m_parser.parse(content); } @@ -1639,8 +1631,10 @@ bool QQmlTypeLoaderQmldirContent::designerSupported() const Constructs a new type loader that uses the given \a engine. */ QQmlTypeLoader::QQmlTypeLoader(QQmlEngine *engine) - : m_engine(engine), m_thread(new QQmlTypeLoaderThread(this)), - m_typeCacheTrimThreshold(TYPELOADER_MINIMUM_TRIM_THRESHOLD) + : m_engine(engine) + , m_thread(new QQmlTypeLoaderThread(this)) + , m_mutex(m_thread->mutex()) + , m_typeCacheTrimThreshold(TYPELOADER_MINIMUM_TRIM_THRESHOLD) { } @@ -1684,9 +1678,11 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &url, Mode mode) typeData = new QQmlTypeData(url, this); // TODO: if (compiledData == 0), is it safe to omit this insertion? m_typeCache.insert(url, typeData); - if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(typeData->url())) { + QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; + if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(typeData->url(), &error)) { QQmlTypeLoader::loadWithCachedUnit(typeData, cachedUnit, mode); } else { + typeData->setCachedUnitStatus(error); QQmlTypeLoader::load(typeData, mode); } } else if ((mode == PreferSynchronous || mode == Synchronous) && QQmlFile::isSynchronous(url)) { @@ -1745,9 +1741,11 @@ QQmlScriptBlob *QQmlTypeLoader::getScript(const QUrl &url) scriptBlob = new QQmlScriptBlob(url, this); m_scriptCache.insert(url, scriptBlob); - if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(scriptBlob->url())) { + QQmlMetaType::CachedUnitLookupError error; + if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(scriptBlob->url(), &error)) { QQmlTypeLoader::loadWithCachedUnit(scriptBlob, cachedUnit); } else { + scriptBlob->setCachedUnitStatus(error); QQmlTypeLoader::load(scriptBlob); } } @@ -1831,6 +1829,7 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path) int lastSlash = path.lastIndexOf(QLatin1Char('/')); QString dirPath(path.left(lastSlash)); + LockHolder<QQmlTypeLoader> holder(this); if (!m_importDirCache.contains(dirPath)) { bool exists = QDir(dirPath).exists(); QCache<QString, bool> *entry = exists ? new QCache<QString, bool> : nullptr; @@ -1894,6 +1893,7 @@ bool QQmlTypeLoader::directoryExists(const QString &path) --length; QString dirPath(path.left(length)); + LockHolder<QQmlTypeLoader> holder(this); if (!m_importDirCache.contains(dirPath)) { bool exists = QDir(dirPath).exists(); QCache<QString, bool> *files = exists ? new QCache<QString, bool> : nullptr; @@ -1912,8 +1912,10 @@ Return a QQmlTypeLoaderQmldirContent for absoluteFilePath. The QQmlTypeLoaderQm It can also be a remote path for a remote directory import, but it will have been cached by now in this case. */ -const QQmlTypeLoaderQmldirContent *QQmlTypeLoader::qmldirContent(const QString &filePathIn) +const QQmlTypeLoaderQmldirContent QQmlTypeLoader::qmldirContent(const QString &filePathIn) { + LockHolder<QQmlTypeLoader> holder(this); + QString filePath; // Try to guess if filePathIn is already a URL. This is necessarily fragile, because @@ -1927,39 +1929,39 @@ const QQmlTypeLoaderQmldirContent *QQmlTypeLoader::qmldirContent(const QString & filePath = filePathIn; } else { filePath = QQmlFile::urlToLocalFileOrQrc(url); - if (filePath.isEmpty()) // Can't load the remote here, but should be cached - return *(m_importQmlDirCache.value(filePathIn)); + if (filePath.isEmpty()) { // Can't load the remote here, but should be cached + if (auto entry = m_importQmlDirCache.value(filePathIn)) + return **entry; + else + return QQmlTypeLoaderQmldirContent(); + } } - QQmlTypeLoaderQmldirContent *qmldir; QQmlTypeLoaderQmldirContent **val = m_importQmlDirCache.value(filePath); - if (!val) { - qmldir = new QQmlTypeLoaderQmldirContent; + if (val) + return **val; + QQmlTypeLoaderQmldirContent *qmldir = new QQmlTypeLoaderQmldirContent; #define ERROR(description) { QQmlError e; e.setDescription(description); qmldir->setError(e); } #define NOT_READABLE_ERROR QString(QLatin1String("module \"$$URI$$\" definition \"%1\" not readable")) #define CASE_MISMATCH_ERROR QString(QLatin1String("cannot load module \"$$URI$$\": File name case mismatch for \"%1\"")) - QFile file(filePath); - if (!QQml_isFileCaseCorrect(filePath)) { - ERROR(CASE_MISMATCH_ERROR.arg(filePath)); - } else if (file.open(QFile::ReadOnly)) { - QByteArray data = file.readAll(); - qmldir->setContent(filePath, QString::fromUtf8(data)); - } else { - ERROR(NOT_READABLE_ERROR.arg(filePath)); - } + QFile file(filePath); + if (!QQml_isFileCaseCorrect(filePath)) { + ERROR(CASE_MISMATCH_ERROR.arg(filePath)); + } else if (file.open(QFile::ReadOnly)) { + QByteArray data = file.readAll(); + qmldir->setContent(filePath, QString::fromUtf8(data)); + } else { + ERROR(NOT_READABLE_ERROR.arg(filePath)); + } #undef ERROR #undef NOT_READABLE_ERROR #undef CASE_MISMATCH_ERROR - m_importQmlDirCache.insert(filePath, qmldir); - } else { - qmldir = *val; - } - - return qmldir; + m_importQmlDirCache.insert(filePath, qmldir); + return *qmldir; } void QQmlTypeLoader::setQmldirContent(const QString &url, const QString &content) @@ -2446,8 +2448,13 @@ void QQmlTypeData::dataReceived(const SourceCodeData &data) if (isError()) return; - if (!m_backupSourceCode.exists()) { - setError(QQmlTypeLoader::tr("No such file or directory")); + if (!m_backupSourceCode.exists() || m_backupSourceCode.isEmpty()) { + if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch) + setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile")); + else if (!m_backupSourceCode.exists()) + setError(QQmlTypeLoader::tr("No such file or directory")); + else + setError(QQmlTypeLoader::tr("File is empty")); return; } @@ -2999,6 +3006,13 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data) } } + if (!data.exists()) { + if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch) + setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile")); + else + setError(QQmlTypeLoader::tr("No such file or directory")); + return; + } QmlIR::Document irUnit(isDebugging()); @@ -3196,7 +3210,7 @@ void QQmlQmldirData::initializeFromCachedUnit(const QV4::CompiledData::Unit *) QString QQmlDataBlob::SourceCodeData::readAll(QString *error) const { error->clear(); - if (!inlineSourceCode.isEmpty()) + if (hasInlineSourceCode) return inlineSourceCode; QFile f(fileInfo.absoluteFilePath()); @@ -3223,7 +3237,7 @@ QString QQmlDataBlob::SourceCodeData::readAll(QString *error) const QDateTime QQmlDataBlob::SourceCodeData::sourceTimeStamp() const { - if (!inlineSourceCode.isEmpty()) + if (hasInlineSourceCode) return QDateTime(); QDateTime timeStamp = fileInfo.lastModified(); @@ -3238,11 +3252,18 @@ QDateTime QQmlDataBlob::SourceCodeData::sourceTimeStamp() const bool QQmlDataBlob::SourceCodeData::exists() const { - if (!inlineSourceCode.isEmpty()) + if (hasInlineSourceCode) return true; return fileInfo.exists(); } +bool QQmlDataBlob::SourceCodeData::isEmpty() const +{ + if (hasInlineSourceCode) + return inlineSourceCode.isEmpty(); + return fileInfo.size() == 0; +} + QT_END_NAMESPACE #include "qqmltypeloader.moc" diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 713f707387..5988632547 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -140,11 +140,13 @@ public: QString readAll(QString *error) const; QDateTime sourceTimeStamp() const; bool exists() const; + bool isEmpty() const; private: friend class QQmlDataBlob; friend class QQmlTypeLoader; QString inlineSourceCode; QFileInfo fileInfo; + bool hasInlineSourceCode = false; }; protected: @@ -228,12 +230,16 @@ class QQmlTypeLoaderQmldirContent { private: friend class QQmlTypeLoader; - QQmlTypeLoaderQmldirContent(); void setContent(const QString &location, const QString &content); void setError(const QQmlError &); public: + QQmlTypeLoaderQmldirContent(); + QQmlTypeLoaderQmldirContent(const QQmlTypeLoaderQmldirContent &) = default; + QQmlTypeLoaderQmldirContent &operator=(const QQmlTypeLoaderQmldirContent &) = default; + + bool hasContent() const { return m_hasContent; } bool hasError() const; QList<QQmlError> errors(const QString &uri) const; @@ -250,6 +256,7 @@ public: private: QQmlDirParser m_parser; QString m_location; + bool m_hasContent = false; }; class Q_QML_PRIVATE_EXPORT QQmlTypeLoader @@ -266,6 +273,8 @@ public: const QQmlImports &imports() const { return m_importCache; } + void setCachedUnitStatus(QQmlMetaType::CachedUnitLookupError status) { m_cachedUnitStatus = status; } + protected: bool addImport(const QV4::CompiledData::Import *import, QList<QQmlError> *errors); @@ -288,6 +297,7 @@ public: QQmlImports m_importCache; QHash<const QV4::CompiledData::Import*, int> m_unresolvedImports; QList<QQmlQmldirData *> m_qmldirs; + QQmlMetaType::CachedUnitLookupError m_cachedUnitStatus = QQmlMetaType::CachedUnitLookupError::NoError; }; QQmlTypeLoader(QQmlEngine *); @@ -304,7 +314,7 @@ public: QString absoluteFilePath(const QString &path); bool directoryExists(const QString &path); - const QQmlTypeLoaderQmldirContent *qmldirContent(const QString &filePath); + const QQmlTypeLoaderQmldirContent qmldirContent(const QString &filePath); void setQmldirContent(const QString &filePath, const QString &content); void clearCache(); @@ -313,8 +323,8 @@ public: bool isTypeLoaded(const QUrl &url) const; bool isScriptLoaded(const QUrl &url) const; - void lock(); - void unlock(); + void lock() { m_mutex.lock(); } + void unlock() { m_mutex.unlock(); } void load(QQmlDataBlob *, Mode = PreferSynchronous); void loadWithStaticData(QQmlDataBlob *, const QByteArray &, Mode = PreferSynchronous); @@ -381,6 +391,7 @@ private: QQmlEngine *m_engine; QQmlTypeLoaderThread *m_thread; + QMutex &m_mutex; #if QT_CONFIG(qml_debug) QScopedPointer<QQmlProfiler> m_profiler; diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 6dbf6ad8c1..ce35e966aa 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -388,6 +388,7 @@ ReturnedValue QQmlTypeWrapper::instanceOf(const Object *typeObject, const Value QQmlTypeData *td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl()); CompiledData::CompilationUnit *cu = td->compilationUnit(); myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId); + td->release(); } else { myQmlType = qenginepriv->metaObjectForType(myTypeId); } diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index a28115d192..6196a09d94 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -463,8 +463,12 @@ bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f); - QV4::ScopedContext ctx(scope, bindingFunction->scope()); - QQmlBinding *newBinding = QQmlBinding::create(&cacheData, bindingFunction->function(), referenceObject, context, ctx); + QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction()); + QV4::ScopedContext ctx(scope, f->scope()); + QQmlBinding *newBinding = QQmlBinding::create(&cacheData, f->function(), referenceObject, context, ctx); + newBinding->setSourceLocation(bindingFunction->currentLocation()); + if (f->isBoundFunction()) + newBinding->setBoundFunction(static_cast<QV4::BoundFunction *>(f.getPointer())); newBinding->setSourceLocation(bindingFunction->currentLocation()); newBinding->setTarget(referenceObject, cacheData, pd); QQmlPropertyPrivate::setBinding(newBinding); diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 5673acec89..567d83f3ee 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -1019,7 +1019,7 @@ public: Opened = 1, HeadersReceived = 2, Loading = 3, Done = 4 }; - QQmlXMLHttpRequest(QNetworkAccessManager *manager); + QQmlXMLHttpRequest(QNetworkAccessManager *manager, QV4::ExecutionEngine *v4); virtual ~QQmlXMLHttpRequest(); bool sendFlag() const; @@ -1028,9 +1028,9 @@ public: int replyStatus() const; QString replyStatusText() const; - ReturnedValue open(Object *thisObject, QQmlContextData *context, const QString &, const QUrl &, LoadType); + ReturnedValue open(Object *thisObject, const QString &, const QUrl &, LoadType); ReturnedValue send(Object *thisObject, QQmlContextData *context, const QByteArray &); - ReturnedValue abort(Object *thisObject, QQmlContextData *context); + ReturnedValue abort(Object *thisObject); void addHeader(const QString &, const QString &); QString header(const QString &name) const; @@ -1078,9 +1078,10 @@ private: PersistentValue m_thisObject; QQmlContextDataRef m_qmlContext; + bool m_wasConstructedWithQmlContext = true; - static void dispatchCallback(Object *thisObj, QQmlContextData *context); - void dispatchCallback(); + static void dispatchCallbackNow(Object *thisObj); + void dispatchCallbackSafely(); int m_status; QString m_statusText; @@ -1096,12 +1097,13 @@ private: QV4::PersistentValue m_parsedDocument; }; -QQmlXMLHttpRequest::QQmlXMLHttpRequest(QNetworkAccessManager *manager) +QQmlXMLHttpRequest::QQmlXMLHttpRequest(QNetworkAccessManager *manager, QV4::ExecutionEngine *v4) : m_state(Unsent), m_errorFlag(false), m_sendFlag(false) , m_redirectCount(0), m_gotXml(false), m_textCodec(nullptr), m_network(nullptr), m_nam(manager) , m_responseType() , m_parsedDocument() { + m_wasConstructedWithQmlContext = v4->callingQmlContext() != nullptr; } QQmlXMLHttpRequest::~QQmlXMLHttpRequest() @@ -1134,7 +1136,7 @@ QString QQmlXMLHttpRequest::replyStatusText() const return m_statusText; } -ReturnedValue QQmlXMLHttpRequest::open(Object *thisObject, QQmlContextData *context, const QString &method, const QUrl &url, LoadType loadType) +ReturnedValue QQmlXMLHttpRequest::open(Object *thisObject, const QString &method, const QUrl &url, LoadType loadType) { destroyNetwork(); m_sendFlag = false; @@ -1145,7 +1147,7 @@ ReturnedValue QQmlXMLHttpRequest::open(Object *thisObject, QQmlContextData *cont m_request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, loadType == SynchronousLoad); m_state = Opened; m_addedHeaders.clear(); - dispatchCallback(thisObject, context); + dispatchCallbackNow(thisObject); return Encode::undefined(); } @@ -1297,7 +1299,7 @@ ReturnedValue QQmlXMLHttpRequest::send(Object *thisObject, QQmlContextData *cont return Encode::undefined(); } -ReturnedValue QQmlXMLHttpRequest::abort(Object *thisObject, QQmlContextData *context) +ReturnedValue QQmlXMLHttpRequest::abort(Object *thisObject) { destroyNetwork(); m_responseEntityBody = QByteArray(); @@ -1310,7 +1312,7 @@ ReturnedValue QQmlXMLHttpRequest::abort(Object *thisObject, QQmlContextData *con m_state = Done; m_sendFlag = false; - dispatchCallback(thisObject, context); + dispatchCallbackNow(thisObject); } m_state = Unsent; @@ -1329,7 +1331,7 @@ void QQmlXMLHttpRequest::readyRead() if (m_state < HeadersReceived) { m_state = HeadersReceived; fillHeadersList (); - dispatchCallback(); + dispatchCallbackSafely(); } bool wasEmpty = m_responseEntityBody.isEmpty(); @@ -1337,7 +1339,7 @@ void QQmlXMLHttpRequest::readyRead() if (wasEmpty && !m_responseEntityBody.isEmpty()) m_state = Loading; - dispatchCallback(); + dispatchCallbackSafely(); } static const char *errorToString(QNetworkReply::NetworkError error) @@ -1380,14 +1382,14 @@ void QQmlXMLHttpRequest::error(QNetworkReply::NetworkError error) error == QNetworkReply::ServiceUnavailableError || error == QNetworkReply::UnknownServerError) { m_state = Loading; - dispatchCallback(); + dispatchCallbackSafely(); } else { m_errorFlag = true; m_responseEntityBody = QByteArray(); } m_state = Done; - dispatchCallback(); + dispatchCallbackSafely(); } #define XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION 15 @@ -1419,7 +1421,7 @@ void QQmlXMLHttpRequest::finished() if (m_state < HeadersReceived) { m_state = HeadersReceived; fillHeadersList (); - dispatchCallback(); + dispatchCallbackSafely(); } m_responseEntityBody.append(m_network->readAll()); readEncoding(); @@ -1436,11 +1438,11 @@ void QQmlXMLHttpRequest::finished() destroyNetwork(); if (m_state < Loading) { m_state = Loading; - dispatchCallback(); + dispatchCallbackSafely(); } m_state = Done; - dispatchCallback(); + dispatchCallbackSafely(); m_thisObject.clear(); m_qmlContext.setContextData(nullptr); @@ -1557,17 +1559,10 @@ const QByteArray &QQmlXMLHttpRequest::rawResponseBody() const return m_responseEntityBody; } -void QQmlXMLHttpRequest::dispatchCallback(Object *thisObj, QQmlContextData *context) +void QQmlXMLHttpRequest::dispatchCallbackNow(Object *thisObj) { Q_ASSERT(thisObj); - if (!context) - // if the calling context object is no longer valid, then it has been - // deleted explicitly (e.g., by a Loader deleting the itemContext when - // the source is changed). We do nothing in this case, as the evaluation - // cannot succeed. - return; - QV4::Scope scope(thisObj->engine()); ScopedString s(scope, scope.engine->newString(QStringLiteral("onreadystatechange"))); ScopedFunctionObject callback(scope, thisObj->get(s)); @@ -1585,9 +1580,16 @@ void QQmlXMLHttpRequest::dispatchCallback(Object *thisObj, QQmlContextData *cont } } -void QQmlXMLHttpRequest::dispatchCallback() +void QQmlXMLHttpRequest::dispatchCallbackSafely() { - dispatchCallback(m_thisObject.as<Object>(), m_qmlContext.contextData()); + if (m_wasConstructedWithQmlContext && !m_qmlContext.contextData()) + // if the calling context object is no longer valid, then it has been + // deleted explicitly (e.g., by a Loader deleting the itemContext when + // the source is changed). We do nothing in this case, as the evaluation + // cannot succeed. + return; + + dispatchCallbackNow(m_thisObject.as<Object>()); } void QQmlXMLHttpRequest::destroyNetwork() @@ -1640,7 +1642,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject Scope scope(f->engine()); const QQmlXMLHttpRequestCtor *ctor = static_cast<const QQmlXMLHttpRequestCtor *>(f); - QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager()); + QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager(), scope.engine); Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocObject<QQmlXMLHttpRequestWrapper>(r)); ScopedObject proto(scope, ctor->d()->proto); w->setPrototype(proto); @@ -1778,7 +1780,7 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_open(const FunctionObject *b, const if (!username.isNull()) url.setUserName(username); if (!password.isNull()) url.setPassword(password); - return r->open(w, scope.engine->callingQmlContext(), method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad); + return r->open(w, method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad); } ReturnedValue QQmlXMLHttpRequestCtor::method_setRequestHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) @@ -1860,7 +1862,7 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_abort(const FunctionObject *b, cons V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - return r->abort(w, scope.engine->callingQmlContext()); + return r->abort(w); } ReturnedValue QQmlXMLHttpRequestCtor::method_getResponseHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 1371f1f041..9e7c84011b 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1344,11 +1344,12 @@ ReturnedValue QtObject::method_locale(const FunctionObject *b, const Value *, co return QQmlLocale::locale(scope.engine, code); } -void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *originalFunction) +void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *bindingFunction) { - Scope scope(originalFunction->engine()); - ScopedContext context(scope, originalFunction->scope()); - FunctionObject::init(context, originalFunction->function()); + Scope scope(bindingFunction->engine()); + ScopedContext context(scope, bindingFunction->scope()); + FunctionObject::init(context, bindingFunction->function()); + this->bindingFunction.set(internalClass->engine, bindingFunction->d()); } QQmlSourceLocation QQmlBindingFunction::currentLocation() const diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h index 104dae5d79..ee3b5f7d6e 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h +++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h @@ -80,8 +80,11 @@ struct ConsoleObject : Object { void init(); }; -struct QQmlBindingFunction : FunctionObject { - void init(const QV4::FunctionObject *originalFunction); +#define QQmlBindingFunctionMembers(class, Member) \ + Member(class, Pointer, FunctionObject *, bindingFunction) +DECLARE_HEAP_OBJECT(QQmlBindingFunction, FunctionObject) { + DECLARE_MARKOBJECTS(QQmlBindingFunction) + void init(const QV4::FunctionObject *bindingFunction); }; } @@ -179,6 +182,7 @@ struct QQmlBindingFunction : public QV4::FunctionObject { V4_OBJECT2(QQmlBindingFunction, FunctionObject) + Heap::FunctionObject *bindingFunction() const { return d()->bindingFunction; } QQmlSourceLocation currentLocation() const; // from caller stack trace }; |