diff options
23 files changed, 234 insertions, 97 deletions
diff --git a/.qmake.conf b/.qmake.conf index 62944bc6fb..0fd16ba44d 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,4 +1,4 @@ load(qt_build_config) CONFIG += warning_clean -MODULE_VERSION = 5.9.5 +MODULE_VERSION = 5.9.6 diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp index 0f88ec641b..7057fd0621 100644 --- a/src/imports/statemachine/signaltransition.cpp +++ b/src/imports/statemachine/signaltransition.cpp @@ -176,9 +176,10 @@ void SignalTransition::connectTriggered() QMetaMethod metaMethod = target->metaObject()->method(qobjectSignal->methodIndex()); int signalIndex = QMetaObjectPrivate::signalIndex(metaMethod); - QQmlBoundSignalExpression *expression = ctxtdata ? - new QQmlBoundSignalExpression(target, signalIndex, - ctxtdata, this, m_compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]) : 0; + auto f = m_compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; + QQmlBoundSignalExpression *expression = + ctxtdata ? new QQmlBoundSignalExpression(target, signalIndex, ctxtdata, this, f) + : nullptr; if (expression) expression->setNotifyOnValueChanged(false); m_signalExpression = expression; diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index d2934ba034..507f12cf3a 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -812,7 +812,8 @@ void QQmlEngineDebugServiceImpl::engineAboutToBeRemoved(QJSEngine *engine) void QQmlEngineDebugServiceImpl::objectCreated(QJSEngine *engine, QObject *object) { Q_ASSERT(engine); - Q_ASSERT(m_engines.contains(engine)); + if (!m_engines.contains(engine)) + return; int engineId = QQmlDebugService::idForObject(engine); int objectId = QQmlDebugService::idForObject(object); diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index b07e9f55f5..b609a06382 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -2102,6 +2102,15 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, IR::BasicBlock *exitBlock = function->newBasicBlock(0, IR::Function::DontInsertBlock); function->hasDirectEval = _env->hasDirectEval || _env->compilationMode == EvalCode || _module->debugMode; // Conditional breakpoints are like eval in the function + + // When a user writes the following QML signal binding: + // onSignal: function() { doSomethingUsefull } + // we will generate a binding function that just returns the closure. However, that's not useful + // at all, because if the onSignal is a signal handler, the user is actually making it explicit + // that the binding is a function, so we should execute that. However, we don't know that during + // AOT compilation, so mark the surrounding function as only-returning-a-closure. + function->returnsClosure = cast<ExpressionStatement *>(ast) && cast<FunctionExpression *>(cast<ExpressionStatement *>(ast)->expression); + function->usesArgumentsObject = _env->parent && (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed); function->usesThis = _env->usesThis; function->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 188a571394..b15e4ade60 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -228,6 +228,7 @@ struct Function LEUInt32 nLocals; LEUInt32 localsOffset; LEUInt32 nInnerFunctions; + LEUInt32 nestedFunctionIndex; // for functions that only return a single closure, used in signal handlers Location location; // Qml Extensions Begin @@ -248,6 +249,7 @@ struct Function quint8 flags; quint8 padding1; LEUInt16 padding2; + LEUInt32 padding3; const LEUInt32 *formalsTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + formalsOffset); } const LEUInt32 *localsTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + localsOffset); } @@ -266,7 +268,7 @@ struct Function return (sizeof(Function) + (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies + 2 * nPropertyDependencies) * sizeof(quint32) + 7) & ~0x7; } }; -static_assert(sizeof(Function) == 72, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Function) == 80, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); // Qml data structures diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 0dc40d9698..e11e0bbcdd 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -298,6 +298,9 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i if (irFunction->hasTry || irFunction->hasWith) function->flags |= CompiledData::Function::HasCatchOrWith; function->nFormals = irFunction->formals.size(); + function->nestedFunctionIndex = + irFunction->returnsClosure ? quint32(irModule->functions.indexOf(irFunction->nestedFunctions.first())) + : std::numeric_limits<uint32_t>::max(); function->formalsOffset = currentOffset; currentOffset += function->nFormals * sizeof(quint32); diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index a4038f827a..7741a64667 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -374,6 +374,7 @@ Function::Function(Module *module, Function *outer, const QString &name) , hasTry(false) , hasWith(false) , isQmlBinding(false) + , returnsClosure(false) , unused(0) , line(0) , column(0) diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 317abd09bb..35b6abaa58 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -1313,7 +1313,8 @@ struct Function { uint hasTry: 1; uint hasWith: 1; uint isQmlBinding: 1; - uint unused : 24; + uint returnsClosure: 1; + uint unused : 23; // Location of declaration in source code (0 if not specified) uint line; diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index 0944e6d271..0d950223b0 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -647,7 +647,7 @@ uint ArrayData::append(Object *obj, ArrayObject *otherObj, uint n) uint toCopy = n; uint chunk = toCopy; if (chunk > os->alloc - os->offset) - chunk -= os->alloc - os->offset; + chunk = os->alloc - os->offset; obj->arrayPut(oldSize, os->arrayData + os->offset, chunk); toCopy -= chunk; if (toCopy) diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index b212b3d027..fb8f1400a2 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -50,6 +50,7 @@ // We mean it. // +#include <stdint.h> #include "qv4global_p.h" #include <private/qqmlglobal_p.h> #include <private/qv4compileddata_p.h> @@ -100,6 +101,12 @@ struct Q_QML_EXPORT Function { return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column); } + Function *nestedFunction() const + { + if (compiledFunction->nestedFunctionIndex == std::numeric_limits<uint32_t>::max()) + return nullptr; + return compilationUnit->runtimeFunctions[compiledFunction->nestedFunctionIndex]; + } }; diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 19ece44beb..efe0cccc8d 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 = QQmlEnginePrivate::getV4Engine(ctxt->engine); QList<QByteArray> signalParameters = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).parameterNames(); diff --git a/src/qml/qml/qqmldirparser_p.h b/src/qml/qml/qqmldirparser_p.h index 1530b7a6cf..1297d21fa0 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 f4656bafd2..ac92f8dde8 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -1098,7 +1098,9 @@ QQmlEngine::~QQmlEngine() void QQmlEngine::clearComponentCache() { Q_D(QQmlEngine); + d->typeLoader.lock(); d->typeLoader.clearCache(); + d->typeLoader.unlock(); } /*! diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index f43ffb4c3c..6e467a6d96 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -316,17 +316,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); @@ -664,14 +664,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(); @@ -1060,26 +1060,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; @@ -1090,7 +1090,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) @@ -1100,7 +1100,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()) { @@ -1166,7 +1166,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); } @@ -1179,17 +1179,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); @@ -1313,14 +1313,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) { @@ -1344,7 +1344,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) { @@ -1436,14 +1436,14 @@ bool QQmlImportsPrivate::addLibraryImport(const QString& uri, const QString &pre Q_ASSERT(inserted); if (!incomplete) { - const QQmlTypeLoaderQmldirContent *qmldir = 0; + 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)) @@ -1461,7 +1461,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; @@ -1541,12 +1541,12 @@ bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix Q_ASSERT(inserted); if (!incomplete && !qmldirIdentifier.isEmpty()) { - const QQmlTypeLoaderQmldirContent *qmldir = 0; + 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)) @@ -1565,14 +1565,14 @@ bool QQmlImportsPrivate::updateQmldirContent(const QString &uri, const QString & Q_ASSERT(nameSpace); if (QQmlImportInstance *import = nameSpace->findImport(uri)) { - const QQmlTypeLoaderQmldirContent *qmldir = 0; + 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 9cb5340c68..757179bb44 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -80,7 +80,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/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 5318375af7..afcb9cdbe9 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1379,8 +1379,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); @@ -1427,8 +1427,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); @@ -1593,6 +1593,7 @@ QString QQmlTypeLoaderQmldirContent::typeNamespace() const void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QString &content) { + m_hasContent = true; m_location = location; m_parser.parse(content); } @@ -1815,6 +1816,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> : 0; @@ -1878,6 +1880,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> : 0; @@ -1896,8 +1899,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 @@ -1911,39 +1916,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) diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index ab32bac7b2..71bd5a8a7f 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -228,12 +228,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 +254,7 @@ public: private: QQmlDirParser m_parser; QString m_location; + bool m_hasContent = false; }; class Q_QML_PRIVATE_EXPORT QQmlTypeLoader @@ -304,7 +309,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(); diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index 7607c19374..2554dcf2b6 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -289,9 +289,10 @@ void QQmlConnections::connectSignals() new QQmlBoundSignal(target, signalIndex, this, qmlEngine(this)); signal->setEnabled(d->enabled); - QQmlBoundSignalExpression *expression = ctxtdata ? - new QQmlBoundSignalExpression(target, signalIndex, - ctxtdata, this, d->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]) : 0; + auto f = d->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; + QQmlBoundSignalExpression *expression = + ctxtdata ? new QQmlBoundSignalExpression(target, signalIndex, ctxtdata, this, f) + : nullptr; signal->takeExpression(expression); d->boundsignals += signal; } else { diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 15ade6767c..a1136cc3f0 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1883,6 +1883,11 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) qCDebug(DBG_TOUCH) << event; Q_Q(QQuickWindow); + if (q->mouseGrabberItem()) + q->mouseGrabberItem()->ungrabMouse(); + touchMouseId = -1; + touchMouseDevice = nullptr; + // A TouchCancel event will typically not contain any points. // Deliver it to all items that have active touches. QQuickPointerEvent *pointerEvent = pointerEventInstance(QQuickPointerDevice::touchDevice(event->device())); @@ -1891,10 +1896,6 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) for (QQuickItem *grabber: qAsConst(grabbers)) { q->sendEvent(grabber, event); } - touchMouseId = -1; - touchMouseDevice = nullptr; - if (q->mouseGrabberItem()) - q->mouseGrabberItem()->ungrabMouse(); // The next touch event can only be a TouchBegin so clean up. pointerEvent->clearGrabbers(); diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp index 6d31ff9219..9d1aaa4458 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp @@ -141,6 +141,7 @@ private slots: void queryObjectWithNonStreamableTypes(); void asynchronousCreate(); void invalidContexts(); + void createObjectOnDestruction(); }; QmlDebugObjectReference tst_QQmlEngineDebugService::findRootObject( @@ -1329,6 +1330,29 @@ void tst_QQmlEngineDebugService::invalidContexts() QCOMPARE(m_dbg->rootContext().contexts.count(), 0); } +void tst_QQmlEngineDebugService::createObjectOnDestruction() +{ + QSignalSpy spy(m_dbg, SIGNAL(newObject(int))); + { + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData( + "import QtQml 2.0;" + "QtObject {" + "property Component x:" + "Qt.createQmlObject('import QtQml 2.0; Component { QtObject { } }'," + "this, 'x.qml');" + "Component.onDestruction: x.createObject(this, {});" + "}", QUrl::fromLocalFile("x.qml")); + QVERIFY(component.isReady()); + QVERIFY(component.create()); + QTRY_COMPARE(spy.count(), 2); + } + // Doesn't crash and doesn't give us another signal for the object created on destruction. + QTest::qWait(500); + QCOMPARE(spy.count(), 2); +} + int main(int argc, char *argv[]) { int _argc = argc + 1; diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 8b815f7a06..166f17f9e1 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -128,6 +128,7 @@ private slots: void JSONparse(); void arraySort(); void lookupOnDisappearingProperty(); + void arrayConcat(); void qRegExpInport_data(); void qRegExpInport(); @@ -3018,6 +3019,19 @@ void tst_QJSEngine::lookupOnDisappearingProperty() QVERIFY(func.call(QJSValueList()<< o).isUndefined()); } +void tst_QJSEngine::arrayConcat() +{ + QJSEngine eng; + QJSValue v = eng.evaluate("var x = [1, 2, 3, 4, 5, 6];" + "var y = [];" + "for (var i = 0; i < 5; ++i)" + " x.shift();" + "for (var i = 10; i < 13; ++i)" + " x.push(i);" + "x.toString();"); + QCOMPARE(v.toString(), QString::fromLatin1("6,10,11,12")); +} + static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; } void tst_QJSEngine::qRegExpInport_data() diff --git a/tests/auto/qmltest/statemachine/tst_triggeredArguments2.qml b/tests/auto/qmltest/statemachine/tst_triggeredArguments2.qml index 73bc653404..f23cc14152 100644 --- a/tests/auto/qmltest/statemachine/tst_triggeredArguments2.qml +++ b/tests/auto/qmltest/statemachine/tst_triggeredArguments2.qml @@ -67,7 +67,6 @@ TestCase { // Emit the signalTrans.signal testCase.mysignal("test1", true, 2) - expectFail("", "QTBUG-50328") compare(testCase.mystr, "test1") compare(testCase.mybool, true) compare(testCase.myint, 2) diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index cb710e2c8e..4ddb37f70c 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -142,8 +142,9 @@ class TestTouchItem : public QQuickRectangle public: TestTouchItem(QQuickItem *parent = 0) : QQuickRectangle(parent), acceptTouchEvents(true), acceptMouseEvents(true), - mousePressId(0), - spinLoopWhenPressed(false), touchEventCount(0) + mousePressCount(0), mouseMoveCount(0), + spinLoopWhenPressed(false), touchEventCount(0), + mouseUngrabEventCount(0) { border()->setWidth(1); setAcceptedMouseButtons(Qt::LeftButton); @@ -161,9 +162,11 @@ public: lastMousePos = QPointF(); lastMouseCapabilityFlags = 0; touchEventCount = 0; + mouseMoveCount = 0; + mouseUngrabEventCount = 0; } - static void clearMousePressCounter() + static void clearMouseEventCounters() { mousePressNum = mouseMoveNum = mouseReleaseNum = 0; } @@ -176,9 +179,11 @@ public: bool acceptTouchEvents; bool acceptMouseEvents; TouchEventData lastEvent; - int mousePressId; + int mousePressCount; + int mouseMoveCount; bool spinLoopWhenPressed; int touchEventCount; + int mouseUngrabEventCount; QVector2D lastVelocity; QVector2D lastVelocityFromMouseMove; QPointF lastMousePos; @@ -206,7 +211,7 @@ public: e->ignore(); return; } - mousePressId = ++mousePressNum; + mousePressCount = ++mousePressNum; lastMousePos = e->pos(); lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e); } @@ -216,7 +221,7 @@ public: e->ignore(); return; } - ++mouseMoveNum; + mouseMoveCount = ++mouseMoveNum; lastVelocityFromMouseMove = QGuiApplicationPrivate::mouseEventVelocity(e); lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e); lastMousePos = e->pos(); @@ -232,10 +237,23 @@ public: lastMouseCapabilityFlags = QGuiApplicationPrivate::mouseEventCaps(e); } - bool childMouseEventFilter(QQuickItem *, QEvent *event) { - // TODO Is it a bug if a QTouchEvent comes here? - if (event->type() == QEvent::MouseButtonPress) - mousePressId = ++mousePressNum; + void mouseUngrabEvent() { + ++mouseUngrabEventCount; + } + + bool childMouseEventFilter(QQuickItem *item, QEvent *e) { + qCDebug(lcTests) << objectName() << "filtering" << e << "ahead of delivery to" << item->metaObject()->className() << item->objectName(); + switch (e->type()) { + case QEvent::MouseButtonPress: + mousePressCount = ++mousePressNum; + break; + case QEvent::MouseMove: + mouseMoveCount = ++mouseMoveNum; + break; + default: + break; + } + return false; } @@ -367,6 +385,7 @@ private slots: void touchEvent_propagation(); void touchEvent_propagation_data(); void touchEvent_cancel(); + void touchEvent_cancelClearsMouseGrab(); void touchEvent_reentrant(); void touchEvent_velocity(); @@ -575,7 +594,7 @@ void tst_qquickwindow::constantUpdatesOnWindow() void tst_qquickwindow::touchEvent_basic() { - TestTouchItem::clearMousePressCounter(); + TestTouchItem::clearMouseEventCounters(); QQuickWindow *window = new QQuickWindow; QScopedPointer<QQuickWindow> cleanup(window); @@ -704,7 +723,7 @@ void tst_qquickwindow::touchEvent_basic() void tst_qquickwindow::touchEvent_propagation() { - TestTouchItem::clearMousePressCounter(); + TestTouchItem::clearMouseEventCounters(); QFETCH(bool, acceptTouchEvents); QFETCH(bool, acceptMouseEvents); @@ -848,7 +867,7 @@ void tst_qquickwindow::touchEvent_propagation_data() void tst_qquickwindow::touchEvent_cancel() { - TestTouchItem::clearMousePressCounter(); + TestTouchItem::clearMouseEventCounters(); QQuickWindow *window = new QQuickWindow; QScopedPointer<QQuickWindow> cleanup(window); @@ -880,9 +899,41 @@ void tst_qquickwindow::touchEvent_cancel() delete item; } +void tst_qquickwindow::touchEvent_cancelClearsMouseGrab() +{ + TestTouchItem::clearMouseEventCounters(); + + QQuickWindow *window = new QQuickWindow; + QScopedPointer<QQuickWindow> cleanup(window); + + window->resize(250, 250); + window->setPosition(100, 100); + window->setTitle(QTest::currentTestFunction()); + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window)); + + TestTouchItem *item = new TestTouchItem(window->contentItem()); + item->setPosition(QPointF(50, 50)); + item->setSize(QSizeF(150, 150)); + item->acceptMouseEvents = true; + item->acceptTouchEvents = false; + + QPointF pos(50, 50); + QTest::touchEvent(window, touchDevice).press(0, item->mapToScene(pos).toPoint(), window); + QCoreApplication::processEvents(); + + QTRY_COMPARE(item->mousePressCount, 1); + QTRY_COMPARE(item->mouseUngrabEventCount, 0); + + QWindowSystemInterface::handleTouchCancelEvent(0, touchDevice); + QCoreApplication::processEvents(); + + QTRY_COMPARE(item->mouseUngrabEventCount, 1); +} + void tst_qquickwindow::touchEvent_reentrant() { - TestTouchItem::clearMousePressCounter(); + TestTouchItem::clearMouseEventCounters(); QQuickWindow *window = new QQuickWindow; QScopedPointer<QQuickWindow> cleanup(window); @@ -921,7 +972,7 @@ void tst_qquickwindow::touchEvent_reentrant() void tst_qquickwindow::touchEvent_velocity() { - TestTouchItem::clearMousePressCounter(); + TestTouchItem::clearMouseEventCounters(); QQuickWindow *window = new QQuickWindow; QScopedPointer<QQuickWindow> cleanup(window); @@ -1056,7 +1107,7 @@ void tst_qquickwindow::mouseFromTouch_basic() // should result in sending mouse events generated from the touch // with the new event propagation system. - TestTouchItem::clearMousePressCounter(); + TestTouchItem::clearMouseEventCounters(); QQuickWindow *window = new QQuickWindow; QScopedPointer<QQuickWindow> cleanup(window); window->resize(250, 250); @@ -1196,7 +1247,7 @@ void tst_qquickwindow::clearWindow() void tst_qquickwindow::mouseFiltering() { - TestTouchItem::clearMousePressCounter(); + TestTouchItem::clearMouseEventCounters(); QQuickWindow *window = new QQuickWindow; QScopedPointer<QQuickWindow> cleanup(window); @@ -1229,9 +1280,9 @@ void tst_qquickwindow::mouseFiltering() // 1. middleItem filters event // 2. bottomItem filters event // 3. topItem receives event - QTRY_COMPARE(middleItem->mousePressId, 1); - QTRY_COMPARE(bottomItem->mousePressId, 2); - QTRY_COMPARE(topItem->mousePressId, 3); + QTRY_COMPARE(middleItem->mousePressCount, 1); + QTRY_COMPARE(bottomItem->mousePressCount, 2); + QTRY_COMPARE(topItem->mousePressCount, 3); // clean up mouse press state for the next tests QTest::mouseRelease(window, Qt::LeftButton, 0, pos); |