diff options
author | Liang Qi <liang.qi@qt.io> | 2018-01-19 10:49:56 +0100 |
---|---|---|
committer | Liang Qi <liang.qi@qt.io> | 2018-01-24 09:34:11 +0100 |
commit | 2570b801c74832a3c83a8b56ad0f76812969e190 (patch) | |
tree | c4bd64d8e6b15b507f51f550ba13a0c062528d65 /src/qml | |
parent | 1b96186d1418adcba85fdbfd794da2d2f6ea122d (diff) | |
parent | 706a6647db695cdeb854ef1bf956ded56b498f78 (diff) |
Merge remote-tracking branch 'origin/5.9' into 5.10
Conflicts:
.qmake.conf
src/qml/compiler/qv4codegen.cpp
src/qml/compiler/qv4compileddata_p.h
src/qml/debugger/qqmlprofiler_p.h
src/qml/jsruntime/qv4engine.cpp
src/qml/memory/qv4mm.cpp
src/qml/qml/qqmlcomponent.cpp
src/qml/qml/qqmlobjectcreator.cpp
src/qml/qml/qqmlobjectcreator_p.h
src/qml/types/qqmldelegatemodel.cpp
src/quick/items/qquickitem_p.h
src/quick/items/qquickwindow.cpp
tests/auto/quick/touchmouse/BLACKLIST
tests/benchmarks/qml/holistic/tst_holistic.cpp
Change-Id: I520f349ab4b048dd337d9647113564fc257865c2
Diffstat (limited to 'src/qml')
41 files changed, 504 insertions, 377 deletions
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 57121e5460..184925f167 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1647,8 +1647,10 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding return bindingPtr; } -JSCodeGen::JSCodeGen(const QString &fileName, const QString &sourceCode, QV4::IR::Module *jsModule, QQmlJS::Engine *jsEngine, - QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports, const QV4::Compiler::StringTableGenerator *stringPool) +JSCodeGen::JSCodeGen(const QString &fileName, const QString &finalUrl, const QString &sourceCode, + QV4::IR::Module *jsModule, QQmlJS::Engine *jsEngine, + QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports, + const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames) : QQmlJS::Codegen(/*strict mode*/false) , sourceCode(sourceCode) , jsEngine(jsEngine) @@ -1660,9 +1662,11 @@ JSCodeGen::JSCodeGen(const QString &fileName, const QString &sourceCode, QV4::IR , _scopeObject(0) , _qmlContextTemp(-1) , _importedScriptsTemp(-1) + , m_globalNames(globalNames) { _module = jsModule; _module->setFileName(fileName); + _module->setFinalUrl(finalUrl); _fileNameIsUrl = true; } @@ -2138,6 +2142,9 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int } } + if (m_globalNames.contains(name)) + return _block->GLOBALNAME(name, line, col, /*forceLookup =*/ true); + #else Q_UNUSED(name) #endif // V4_BOOTSTRAP diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index a5b4815745..f4d5901f92 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -608,9 +608,9 @@ struct Q_QML_EXPORT PropertyResolver struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QQmlJS::Codegen { - JSCodeGen(const QString &fileName, const QString &sourceCode, QV4::IR::Module *jsModule, - QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports, - const QV4::Compiler::StringTableGenerator *stringPool); + JSCodeGen(const QString &fileName, const QString &finalUrl, const QString &sourceCode, + QV4::IR::Module *jsModule, QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot, + QQmlTypeNameCache *imports, const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames); struct IdMapping { @@ -645,6 +645,7 @@ private: QQmlPropertyCache *_scopeObject; int _qmlContextTemp; int _importedScriptsTemp; + QSet<QString> m_globalNames; }; struct Q_QML_PRIVATE_EXPORT IRLoader { diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 46f0c46b99..c502d410f1 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -140,7 +140,10 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() sss.scan(); } - QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, typeNameCache, &document->jsGenerator.stringTable); + QmlIR::JSCodeGen v4CodeGenerator(typeData->urlString(), typeData->finalUrlString(), + document->code, &document->jsModule, + &document->jsParserEngine, document->program, + typeNameCache, &document->jsGenerator.stringTable, engine->v8engine()->illegalNames()); QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); if (!jsCodeGen.generateCodeForComponents()) return nullptr; @@ -1100,7 +1103,11 @@ QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolv alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; } else { QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex); - Q_ASSERT(targetCache); + if (!targetCache) { + *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString())); + break; + } + QmlIR::PropertyResolver resolver(targetCache); QQmlPropertyData *targetProperty = resolver.property(property.toString()); diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 5b7a7f9050..06f7cce2c7 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -501,6 +501,7 @@ Codegen::Codegen(bool strict) } void Codegen::generateFromProgram(const QString &fileName, + const QString &finalUrl, const QString &sourceCode, Program *node, QV4::IR::Module *module, @@ -513,6 +514,7 @@ void Codegen::generateFromProgram(const QString &fileName, _variableEnvironment = 0; _module->setFileName(fileName); + _module->setFinalUrl(finalUrl); ScanFunctions scan(this, sourceCode, mode); scan(node); @@ -523,12 +525,14 @@ void Codegen::generateFromProgram(const QString &fileName, } void Codegen::generateFromFunctionExpression(const QString &fileName, + const QString &finalUrl, const QString &sourceCode, AST::FunctionExpression *ast, QV4::IR::Module *module) { _module = module; _module->setFileName(fileName); + _module->setFinalUrl(finalUrl); _variableEnvironment = 0; ScanFunctions scan(this, sourceCode, GlobalCode); diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 31e9cc0f46..f1066b88c5 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -86,15 +86,17 @@ public: }; void generateFromProgram(const QString &fileName, + const QString &finalUrl, const QString &sourceCode, AST::Program *ast, QV4::IR::Module *module, CompilationMode mode = GlobalCode, const QStringList &inheritedLocals = QStringList()); void generateFromFunctionExpression(const QString &fileName, - const QString &sourceCode, - AST::FunctionExpression *ast, - QV4::IR::Module *module); + const QString &finalUrl, + const QString &sourceCode, + AST::FunctionExpression *ast, + QV4::IR::Module *module); protected: enum Format { ex, cx, nx }; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 292f5cde45..cacc3752ee 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -500,6 +500,7 @@ Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) if (jsUnit->sourceFileIndex == quint32(0) || jsUnit->stringAt(jsUnit->sourceFileIndex) != irDocument->jsModule.fileName) { ensureWritableUnit(); jsUnit->sourceFileIndex = stringTable.registerString(irDocument->jsModule.fileName); + jsUnit->finalUrlIndex = stringTable.registerString(irDocument->jsModule.finalUrl); } // Collect signals that have had a change in signature (from onClicked to onClicked(mouse) for example) @@ -744,7 +745,7 @@ static QByteArray ownLibraryChecksum() // the cache files may end up being re-used. To avoid that we also add the checksum of // the QtQml library. Dl_info libInfo; - if (dladdr(reinterpret_cast<const void *>(&ownLibraryChecksum), &libInfo) != 0) { + if (dladdr(reinterpret_cast<void *>(&ownLibraryChecksum), &libInfo) != 0) { QFile library(QFile::decodeName(libInfo.dli_fname)); if (library.open(QIODevice::ReadOnly)) { QCryptographicHash hash(QCryptographicHash::Md5); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index adf6c21cc3..dc18a78681 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -72,7 +72,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x13 +#define QV4_DATA_STRUCTURE_VERSION 0x14 class QIODevice; class QQmlPropertyCache; @@ -704,6 +704,7 @@ struct Unit quint32_le offsetToJSClassTable; qint32_le indexOfRootFunction; quint32_le sourceFileIndex; + quint32_le finalUrlIndex; /* QML specific fields */ quint32_le nImports; @@ -711,6 +712,8 @@ struct Unit quint32_le nObjects; quint32_le offsetToObjects; + quint32_le padding; + const Import *importAt(int idx) const { return reinterpret_cast<const Import*>((reinterpret_cast<const char *>(this)) + offsetToImports + idx * sizeof(Import)); } @@ -775,7 +778,7 @@ struct Unit } }; -static_assert(sizeof(Unit) == 144, "Unit 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(Unit) == 152, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct TypeReference { @@ -885,14 +888,29 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public ExecutionEngine *engine; QQmlEnginePrivate *qmlEngine; // only used in QML environment for composite types, not in plain QJSEngine case. + // url() and fileName() shall be used to load the actual QML/JS code or to show errors or + // warnings about that code. They include any potential URL interceptions and thus represent the + // "physical" location of the code. + // + // finalUrl() and finalUrlString() shall be used to resolve further URLs referred to in the code + // They are _not_ intercepted and thus represent the "logical" name for the code. + QString fileName() const { return data->stringAt(data->sourceFileIndex); } + QString finalUrlString() const { return data->stringAt(data->finalUrlIndex); } QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; } + QUrl finalUrl() const + { + if (m_finalUrl.isNull) + m_finalUrl = QUrl(finalUrlString()); + return m_finalUrl; + } QV4::Lookup *runtimeLookups; QV4::Value *runtimeRegularExpressions; QV4::InternalClass **runtimeClasses; QVector<QV4::Function *> runtimeFunctions; mutable QQmlNullableValue<QUrl> m_url; + mutable QQmlNullableValue<QUrl> m_finalUrl; // QML specific fields QQmlPropertyCacheVector propertyCaches; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index f02ee728c9..fc03320a20 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -212,6 +212,7 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, IR::ExprList *arg QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option) { registerString(irModule->fileName); + registerString(irModule->finalUrl); for (QV4::IR::Function *f : qAsConst(irModule->functions)) { registerString(*f->name); for (int i = 0; i < f->formals.size(); ++i) @@ -429,6 +430,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp } unit.indexOfRootFunction = -1; unit.sourceFileIndex = getStringId(irModule->fileName); + unit.finalUrlIndex = getStringId(irModule->finalUrl); unit.sourceTimeStamp = irModule->sourceTimeStamp.isValid() ? irModule->sourceTimeStamp.toMSecsSinceEpoch() : 0; unit.nImports = 0; unit.offsetToImports = 0; diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index 82f5225f28..c29ffa10c2 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -386,7 +386,7 @@ void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *target) { - if (useFastLookups && func->global) { + if ((useFastLookups || func->forceLookup) && func->global) { Instruction::ConstructGlobalLookup call; call.index = registerGlobalGetterLookup(*func->id); prepareCallArgs(args, call.argc); @@ -491,7 +491,7 @@ void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target void InstructionSelection::getActivationProperty(const IR::Name *name, IR::Expr *target) { - if (useFastLookups && name->global) { + if ((useFastLookups || name->forceLookup) && name->global) { Instruction::GetGlobalLookup load; load.index = registerGlobalGetterLookup(*name->id); load.result = getResultParam(target); @@ -1015,7 +1015,7 @@ void InstructionSelection::visitRet(IR::Ret *s) void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) { - if (useFastLookups && func->global) { + if ((useFastLookups || func->forceLookup) && func->global) { Instruction::CallGlobalLookup call; call.index = registerGlobalGetterLookup(*func->id); prepareCallArgs(args, call.argc); diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index 0b0ed391fb..a4038f827a 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -243,11 +243,12 @@ private: } }; -void Name::initGlobal(const QString *id, quint32 line, quint32 column) +void Name::initGlobal(const QString *id, quint32 line, quint32 column, bool forceLookup) { this->id = id; this->builtin = builtin_invalid; this->global = true; + this->forceLookup = forceLookup; this->qmlSingleton = false; this->freeOfSideEffects = false; this->line = line; @@ -259,6 +260,7 @@ void Name::init(const QString *id, quint32 line, quint32 column) this->id = id; this->builtin = builtin_invalid; this->global = false; + this->forceLookup = false; this->qmlSingleton = false; this->freeOfSideEffects = false; this->line = line; @@ -270,6 +272,7 @@ void Name::init(Builtin builtin, quint32 line, quint32 column) this->id = 0; this->builtin = builtin; this->global = false; + this->forceLookup = false; this->qmlSingleton = false; this->freeOfSideEffects = false; this->line = line; @@ -351,6 +354,11 @@ void Module::setFileName(const QString &name) fileName = name; } +void Module::setFinalUrl(const QString &url) +{ + finalUrl = url; +} + Function::Function(Module *module, Function *outer, const QString &name) : module(module) , pool(&module->pool) diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index fb65eb4d8c..8c314a9635 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -480,6 +480,7 @@ struct Name: Expr { const QString *id; Builtin builtin; bool global : 1; + bool forceLookup : 1; bool qmlSingleton : 1; bool freeOfSideEffects : 1; quint32 line; @@ -487,7 +488,7 @@ struct Name: Expr { Name(): Expr(NameExpr) {} - void initGlobal(const QString *id, quint32 line, quint32 column); + void initGlobal(const QString *id, quint32 line, quint32 column, bool forceLookup = false); void init(const QString *id, quint32 line, quint32 column); void init(Builtin builtin, quint32 line, quint32 column); @@ -952,6 +953,7 @@ struct Q_QML_PRIVATE_EXPORT Module { QVector<Function *> functions; Function *rootFunction; QString fileName; + QString finalUrl; QDateTime sourceTimeStamp; bool isQmlModule; // implies rootFunction is always 0 uint unitFlags; // flags merged into CompiledData::Unit::flags @@ -977,6 +979,7 @@ struct Q_QML_PRIVATE_EXPORT Module { ~Module(); void setFileName(const QString &name); + void setFinalUrl(const QString &url); }; struct BasicBlock { @@ -1139,7 +1142,7 @@ public: Name *NAME(const QString &id, quint32 line, quint32 column); Name *NAME(Name::Builtin builtin, quint32 line, quint32 column); - Name *GLOBALNAME(const QString &id, quint32 line, quint32 column); + Name *GLOBALNAME(const QString &id, quint32 line, quint32 column, bool forceLookup = false); Closure *CLOSURE(int functionInModule); @@ -1607,11 +1610,11 @@ inline Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column) return e; } -inline Name *BasicBlock::GLOBALNAME(const QString &id, quint32 line, quint32 column) +inline Name *BasicBlock::GLOBALNAME(const QString &id, quint32 line, quint32 column, bool forceLookup) { Q_ASSERT(!isRemoved()); Name *e = function->New<Name>(); - e->initGlobal(function->newString(id), line, column); + e->initGlobal(function->newString(id), line, column, forceLookup); return e; } diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index 6dc9859295..f03657508e 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -234,9 +234,10 @@ public: { // Use the QV4::Function as ID, as that is common among different instances of the same // component. QQmlBinding is per instance. - // Add 1 to the ID, to make it different from the IDs the V4 profiler produces. The +1 makes - // the pointer point into the middle of the QV4::Function. Thus it still points to valid - // memory but we cannot accidentally create a duplicate key from another object. + // Add 1 to the ID, to make it different from the IDs the V4 and signal handling profilers + // produce. The +1 makes the pointer point into the middle of the QV4::Function. Thus it + // still points to valid memory but we cannot accidentally create a duplicate key from + // another object. // If there is no function, use a static but valid address: The profiler itself. quintptr locationId = function ? id(function) + 1 : id(this); m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(), @@ -268,7 +269,12 @@ public: void startHandlingSignal(QQmlBoundSignalExpression *expression) { - quintptr locationId(id(expression)); + // Use the QV4::Function as ID, as that is common among different instances of the same + // component. QQmlBoundSignalExpression is per instance. + // Add 2 to the ID, to make it different from the IDs the V4 and binding profilers produce. + // The +2 makes the pointer point into the middle of the QV4::Function. Thus it still points + // to valid memory but we cannot accidentally create a duplicate key from another object. + quintptr locationId(id(expression->function()) + 2); m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(), (1 << RangeStart | 1 << RangeLocation), HandlingSignal, locationId)); diff --git a/src/qml/doc/src/includes/qqmlcomponent.qdoc b/src/qml/doc/src/includes/qqmlcomponent.qdoc new file mode 100644 index 0000000000..0eb60ed743 --- /dev/null +++ b/src/qml/doc/src/includes/qqmlcomponent.qdoc @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [url-note] +Ensure that the URL provided is full and correct, in particular, use +\l QUrl::fromLocalFile() when loading a file from the local filesystem. + +Relative paths will be resolved against the engine's +\l {QQmlEngine::baseUrl}{baseUrl()}, which is the current working directory +unless specified. +//! [url-note] diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc index 207fb53ca0..31650db7c0 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc @@ -449,13 +449,38 @@ right-hand-side of the property declaration must be a valid alias reference: [default] property alias <name>: <alias reference> \endcode -Unlike an ordinary property, an alias can only refer to an object, or the -property of an object, that is within the scope of the \l{QML Object Types} -{type} within which the alias is declared. It cannot contain arbitrary -JavaScript expressions and it cannot refer to objects declared outside of -the scope of its type. Also note the \e {alias reference} is not optional, -unlike the optional default value for an ordinary property; the alias reference -must be provided when the alias is first declared. +Unlike an ordinary property, an alias has the following restrictions: + +\list +\li It can only refer to an object, or the + property of an object, that is within the scope of the \l{QML Object Types} + {type} within which the alias is declared. +\li It cannot contain arbitrary + JavaScript expressions +\li It cannot refer to objects declared outside of + the scope of its type. +\li The \e {alias reference} is not optional, + unlike the optional default value for an ordinary property; the alias reference + must be provided when the alias is first declared. +\li It cannot refer to grouped properties; the following code will not work: + \code + property alias color: rectangle.border.color + + Rectangle { + id: rectangle + } + \endcode + + However, aliases to \l {QML Basic Types}{value type} properties do work: + \code + property alias rectX: object.rectProperty.x + + Item { + id: object + property rect rectProperty + } + \endcode +\endlist For example, below is a \c Button type with a \c buttonText aliased property which is connected to the \c text object of the \l Text child: diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index 7784eb364e..a35b12d51e 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -168,7 +168,7 @@ void InstructionSelection<JITAssembler>::callBuiltinInvalid(IR::Name *func, IR:: { prepareCallData(args, 0); - if (useFastLookups && func->global) { + if ((useFastLookups || func->forceLookup) && func->global) { uint index = registerGlobalGetterLookup(*func->id); generateRuntimeCall(_as, result, callGlobalLookup, JITTargetPlatform::EngineRegister, @@ -520,7 +520,7 @@ void InstructionSelection<JITAssembler>::loadRegexp(IR::RegExp *sourceRegexp, IR template <typename JITAssembler> void InstructionSelection<JITAssembler>::getActivationProperty(const IR::Name *name, IR::Expr *target) { - if (useFastLookups && name->global) { + if ((useFastLookups || name->forceLookup) && name->global) { uint index = registerGlobalGetterLookup(*name->id); generateLookupCall(target, index, offsetof(QV4::Lookup, globalGetter), JITTargetPlatform::EngineRegister, JITAssembler::Void); return; @@ -1136,7 +1136,7 @@ void InstructionSelection<JITAssembler>::constructActivationProperty(IR::Name *f Q_ASSERT(func != 0); prepareCallData(args, 0); - if (useFastLookups && func->global) { + if ((useFastLookups || func->forceLookup) && func->global) { uint index = registerGlobalGetterLookup(*func->id); generateRuntimeCall(_as, result, constructGlobalLookup, JITTargetPlatform::EngineRegister, diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 7b298a302c..f19f134c53 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -219,6 +219,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) internalClasses[Class_SimpleArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable()); internalClasses[Class_SparseArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable()); internalClasses[Class_ExecutionContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable()); + internalClasses[EngineBase::Class_QmlContext] = internalClasses[EngineBase::Class_ExecutionContext]->changeVTable(QV4::QmlContext::staticVTable()); internalClasses[Class_SimpleCallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable()); jsStrings[String_Empty] = newIdentifier(QString()); @@ -261,6 +262,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) InternalClass *ic = internalClasses[Class_Empty]->changeVTable(QV4::Object::staticVTable()); jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic); internalClasses[Class_Object] = ic->changePrototype(objectPrototype()->d()); + internalClasses[EngineBase::Class_QmlContextWrapper] = internalClasses[Class_Object]->changeVTable(QV4::QQmlContextWrapper::staticVTable()); ic = newInternalClass(ArrayPrototype::staticVTable(), objectPrototype()); Q_ASSERT(ic->prototype); @@ -912,14 +914,14 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file) while (c) { SimpleCallContext *callCtx = c->asSimpleCallContext(); if (callCtx && callCtx->d()->v4Function) { - base.setUrl(callCtx->d()->v4Function->sourceFile()); + base = callCtx->d()->v4Function->finalUrl(); break; } c = parentContext(c); } if (base.isEmpty() && globalCode) - base.setUrl(globalCode->sourceFile()); + base = globalCode->finalUrl(); if (base.isEmpty()) return src; diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index 88f9dfd85c..8258d644ff 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -106,6 +106,8 @@ struct EngineBase { Class_ErrorObject, Class_ErrorObjectWithMessage, Class_ErrorProto, + Class_QmlContextWrapper, + Class_QmlContext, NClasses }; InternalClass *internalClasses[NClasses]; diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index b11c8af94a..8b2d14a0cf 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -83,6 +83,7 @@ struct Q_QML_EXPORT Function { return compilationUnit->runtimeStrings[compiledFunction->nameIndex]; } inline QString sourceFile() const { return compilationUnit->fileName(); } + inline QUrl finalUrl() const { return compilationUnit->finalUrl(); } inline bool usesArgumentsObject() const { return compiledFunction->flags & CompiledData::Function::UsesArgumentsObject; } inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; } diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 45fdde98f7..2375aae92b 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -222,7 +222,7 @@ void FunctionCtor::construct(const Managed *that, Scope &scope, CallData *callDa IR::Module module(scope.engine->debugger() != 0); QQmlJS::RuntimeCodegen cg(scope.engine, f->strictMode()); - cg.generateFromFunctionExpression(QString(), function, fe, &module); + cg.generateFromFunctionExpression(QString(), QString(), function, fe, &module); Compiler::JSUnitGenerator jsGenerator(&module); QScopedPointer<EvalInstructionSelection> isel(scope.engine->iselFactory->create(QQmlEnginePrivate::get(scope.engine), scope.engine->executableAllocator, &module, &jsGenerator)); diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 8769519a59..5cddf2eaa6 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -95,7 +95,7 @@ inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } && (defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD)) # define V4_ENABLE_JIT #elif defined(Q_PROCESSOR_X86_64) && (QT_POINTER_SIZE == 8) \ - && (defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)) + && (defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)) # define V4_ENABLE_JIT #elif defined(Q_PROCESSOR_ARM_32) && (QT_POINTER_SIZE == 4) # if defined(thumb2) || defined(__thumb2__) || ((defined(__thumb) || defined(__thumb__)) && __TARGET_ARCH_THUMB-0 == 4) @@ -104,7 +104,7 @@ inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } # define V4_ENABLE_JIT # endif #elif defined(Q_PROCESSOR_ARM_64) && (QT_POINTER_SIZE == 8) -# if defined(Q_OS_LINUX) +# if defined(Q_OS_LINUX) || defined(Q_OS_QNX) # define V4_ENABLE_JIT # endif #elif defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX) diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index 48c9ee2c36..65bf4c60ce 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -91,6 +91,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object { V4_OBJECT2(QQmlContextWrapper, Object) V4_NEEDS_DESTROY + V4_INTERNALCLASS(QmlContextWrapper) inline QObject *getScopeObject() const { return d()->scopeObject; } inline QQmlContextData *getContext() const { return *d()->context; } @@ -104,6 +105,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object struct Q_QML_EXPORT QmlContext : public ExecutionContext { V4_MANAGED(QmlContext, ExecutionContext) + V4_INTERNALCLASS(QmlContext) static Heap::QmlContext *createWorkerContext(QV4::ExecutionContext *parent, const QUrl &source, Value *sendFunction); static Heap::QmlContext *create(QV4::ExecutionContext *parent, QQmlContextData *context, QObject *scopeObject); diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index fb3aa47d1b..51a957acc9 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -714,6 +714,10 @@ bool QObjectWrapper::put(Managed *m, String *name, const Value &value) PropertyAttributes QObjectWrapper::query(const Managed *m, String *name) { const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m); + const QObject *thatObject = that->d()->object(); + if (QQmlData::wasDeleted(thatObject)) + return QV4::Object::query(m, name); + ExecutionEngine *engine = that->engine(); QQmlContextData *qmlContext = engine->callingQmlContext(); QQmlPropertyData local; @@ -2083,10 +2087,10 @@ ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine for (int i = 0; i < numberOfConstructors; i++) { const QQmlPropertyData & attempt = d()->constructors[i]; + QQmlMetaObject::ArgTypeStorage storage; int methodArgumentCount = 0; int *methodArgTypes = 0; if (attempt.hasArguments()) { - QQmlMetaObject::ArgTypeStorage storage; int *args = object.constructorParameterTypes(attempt.coreIndex(), &storage, 0); if (!args) // Must be an unknown argument continue; diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 62145f36cc..9d1d5e2589 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -127,7 +127,8 @@ void Script::parse() } RuntimeCodegen cg(v4, strictMode); - cg.generateFromProgram(sourceFile, sourceCode, program, &module, QQmlJS::Codegen::EvalCode, inheritedLocals); + cg.generateFromProgram(sourceFile, sourceFile, sourceCode, program, &module, + QQmlJS::Codegen::EvalCode, inheritedLocals); if (v4->hasException) return; @@ -186,7 +187,10 @@ Function *Script::function() return vmFunction; } -QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, ExecutionEngine *engine, const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors, QQmlJS::Directives *directivesCollector) +QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile( + IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, ExecutionEngine *engine, + const QString &fileName, const QString &finalUrl, const QString &source, + QList<QQmlError> *reportedErrors, QQmlJS::Directives *directivesCollector) { using namespace QQmlJS; using namespace QQmlJS::AST; @@ -205,12 +209,12 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(IR::Module const auto diagnosticMessages = parser.diagnosticMessages(); for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { if (m.isWarning()) { - qWarning("%s:%d : %s", qPrintable(url.toString()), m.loc.startLine, qPrintable(m.message)); + qWarning("%s:%d : %s", qPrintable(fileName), m.loc.startLine, qPrintable(m.message)); continue; } QQmlError error; - error.setUrl(url); + error.setUrl(QUrl(fileName)); error.setDescription(m.message); error.setLine(m.loc.startLine); error.setColumn(m.loc.startColumn); @@ -231,7 +235,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(IR::Module } QQmlJS::Codegen cg(/*strict mode*/false); - cg.generateFromProgram(url.toString(), source, program, module, QQmlJS::Codegen::EvalCode); + cg.generateFromProgram(fileName, finalUrl, source, program, module, QQmlJS::Codegen::EvalCode); errors = cg.qmlErrors(); if (!errors.isEmpty()) { if (reportedErrors) diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index 4ebe2dd609..55a349b5fc 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -139,8 +139,10 @@ struct Q_QML_EXPORT Script { Function *function(); - static QQmlRefPointer<CompiledData::CompilationUnit> precompile(IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, ExecutionEngine *engine, const QUrl &url, const QString &source, - QList<QQmlError> *reportedErrors = 0, QQmlJS::Directives *directivesCollector = 0); + static QQmlRefPointer<CompiledData::CompilationUnit> precompile( + IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, ExecutionEngine *engine, + const QString &fileName, const QString &finalUrl, const QString &source, + QList<QQmlError> *reportedErrors = 0, QQmlJS::Directives *directivesCollector = 0); static ReturnedValue evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext); }; diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 8821d3c921..27b3c756c5 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -44,6 +44,7 @@ #include "qv4qobjectwrapper_p.h" #include <QtCore/qalgorithms.h> #include <QtCore/private/qnumeric_p.h> +#include <QtCore/qloggingcategory.h> #include <qqmlengine.h> #include "PageReservation.h" #include "PageAllocation.h" @@ -91,6 +92,11 @@ #define MIN_UNMANAGED_HEAPSIZE_GC_LIMIT std::size_t(128 * 1024) +Q_LOGGING_CATEGORY(lcGcStats, "qt.qml.gc.statistics") +Q_DECLARE_LOGGING_CATEGORY(lcGcStats) +Q_LOGGING_CATEGORY(lcGcAllocatorStats, "qt.qml.gc.allocatorStats") +Q_DECLARE_LOGGING_CATEGORY(lcGcAllocatorStats) + using namespace WTF; QT_BEGIN_NAMESPACE @@ -607,9 +613,9 @@ template struct StackAllocator<Heap::CallContext>; HeapItem *BlockAllocator::allocate(size_t size, bool forceAllocation) { Q_ASSERT((size % Chunk::SlotSize) == 0); size_t slotsRequired = size >> Chunk::SlotSizeShift; -#if MM_DEBUG - ++allocations[bin]; -#endif + + if (allocationStats) + ++allocationStats[binForSlots(slotsRequired)]; HeapItem **last; @@ -751,37 +757,6 @@ void BlockAllocator::collectGrayItems(MarkStack *markStack) } -#if MM_DEBUG -void BlockAllocator::stats() { - DEBUG << "MM stats:"; - QString s; - for (int i = 0; i < 10; ++i) { - uint c = 0; - HeapItem *item = freeBins[i]; - while (item) { - ++c; - item = item->freeData.next; - } - s += QString::number(c) + QLatin1String(", "); - } - HeapItem *item = freeBins[NumBins - 1]; - uint c = 0; - while (item) { - ++c; - item = item->freeData.next; - } - s += QLatin1String("..., ") + QString::number(c); - DEBUG << "bins:" << s; - QString a; - for (int i = 0; i < 10; ++i) - a += QString::number(allocations[i]) + QLatin1String(", "); - a += QLatin1String("..., ") + QString::number(allocations[NumBins - 1]); - DEBUG << "allocs:" << a; - memset(allocations, 0, sizeof(allocations)); -} -#endif - - HeapItem *HugeItemAllocator::allocate(size_t size) { Chunk *c = chunkAllocator->allocate(size); chunks.push_back(HugeChunk{c, size}); @@ -858,11 +833,15 @@ MemoryManager::MemoryManager(ExecutionEngine *engine) , m_weakValues(new PersistentValueStorage(engine)) , unmanagedHeapSizeGCLimit(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT) , aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC")) - , gcStats(!qEnvironmentVariableIsEmpty(QV4_MM_STATS)) + , gcStats(lcGcStats().isDebugEnabled()) + , gcCollectorStats(lcGcAllocatorStats().isDebugEnabled()) { #ifdef V4_USE_VALGRIND VALGRIND_CREATE_MEMPOOL(this, 0, true); #endif + memset(statistics.allocations, 0, sizeof(statistics.allocations)); + if (gcStats) + blockAllocator.allocationStats = statistics.allocations; } #ifdef MM_STATS @@ -922,9 +901,6 @@ Heap::Base *MemoryManager::allocData(std::size_t size) runGC(); didRunGC = true; } -#ifdef DETAILED_MM_STATS - willAllocate(size); -#endif // DETAILED_MM_STATS Q_ASSERT(size >= Chunk::SlotSize); Q_ASSERT(size % Chunk::SlotSize == 0); @@ -1125,9 +1101,10 @@ bool MemoryManager::shouldRunGC() const size_t dumpBins(BlockAllocator *b, bool printOutput = true) { + const QLoggingCategory &stats = lcGcAllocatorStats(); size_t totalSlotMem = 0; if (printOutput) - qDebug() << "Slot map:"; + qDebug(stats) << "Slot map:"; for (uint i = 0; i < BlockAllocator::NumBins; ++i) { uint nEntries = 0; HeapItem *h = b->freeBins[i]; @@ -1137,7 +1114,7 @@ size_t dumpBins(BlockAllocator *b, bool printOutput = true) h = h->freeData.next; } if (printOutput) - qDebug() << " number of entries in slot" << i << ":" << nEntries; + qDebug(stats) << " number of entries in slot" << i << ":" << nEntries; } SDUMP() << " large slot map"; HeapItem *h = b->freeBins[BlockAllocator::NumBins - 1]; @@ -1147,7 +1124,7 @@ size_t dumpBins(BlockAllocator *b, bool printOutput = true) } if (printOutput) - qDebug() << " total mem in bins" << totalSlotMem*Chunk::SlotSize; + qDebug(stats) << " total mem in bins" << totalSlotMem*Chunk::SlotSize; return totalSlotMem*Chunk::SlotSize; } @@ -1161,27 +1138,32 @@ void MemoryManager::runGC() QScopedValueRollback<bool> gcBlocker(gcBlocked, true); // qDebug() << "runGC"; - if (!gcStats) { -// uint oldUsed = allocator.usedMem(); + if (gcStats) { + statistics.maxReservedMem = qMax(statistics.maxReservedMem, getAllocatedMem()); + statistics.maxAllocatedMem = qMax(statistics.maxAllocatedMem, getUsedMem() + getLargeItemsMem()); + } + + if (!gcCollectorStats) { mark(); sweep(); -// DEBUG << "RUN GC: allocated:" << allocator.allocatedMem() << "used before" << oldUsed << "used now" << allocator.usedMem(); } else { bool triggeredByUnmanagedHeap = (unmanagedHeapSize > unmanagedHeapSizeGCLimit); size_t oldUnmanagedSize = unmanagedHeapSize; + const size_t totalMem = getAllocatedMem(); const size_t usedBefore = getUsedMem(); const size_t largeItemsBefore = getLargeItemsMem(); - qDebug() << "========== GC =========="; + const QLoggingCategory &stats = lcGcAllocatorStats(); + qDebug(stats) << "========== GC =========="; #ifdef MM_STATS - qDebug() << " Triggered by alloc request of" << lastAllocRequestedSlots << "slots."; - qDebug() << " Allocations since last GC" << allocationCount; + qDebug(stats) << " Triggered by alloc request of" << lastAllocRequestedSlots << "slots."; + qDebug(stats) << " Allocations since last GC" << allocationCount; allocationCount = 0; #endif size_t oldChunks = blockAllocator.chunks.size(); - qDebug() << "Allocated" << totalMem << "bytes in" << oldChunks << "chunks"; - qDebug() << "Fragmented memory before GC" << (totalMem - usedBefore); + qDebug(stats) << "Allocated" << totalMem << "bytes in" << oldChunks << "chunks"; + qDebug(stats) << "Fragmented memory before GC" << (totalMem - usedBefore); dumpBins(&blockAllocator); #ifdef MM_STATS @@ -1199,15 +1181,15 @@ void MemoryManager::runGC() qint64 sweepTime = t.nsecsElapsed()/1000; if (triggeredByUnmanagedHeap) { - qDebug() << "triggered by unmanaged heap:"; - qDebug() << " old unmanaged heap size:" << oldUnmanagedSize; - qDebug() << " new unmanaged heap:" << unmanagedHeapSize; - qDebug() << " unmanaged heap limit:" << unmanagedHeapSizeGCLimit; + qDebug(stats) << "triggered by unmanaged heap:"; + qDebug(stats) << " old unmanaged heap size:" << oldUnmanagedSize; + qDebug(stats) << " new unmanaged heap:" << unmanagedHeapSize; + qDebug(stats) << " unmanaged heap limit:" << unmanagedHeapSizeGCLimit; } size_t memInBins = dumpBins(&blockAllocator); - qDebug() << "Marked object in" << markTime << "us."; - qDebug() << " " << markStackSize << "objects marked"; - qDebug() << "Sweeped object in" << sweepTime << "us."; + qDebug(stats) << "Marked object in" << markTime << "us."; + qDebug(stats) << " " << markStackSize << "objects marked"; + qDebug(stats) << "Sweeped object in" << sweepTime << "us."; // sort our object types by number of freed instances MMStatsHash freedObjectStats; @@ -1222,26 +1204,29 @@ void MemoryManager::runGC() return a.second > b.second && strcmp(a.first, b.first) < 0; }); - qDebug() << "Used memory before GC:" << usedBefore; - qDebug() << "Used memory after GC:" << usedAfter; - qDebug() << "Freed up bytes :" << (usedBefore - usedAfter); - qDebug() << "Freed up chunks :" << (oldChunks - blockAllocator.chunks.size()); + qDebug(stats) << "Used memory before GC:" << usedBefore; + qDebug(stats) << "Used memory after GC:" << usedAfter; + qDebug(stats) << "Freed up bytes :" << (usedBefore - usedAfter); + qDebug(stats) << "Freed up chunks :" << (oldChunks - blockAllocator.chunks.size()); size_t lost = blockAllocator.allocatedMem() - memInBins - usedAfter; if (lost) - qDebug() << "!!!!!!!!!!!!!!!!!!!!! LOST MEM:" << lost << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"; + qDebug(stats) << "!!!!!!!!!!!!!!!!!!!!! LOST MEM:" << lost << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"; if (largeItemsBefore || largeItemsAfter) { - qDebug() << "Large item memory before GC:" << largeItemsBefore; - qDebug() << "Large item memory after GC:" << largeItemsAfter; - qDebug() << "Large item memory freed up:" << (largeItemsBefore - largeItemsAfter); + qDebug(stats) << "Large item memory before GC:" << largeItemsBefore; + qDebug(stats) << "Large item memory after GC:" << largeItemsAfter; + qDebug(stats) << "Large item memory freed up:" << (largeItemsBefore - largeItemsAfter); } for (auto it = freedObjectsSorted.cbegin(); it != freedObjectsSorted.cend(); ++it) { - qDebug().noquote() << QString::fromLatin1("Freed JS type: %1 (%2 instances)").arg(QString::fromLatin1(it->first), QString::number(it->second)); + qDebug(stats).noquote() << QString::fromLatin1("Freed JS type: %1 (%2 instances)").arg(QString::fromLatin1(it->first), QString::number(it->second)); } - qDebug() << "======== End GC ========"; + qDebug(stats) << "======== End GC ========"; } + if (gcStats) + statistics.maxUsedMem = qMax(statistics.maxUsedMem, getUsedMem() + getLargeItemsMem()); + if (aggressiveGC) { // ensure we don't 'loose' any memory Q_ASSERT(blockAllocator.allocatedMem() == getUsedMem() + dumpBins(&blockAllocator, false)); @@ -1273,6 +1258,8 @@ MemoryManager::~MemoryManager() { delete m_persistentValues; + dumpStats(); + sweep(/*lastSweep*/true); blockAllocator.freeAll(); hugeItemAllocator.freeAll(); @@ -1288,30 +1275,20 @@ MemoryManager::~MemoryManager() void MemoryManager::dumpStats() const { -#ifdef DETAILED_MM_STATS - std::cerr << "=================" << std::endl; - std::cerr << "Allocation stats:" << std::endl; - std::cerr << "Requests for each chunk size:" << std::endl; - for (int i = 0; i < allocSizeCounters.size(); ++i) { - if (unsigned count = allocSizeCounters[i]) { - std::cerr << "\t" << (i << 4) << " bytes chunks: " << count << std::endl; - } - } -#endif // DETAILED_MM_STATS -} + if (!gcStats) + return; -#ifdef DETAILED_MM_STATS -void MemoryManager::willAllocate(std::size_t size) -{ - unsigned alignedSize = (size + 15) >> 4; - QVector<unsigned> &counters = allocSizeCounters; - if ((unsigned) counters.size() < alignedSize + 1) - counters.resize(alignedSize + 1); - counters[alignedSize]++; + const QLoggingCategory &stats = lcGcStats(); + qDebug(stats) << "Qml GC memory allocation statistics:"; + qDebug(stats) << "Total memory allocated:" << statistics.maxReservedMem; + qDebug(stats) << "Max memory used before a GC run:" << statistics.maxAllocatedMem; + qDebug(stats) << "Max memory used after a GC run:" << statistics.maxUsedMem; + qDebug(stats) << "Requests for different item sizes:"; + for (int i = 1; i < BlockAllocator::NumBins - 1; ++i) + qDebug(stats) << " <" << (i << Chunk::SlotSizeShift) << " bytes: " << statistics.allocations[i]; + qDebug(stats) << " >=" << ((BlockAllocator::NumBins - 1) << Chunk::SlotSizeShift) << " bytes: " << statistics.allocations[BlockAllocator::NumBins - 1]; } -#endif // DETAILED_MM_STATS - void MemoryManager::collectFromJSStack(MarkStack *markStack) const { Value *v = engine->jsStackBase; diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index e16a0ca544..fb21481d5b 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -58,8 +58,6 @@ #include <private/qv4mmdefs_p.h> #include <QVector> -//#define DETAILED_MM_STATS - #define QV4_MM_MAXBLOCK_SHIFT "QV4_MM_MAXBLOCK_SHIFT" #define QV4_MM_MAX_CHUNK_SIZE "QV4_MM_MAX_CHUNK_SIZE" #define QV4_MM_STATS "QV4_MM_STATS" @@ -122,9 +120,6 @@ struct BlockAllocator { : chunkAllocator(chunkAllocator), engine(engine) { memset(freeBins, 0, sizeof(freeBins)); -#if MM_DEBUG - memset(allocations, 0, sizeof(allocations)); -#endif } enum { NumBins = 8 }; @@ -133,10 +128,6 @@ struct BlockAllocator { return nSlots >= NumBins ? NumBins - 1 : nSlots; } -#if MM_DEBUG - void stats(); -#endif - HeapItem *allocate(size_t size, bool forceAllocation = false); size_t totalSlots() const { @@ -166,9 +157,7 @@ struct BlockAllocator { ChunkAllocator *chunkAllocator; ExecutionEngine *engine; std::vector<Chunk *> chunks; -#if MM_DEBUG - uint allocations[NumBins]; -#endif + uint *allocationStats = nullptr; }; struct HugeItemAllocator { @@ -453,10 +442,6 @@ protected: Heap::Base *allocData(std::size_t size); Heap::Object *allocObjectWithMemberData(const QV4::VTable *vtable, uint nMembers); -#ifdef DETAILED_MM_STATS - void willAllocate(std::size_t size); -#endif // DETAILED_MM_STATS - private: void collectFromJSStack(MarkStack *markStack) const; void mark(); @@ -481,6 +466,14 @@ public: bool gcBlocked = false; bool aggressiveGC = false; bool gcStats = false; + bool gcCollectorStats = false; + + struct { + size_t maxReservedMem = 0; + size_t maxAllocatedMem = 0; + size_t maxUsedMem = 0; + uint allocations[BlockAllocator::NumBins]; + } statistics; }; } diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 8ec8669c60..e895f1df1b 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -496,8 +496,7 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, QObject *parent) Create a QQmlComponent from the given \a url and give it the specified \a parent and \a engine. - Ensure that the URL provided is full and correct, in particular, use - \l QUrl::fromLocalFile() when loading a file from the local filesystem. + \include qqmlcomponent.qdoc url-note \sa loadUrl() */ @@ -511,8 +510,7 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *paren specified \a parent and \a engine. If \a mode is \l Asynchronous, the component will be loaded and compiled asynchronously. - Ensure that the URL provided is full and correct, in particular, use - \l QUrl::fromLocalFile() when loading a file from the local filesystem. + \include qqmlcomponent.qdoc url-note \sa loadUrl() */ @@ -548,7 +546,7 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName, : QQmlComponent(engine, parent) { Q_D(QQmlComponent); - const QUrl url = QDir::isAbsolutePath(fileName) ? QUrl::fromLocalFile(fileName) : d->engine->baseUrl().resolved(QUrl(fileName)); + const QUrl url = QDir::isAbsolutePath(fileName) ? QUrl::fromLocalFile(fileName) : QUrl(fileName); d->loadUrl(url, mode); } @@ -561,7 +559,7 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::CompiledData::CompilationU Q_D(QQmlComponent); d->compilationUnit = compilationUnit; d->start = start; - d->url = compilationUnit->url(); + d->url = compilationUnit->finalUrl(); d->progress = 1.0; } @@ -608,8 +606,7 @@ QQmlContext *QQmlComponent::creationContext() const /*! Load the QQmlComponent from the provided \a url. - Ensure that the URL provided is full and correct, in particular, use - \l QUrl::fromLocalFile() when loading a file from the local filesystem. + \include qqmlcomponent.qdoc url-note */ void QQmlComponent::loadUrl(const QUrl &url) { @@ -621,8 +618,7 @@ void QQmlComponent::loadUrl(const QUrl &url) Load the QQmlComponent from the provided \a url. If \a mode is \l Asynchronous, the component will be loaded and compiled asynchronously. - Ensure that the URL provided is full and correct, in particular, use - \l QUrl::fromLocalFile() when loading a file from the local filesystem. + \include qqmlcomponent.qdoc url-note */ void QQmlComponent::loadUrl(const QUrl &url, QQmlComponent::CompilationMode mode) { @@ -635,11 +631,21 @@ void QQmlComponentPrivate::loadUrl(const QUrl &newUrl, QQmlComponent::Compilatio Q_Q(QQmlComponent); clear(); - if ((newUrl.isRelative() && !newUrl.isEmpty()) - || newUrl.scheme() == QLatin1String("file")) // Workaround QTBUG-11929 - url = engine->baseUrl().resolved(newUrl); - else + if (newUrl.isRelative()) { + // The new URL is a relative URL like QUrl("main.qml"). + url = engine->baseUrl().resolved(QUrl(newUrl.toString())); + } else if (engine->baseUrl().isLocalFile() && newUrl.isLocalFile() && !QDir::isAbsolutePath(newUrl.toLocalFile())) { + // The new URL is a file on disk but it's a relative path; e.g.: + // QUrl::fromLocalFile("main.qml") or QUrl("file:main.qml") + // We need to remove the scheme so that it becomes a relative URL with a relative path: + QUrl fixedUrl(newUrl); + fixedUrl.setScheme(QString()); + // Then, turn it into an absolute URL with an absolute path by resolving it against the engine's baseUrl(). + // This is a compatibility hack for QTBUG-58837. + url = engine->baseUrl().resolved(fixedUrl); + } else { url = newUrl; + } if (newUrl.isEmpty()) { QQmlError error; @@ -1073,7 +1079,6 @@ void QQmlComponentPrivate::incubateObject( QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(component); incubatorPriv->compilationUnit = componentPriv->compilationUnit; - incubatorPriv->compilationUnit->addref(); incubatorPriv->enginePriv = enginePriv; incubatorPriv->creator.reset(new QQmlObjectCreator(context, componentPriv->compilationUnit, componentPriv->creationContext)); incubatorPriv->subComponentToCreate = componentPriv->start; diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index 37cb328b36..59e2c83a63 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -845,14 +845,14 @@ QV4::IdentifierHash<int> &QQmlContextData::detachedPropertyNames() QUrl QQmlContextData::url() const { if (typeCompilationUnit) - return typeCompilationUnit->url(); + return typeCompilationUnit->finalUrl(); return baseUrl; } QString QQmlContextData::urlString() const { if (typeCompilationUnit) - return typeCompilationUnit->fileName(); + return typeCompilationUnit->finalUrlString(); return baseUrlString; } diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index 63994a392d..5794e6f0c5 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -156,18 +156,21 @@ public: quint32 hasInterceptorMetaObject:1; quint32 hasVMEMetaObject:1; quint32 parentFrozen:1; - quint32 dummy:22; + quint32 dummy:6; // When bindingBitsSize < sizeof(ptr), we store the binding bit flags inside // bindingBitsValue. When we need more than sizeof(ptr) bits, we allocated // sufficient space and use bindingBits to point to it. - int bindingBitsSize; + quint32 bindingBitsArraySize : 16; typedef quintptr BindingBitsType; + enum { + BitsPerType = sizeof(BindingBitsType) * 8, + InlineBindingArraySize = 2 + }; union { BindingBitsType *bindingBits; - BindingBitsType bindingBitsValue; + BindingBitsType bindingBitsValue[InlineBindingArraySize]; }; - enum { MaxInlineBits = sizeof(BindingBitsType) * 8 }; struct NotifyList { quint64 connectionMask; @@ -259,7 +262,7 @@ public: bool hasExtendedData() const { return extendedData != 0; } QHash<int, QObject *> *attachedProperties() const; - static inline bool wasDeleted(QObject *); + static inline bool wasDeleted(const QObject *); static void markAsDeleted(QObject *); static void setQueuedForDeletion(QObject *); @@ -275,6 +278,9 @@ public: return createPropertyCache(engine, object); } + Q_ALWAYS_INLINE static uint offsetForBit(int bit) { return static_cast<uint>(bit) / BitsPerType; } + Q_ALWAYS_INLINE static BindingBitsType bitFlagForBit(int bit) { return BindingBitsType(1) << (static_cast<uint>(bit) & (BitsPerType - 1)); } + private: // For attachedProperties mutable QQmlDataExtended *extendedData; @@ -286,26 +292,25 @@ private: Q_ALWAYS_INLINE bool hasBitSet(int bit) const { - if (bindingBitsSize <= bit) + uint offset = offsetForBit(bit); + if (bindingBitsArraySize <= offset) return false; - if (bindingBitsSize == MaxInlineBits) - return bindingBitsValue & (BindingBitsType(1) << bit); - else - return bindingBits[bit / MaxInlineBits] & (BindingBitsType(1) << (bit % MaxInlineBits)); + const BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; + return bits[offset] & bitFlagForBit(bit); } }; -bool QQmlData::wasDeleted(QObject *object) +bool QQmlData::wasDeleted(const QObject *object) { if (!object) return true; - QObjectPrivate *priv = QObjectPrivate::get(object); + const QObjectPrivate *priv = QObjectPrivate::get(object); if (!priv || priv->wasDeleted) return true; - QQmlData *ddata = QQmlData::get(object); + const QQmlData *ddata = QQmlData::get(object); return ddata && ddata->isQueuedForDeletion; } diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 20f9b6439d..d53c94e901 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -110,7 +110,10 @@ Q_DECLARE_METATYPE(QQmlProperty) QT_BEGIN_NAMESPACE typedef QQmlData::BindingBitsType BindingBitsType; -enum { MaxInlineBits = QQmlData::MaxInlineBits }; +enum { + BitsPerType = QQmlData::BitsPerType, + InlineBindingArraySize = QQmlData::InlineBindingArraySize +}; void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor) { @@ -747,11 +750,12 @@ QQmlData::QQmlData() : ownedByQml1(false), ownMemory(true), indestructible(true), explicitIndestructibleSet(false), hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false), hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), - bindingBitsSize(MaxInlineBits), bindingBitsValue(0), notifyList(0), + bindingBitsArraySize(InlineBindingArraySize), notifyList(0), bindings(0), signalHandlers(0), nextContextObject(0), prevContextObject(0), lineNumber(0), columnNumber(0), jsEngineId(0), compilationUnit(0), propertyCache(0), guards(0), extendedData(0) { + memset(bindingBitsValue, 0, sizeof(bindingBitsValue)); init(); } @@ -1825,7 +1829,7 @@ void QQmlData::destroyed(QObject *object) signalHandler = next; } - if (bindingBitsSize > MaxInlineBits) + if (bindingBitsArraySize > InlineBindingArraySize) free(bindingBits); if (propertyCache) @@ -1874,47 +1878,35 @@ void QQmlData::parentChanged(QObject *object, QObject *parent) static void QQmlData_setBit(QQmlData *data, QObject *obj, int bit) { - if (Q_UNLIKELY(data->bindingBitsSize <= 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); - int arraySize = (2 * props + MaxInlineBits - 1) / MaxInlineBits; - Q_ASSERT(arraySize > 1); - - // special handling for 32 here is to make sure we wipe the first byte - // when going from bindingBitsValue to bindingBits, and preserve the old - // set bits so we can restore them after the allocation - int oldArraySize = data->bindingBitsSize > MaxInlineBits ? data->bindingBitsSize / MaxInlineBits : 0; - quintptr oldValue = data->bindingBitsSize == MaxInlineBits ? data->bindingBitsValue : 0; - - data->bindingBits = static_cast<BindingBitsType *>(realloc((data->bindingBitsSize == MaxInlineBits) ? 0 : data->bindingBits, - arraySize * sizeof(BindingBitsType))); - - memset(data->bindingBits + oldArraySize, - 0x00, - sizeof(BindingBitsType) * (arraySize - oldArraySize)); + uint arraySize = (2 * static_cast<uint>(props) + BitsPerType - 1) / BitsPerType; + Q_ASSERT(arraySize > InlineBindingArraySize && arraySize > data->bindingBitsArraySize); - data->bindingBitsSize = arraySize * MaxInlineBits; + 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)); - // reinstate bindingBitsValue after we dropped it - if (oldValue) { - memcpy(data->bindingBits, &oldValue, sizeof(oldValue)); - } + if (data->bindingBitsArraySize > InlineBindingArraySize) + free(bits); + data->bindingBits = newBits; + bits = newBits; + data->bindingBitsArraySize = arraySize; } - - if (data->bindingBitsSize == MaxInlineBits) - data->bindingBitsValue |= BindingBitsType(1) << bit; - else - data->bindingBits[bit / MaxInlineBits] |= (BindingBitsType(1) << (bit % MaxInlineBits)); + Q_ASSERT(offset < data->bindingBitsArraySize); + bits[offset] |= QQmlData::bitFlagForBit(bit); } static void QQmlData_clearBit(QQmlData *data, int bit) { - if (data->bindingBitsSize > bit) { - if (data->bindingBitsSize == MaxInlineBits) - data->bindingBitsValue &= ~(BindingBitsType(1) << (bit % MaxInlineBits)); - else - data->bindingBits[bit / MaxInlineBits] &= ~(BindingBitsType(1) << (bit % MaxInlineBits)); + uint offset = QQmlData::offsetForBit(bit); + if (data->bindingBitsArraySize > offset) { + BindingBitsType *bits = (data->bindingBitsArraySize == InlineBindingArraySize) ? data->bindingBitsValue : data->bindingBits; + bits[offset] &= ~QQmlData::bitFlagForBit(bit); } } diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index 6d0e4b915a..9855c27375 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -272,7 +272,7 @@ void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i) if (!compilationUnit) return; - QML_MEMORY_SCOPE_URL(compilationUnit->url()); + QML_MEMORY_SCOPE_URL(compilationUnit->finalUrl()); QExplicitlySharedDataPointer<QQmlIncubatorPrivate> protectThis(this); @@ -377,23 +377,6 @@ finishIncubate: } } -void QQmlIncubatorPrivate::cancel(QObject *object, QQmlContext *context) -{ - if (!context) - context = qmlContext(object); - if (!context) - return; - - QQmlContextData *data = QQmlContextData::get(context); - QQmlIncubatorPrivate *p = data->incubator; - if (!p) - return; - - p->vmeGuard.unguard(object); - if (!p->creator.isNull()) - p->creator->cancel(object); -} - /*! Incubate objects for \a msecs, or until there are no more objects to incubate. */ diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h index 758e0a29f6..676ba1a29a 100644 --- a/src/qml/qml/qqmlincubator_p.h +++ b/src/qml/qml/qqmlincubator_p.h @@ -102,9 +102,6 @@ public: void forceCompletion(QQmlInstantiationInterrupt &i); void incubate(QQmlInstantiationInterrupt &i); - - // used by Qt Quick Controls 2 - Q_QML_PRIVATE_EXPORT static void cancel(QObject *object, QQmlContext *context = 0); }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 7761eb4677..7caab2a5d3 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -445,7 +445,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QString string = binding->valueAsString(qmlUnit); // Encoded dir-separators defeat QUrl processing - decode them first string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); - QUrl value = string.isEmpty() ? QUrl() : compilationUnit->url().resolved(QUrl(string)); + QUrl value = string.isEmpty() ? QUrl() : compilationUnit->finalUrl().resolved(QUrl(string)); // Apply URL interceptor if (engine->urlInterceptor()) value = engine->urlInterceptor()->intercept(value, QQmlAbstractUrlInterceptor::UrlString); @@ -643,7 +643,8 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } else if (property->propType() == qMetaTypeId<QList<QUrl> >()) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); QString urlString = binding->valueAsString(qmlUnit); - QUrl u = urlString.isEmpty() ? QUrl() : compilationUnit->url().resolved(QUrl(urlString)); + QUrl u = urlString.isEmpty() ? QUrl() + : compilationUnit->finalUrl().resolved(QUrl(urlString)); QList<QUrl> value; value.append(u); property->writeProperty(_qobject, &value, propertyWriteFlags); @@ -1103,12 +1104,9 @@ void QQmlObjectCreator::registerObjectWithContextById(const QV4::CompiledData::O context->setIdProperty(object->id, instance); } -QV4::QmlContext *QQmlObjectCreator::currentQmlContext() +void QQmlObjectCreator::createQmlContext() { - if (!_qmlContext->isManaged()) - _qmlContext->setM(QV4::QmlContext::create(v4->rootContext(), context, _scopeObject)); - - return _qmlContext; + _qmlContext->setM(QV4::QmlContext::create(v4->rootContext(), context, _scopeObject)); } QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isContextObject) @@ -1349,21 +1347,6 @@ QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interru return sharedState->rootContext; } -void QQmlObjectCreator::cancel(QObject *object) -{ - int last = sharedState->allCreatedObjects.count() - 1; - int i = last; - while (i >= 0) { - if (sharedState->allCreatedObjects.at(i) == object) { - if (i < last) - qSwap(sharedState->allCreatedObjects[i], sharedState->allCreatedObjects[last]); - sharedState->allCreatedObjects.pop(); - break; - } - --i; - } -} - void QQmlObjectCreator::clear() { if (phase == Done || phase == Finalizing || phase == Startup) diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 593d89e5d5..f4c03af5b7 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -92,7 +92,6 @@ public: bool populateDeferredProperties(QObject *instance, QQmlData::DeferredData *deferredData); bool populateDeferredBinding(const QQmlProperty &qmlProperty, QQmlData::DeferredData *deferredData, const QV4::CompiledData::Binding *binding); QQmlContextData *finalize(QQmlInstantiationInterrupt &interrupt); - void cancel(QObject *object); void clear(); QQmlComponentAttached **componentAttachment() const { return &sharedState->componentAttached; } @@ -124,7 +123,8 @@ private: void registerObjectWithContextById(const QV4::CompiledData::Object *object, QObject *instance) const; - QV4::QmlContext *currentQmlContext(); + inline QV4::QmlContext *currentQmlContext(); + Q_NEVER_INLINE void createQmlContext(); enum Phase { Startup, @@ -174,6 +174,14 @@ private: QRecursionWatcher<QQmlObjectCreatorSharedState, &QQmlObjectCreatorSharedState::recursionNode> watcher; }; +QV4::QmlContext *QQmlObjectCreator::currentQmlContext() +{ + if (!_qmlContext->isManaged()) + _qmlContext->setM(QV4::QmlContext::create(v4->rootContext(), context, _scopeObject)); + + return _qmlContext; +} + QT_END_NAMESPACE #endif // QQMLOBJECTCREATOR_P_H diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 9a138dcf80..50d9f13049 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -223,6 +223,22 @@ QQmlProperty::QQmlProperty(QObject *obj, const QString &name, QQmlEngine *engine if (!isValid()) { d->object = 0; d->context = 0; d->engine = 0; } } +QQmlProperty QQmlPropertyPrivate::create(QObject *target, const QString &propertyName, QQmlContextData *context) +{ + QQmlProperty result; + auto d = new QQmlPropertyPrivate; + result.d = d; + d->context = context; + d->engine = context ? context->engine : nullptr; + d->initProperty(target, propertyName); + if (!result.isValid()) { + d->object = nullptr; + d->context = nullptr; + d->engine = nullptr; + } + return result; +} + QQmlPropertyPrivate::QQmlPropertyPrivate() : context(0), engine(0), object(0), isNameCached(false) { @@ -241,89 +257,93 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) QQmlTypeNameCache *typeNameCache = context?context->imports:0; - const auto path = name.splitRef(QLatin1Char('.')); - if (path.isEmpty()) return; - QObject *currentObject = obj; - - // Everything up to the last property must be an "object type" property - for (int ii = 0; ii < path.count() - 1; ++ii) { - const QStringRef &pathName = path.at(ii); - - if (typeNameCache) { - QQmlTypeNameCache::Result r = typeNameCache->query(pathName); - if (r.isValid()) { - if (r.type.isValid()) { - QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine); - QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate); - if (!func) return; // Not an attachable type - - currentObject = qmlAttachedPropertiesObjectById(r.type.attachedPropertiesId(enginePrivate), currentObject); - if (!currentObject) return; // Something is broken with the attachable type - } else if (r.importNamespace) { - if ((ii + 1) == path.count()) return; // No type following the namespace - - ++ii; r = typeNameCache->query(path.at(ii), r.importNamespace); - if (!r.type.isValid()) return; // Invalid type in namespace - - QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine); - QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate); - if (!func) return; // Not an attachable type - - currentObject = qmlAttachedPropertiesObjectById(r.type.attachedPropertiesId(enginePrivate), currentObject); - if (!currentObject) return; // Something is broken with the attachable type - - } else if (r.scriptIndex != -1) { - return; // Not a type - } else { - Q_ASSERT(!"Unreachable"); + QVector<QStringRef> path; + QStringRef terminal(&name); + + if (name.contains(QLatin1Char('.'))) { + path = name.splitRef(QLatin1Char('.')); + if (path.isEmpty()) return; + + // Everything up to the last property must be an "object type" property + for (int ii = 0; ii < path.count() - 1; ++ii) { + const QStringRef &pathName = path.at(ii); + + if (typeNameCache) { + QQmlTypeNameCache::Result r = typeNameCache->query(pathName); + if (r.isValid()) { + if (r.type.isValid()) { + QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine); + QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate); + if (!func) return; // Not an attachable type + + currentObject = qmlAttachedPropertiesObjectById(r.type.attachedPropertiesId(enginePrivate), currentObject); + if (!currentObject) return; // Something is broken with the attachable type + } else if (r.importNamespace) { + if ((ii + 1) == path.count()) return; // No type following the namespace + + ++ii; r = typeNameCache->query(path.at(ii), r.importNamespace); + if (!r.type.isValid()) return; // Invalid type in namespace + + QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine); + QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate); + if (!func) return; // Not an attachable type + + currentObject = qmlAttachedPropertiesObjectById(r.type.attachedPropertiesId(enginePrivate), currentObject); + if (!currentObject) return; // Something is broken with the attachable type + + } else if (r.scriptIndex != -1) { + return; // Not a type + } else { + Q_ASSERT(!"Unreachable"); + } + continue; } - continue; + } - } + QQmlPropertyData local; + QQmlPropertyData *property = + QQmlPropertyCache::property(engine, currentObject, pathName, context, local); - QQmlPropertyData local; - QQmlPropertyData *property = - QQmlPropertyCache::property(engine, currentObject, pathName, context, local); + if (!property) return; // Not a property + if (property->isFunction()) + return; // Not an object property - if (!property) return; // Not a property - if (property->isFunction()) - return; // Not an object property + if (ii == (path.count() - 2) && QQmlValueTypeFactory::isValueType(property->propType())) { + // We're now at a value type property + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType()); + if (!valueTypeMetaObject) return; // Not a value type - if (ii == (path.count() - 2) && QQmlValueTypeFactory::isValueType(property->propType())) { - // We're now at a value type property - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType()); - if (!valueTypeMetaObject) return; // Not a value type + int idx = valueTypeMetaObject->indexOfProperty(path.last().toUtf8().constData()); + if (idx == -1) return; // Value type property does not exist - int idx = valueTypeMetaObject->indexOfProperty(path.last().toUtf8().constData()); - if (idx == -1) return; // Value type property does not exist + QMetaProperty vtProp = valueTypeMetaObject->property(idx); - QMetaProperty vtProp = valueTypeMetaObject->property(idx); + Q_ASSERT(vtProp.userType() <= 0x0000FFFF); + Q_ASSERT(idx <= 0x0000FFFF); - Q_ASSERT(vtProp.userType() <= 0x0000FFFF); - Q_ASSERT(idx <= 0x0000FFFF); + object = currentObject; + core = *property; + valueTypeData.setFlags(QQmlPropertyData::flagsForProperty(vtProp)); + valueTypeData.setPropType(vtProp.userType()); + valueTypeData.setCoreIndex(idx); - object = currentObject; - core = *property; - valueTypeData.setFlags(QQmlPropertyData::flagsForProperty(vtProp)); - valueTypeData.setPropType(vtProp.userType()); - valueTypeData.setCoreIndex(idx); + return; + } else { + if (!property->isQObject()) + return; // Not an object property - return; - } else { - if (!property->isQObject()) - return; // Not an object property + property->readProperty(currentObject, ¤tObject); + if (!currentObject) return; // No value - property->readProperty(currentObject, ¤tObject); - if (!currentObject) return; // No value + } } + terminal = path.last(); } - const QStringRef &terminal = path.last(); - if (terminal.count() >= 3 && terminal.at(0) == QLatin1Char('o') && terminal.at(1) == QLatin1Char('n') && diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h index 53062a2f13..7a66d8113c 100644 --- a/src/qml/qml/qqmlproperty_p.h +++ b/src/qml/qml/qqmlproperty_p.h @@ -144,6 +144,8 @@ public: static void flushSignal(const QObject *sender, int signal_index); static QVariant resolvedUrlSequence(const QVariant &value, QQmlContextData *context); + static QQmlProperty create(QObject *target, const QString &propertyName, QQmlContextData *context); + }; Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyPrivate::BindingFlags) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index a4db62eebf..4b6e69e617 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -356,8 +356,10 @@ qreal QQmlDataBlob::progress() const } /*! -Returns the blob url passed to the constructor. If a network redirect -happens while fetching the data, this url remains the same. +Returns the physical url of the data. Initially this is the same as +finalUrl(), but if a network redirect happens while fetching the data, this url +is updated to reflect the new location. Also, if a URL interceptor is set, it +will work on this URL and leave finalUrl() alone. \sa finalUrl() */ @@ -366,16 +368,25 @@ QUrl QQmlDataBlob::url() const return m_url; } +QString QQmlDataBlob::urlString() const +{ + if (m_urlString.isEmpty()) + m_urlString = m_url.toString(); + + return m_urlString; +} + /*! -Returns the final url of the data. Initially this is the same as -url(), but if a network redirect happens while fetching the data, this url -is updated to reflect the new location. +Returns the logical URL to be used for resolving further URLs referred to in +the code. -May only be called from the load thread, or after the blob isCompleteOrError(). +This is the blob url passed to the constructor. If a network redirect +happens while fetching the data, this url remains the same. + +\sa url() */ QUrl QQmlDataBlob::finalUrl() const { - Q_ASSERT(isCompleteOrError() || (m_typeLoader && m_typeLoader->m_thread->isThisThread())); return m_finalUrl; } @@ -384,7 +395,6 @@ Returns the finalUrl() as a string. */ QString QQmlDataBlob::finalUrlString() const { - Q_ASSERT(isCompleteOrError() || (m_typeLoader && m_typeLoader->m_thread->isThisThread())); if (m_finalUrlString.isEmpty()) m_finalUrlString = m_finalUrl.toString(); @@ -433,7 +443,7 @@ void QQmlDataBlob::setError(const QList<QQmlError> &errors) m_data.setStatus(Error); if (dumpErrors()) { - qWarning().nospace() << "Errors for " << m_finalUrl.toString(); + qWarning().nospace() << "Errors for " << urlString(); for (int ii = 0; ii < errors.count(); ++ii) qWarning().nospace() << " " << qPrintable(errors.at(ii).toString()); } @@ -472,7 +482,7 @@ void QQmlDataBlob::setError(const QString &description) { QQmlError e; e.setDescription(description); - e.setUrl(finalUrl()); + e.setUrl(url()); setError(e); } @@ -537,7 +547,7 @@ void QQmlDataBlob::networkError(QNetworkReply::NetworkError networkError) Q_UNUSED(networkError); QQmlError error; - error.setUrl(m_finalUrl); + error.setUrl(m_url); const char *errorString = 0; switch (networkError) { @@ -654,7 +664,7 @@ void QQmlDataBlob::tryDone() addref(); #ifdef DATABLOB_DEBUG - qWarning("QQmlDataBlob::done() %s", qPrintable(url().toString())); + qWarning("QQmlDataBlob::done() %s", qPrintable(urlString())); #endif done(); @@ -893,7 +903,7 @@ void QQmlTypeLoaderThread::callCompletedMain(QQmlDataBlob *b) { QML_MEMORY_SCOPE_URL(b->url()); #ifdef DATABLOB_DEBUG - qWarning("QQmlTypeLoaderThread: %s completed() callback", qPrintable(b->url().toString())); + qWarning("QQmlTypeLoaderThread: %s completed() callback", qPrintable(b->urlString())); #endif b->completed(); b->release(); @@ -903,7 +913,7 @@ void QQmlTypeLoaderThread::callDownloadProgressChangedMain(QQmlDataBlob *b, qrea { #ifdef DATABLOB_DEBUG qWarning("QQmlTypeLoaderThread: %s downloadProgressChanged(%f) callback", - qPrintable(b->url().toString()), p); + qPrintable(b->urlString()), p); #endif b->downloadProgressChanged(p); b->release(); @@ -1037,7 +1047,7 @@ template<typename Loader> void QQmlTypeLoader::doLoad(const Loader &loader, QQmlDataBlob *blob, Mode mode) { #ifdef DATABLOB_DEBUG - qWarning("QQmlTypeLoader::doLoad(%s): %s thread", qPrintable(blob->m_url.toString()), + qWarning("QQmlTypeLoader::doLoad(%s): %s thread", qPrintable(blob->urlString()), m_thread->isThisThread()?"Compile":"Engine"); #endif blob->startLoading(); @@ -1159,7 +1169,7 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob) } #ifdef DATABLOB_DEBUG - qWarning("QQmlDataBlob: requested %s", qPrintable(blob->url().toString())); + qWarning("QQmlDataBlob: requested %s", qPrintable(blob->urlString())); #endif // DATABLOB_DEBUG #endif // qml_network } @@ -1185,14 +1195,15 @@ void QQmlTypeLoader::networkReplyFinished(QNetworkReply *reply) QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); if (redirect.isValid()) { QUrl url = reply->url().resolved(redirect.toUrl()); - blob->m_finalUrl = url; + blob->m_url = url; + blob->m_urlString.clear(); QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(url)); QObject *nrp = m_thread->networkReplyProxy(); QObject::connect(reply, SIGNAL(finished()), nrp, SLOT(finished())); m_networkReplies.insert(reply, blob); #ifdef DATABLOB_DEBUG - qWarning("QQmlDataBlob: redirected to %s", qPrintable(blob->m_finalUrl.toString())); + qWarning("QQmlDataBlob: redirected to %s", qPrintable(blob->urlString())); #endif return; } @@ -1348,7 +1359,7 @@ bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QV4::CompiledData: bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors) { - QString qmldirIdentifier = data->url().toString(); + QString qmldirIdentifier = data->urlString(); QString qmldirUrl = qmldirIdentifier.left(qmldirIdentifier.lastIndexOf(QLatin1Char('/')) + 1); typeLoader()->setQmldirContent(qmldirIdentifier, data->content()); @@ -2104,7 +2115,7 @@ bool QQmlTypeData::tryLoadFromDiskCache() { QString error; if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), v4->iselFactory.data(), &error)) { - qCDebug(DBG_DISK_CACHE) << "Error loading" << url().toString() << "from disk cache:" << error; + qCDebug(DBG_DISK_CACHE) << "Error loading" << urlString() << "from disk cache:" << error; return false; } } @@ -2224,10 +2235,10 @@ void QQmlTypeData::done() if (script.script->isError()) { QList<QQmlError> errors = script.script->errors(); QQmlError error; - error.setUrl(finalUrl()); + error.setUrl(url()); error.setLine(script.location.line); error.setColumn(script.location.column); - error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString())); + error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString())); errors.prepend(error); setError(errors); return; @@ -2244,7 +2255,7 @@ void QQmlTypeData::done() QList<QQmlError> errors = type.typeData->errors(); QQmlError error; - error.setUrl(finalUrl()); + error.setUrl(url()); error.setLine(type.location.line); error.setColumn(type.location.column); error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); @@ -2263,7 +2274,7 @@ void QQmlTypeData::done() QList<QQmlError> errors = type.typeData->errors(); QQmlError error; - error.setUrl(finalUrl()); + error.setUrl(url()); error.setLine(type.location.line); error.setColumn(type.location.column); error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName)); @@ -2293,7 +2304,7 @@ void QQmlTypeData::done() // verify if any dependencies changed if we're using a cache if (m_document.isNull() && !m_compiledData->verifyChecksum(dependencyHasher)) { - qCDebug(DBG_DISK_CACHE) << "Checksum mismatch for cached version of" << m_compiledData->url().toString(); + qCDebug(DBG_DISK_CACHE) << "Checksum mismatch for cached version of" << m_compiledData->fileName(); if (!loadFromSource()) return; m_backupSourceCode = SourceCodeData(); @@ -2458,7 +2469,7 @@ bool QQmlTypeData::loadFromSource() errors.reserve(compiler.errors.count()); for (const QQmlJS::DiagnosticMessage &msg : qAsConst(compiler.errors)) { QQmlError e; - e.setUrl(finalUrl()); + e.setUrl(url()); e.setLine(msg.loc.startLine); e.setColumn(msg.loc.startColumn); e.setDescription(msg.message); @@ -2475,7 +2486,8 @@ void QQmlTypeData::restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_document.reset(new QmlIR::Document(isDebugging())); QmlIR::IRLoader loader(unit->data, m_document.data()); loader.load(); - m_document->jsModule.setFileName(finalUrlString()); + m_document->jsModule.setFileName(urlString()); + m_document->jsModule.setFinalUrl(finalUrlString()); m_document->javaScriptCompilationUnit = unit; continueLoadFromIR(); } @@ -2598,7 +2610,7 @@ void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCach // ignore error, keep using the in-memory compilation unit. } } else { - qCDebug(DBG_DISK_CACHE) << "Error saving cached version of" << m_compiledData->url().toString() << "to disk:" << errorString; + qCDebug(DBG_DISK_CACHE) << "Error saving cached version of" << m_compiledData->fileName() << "to disk:" << errorString; } } } @@ -2973,7 +2985,7 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data) initializeFromCompilationUnit(unit); return; } else { - qCDebug(DBG_DISK_CACHE()) << "Error loading" << url().toString() << "from disk cache:" << error; + qCDebug(DBG_DISK_CACHE()) << "Error loading" << urlString() << "from disk cache:" << error; } } @@ -2991,7 +3003,9 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data) QmlIR::ScriptDirectivesCollector collector(&irUnit.jsParserEngine, &irUnit.jsGenerator); QList<QQmlError> errors; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Script::precompile(&irUnit.jsModule, &irUnit.jsGenerator, v4, finalUrl(), source, &errors, &collector); + QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Script::precompile( + &irUnit.jsModule, &irUnit.jsGenerator, v4, urlString(), finalUrlString(), + source, &errors, &collector); // No need to addref on unit, it's initial refcount is 1 source.clear(); if (!errors.isEmpty()) { @@ -3015,7 +3029,7 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data) if (!disableDiskCache() || forceDiskCache()) { QString errorString; if (!unit->saveToDisk(url(), &errorString)) { - qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of" << unit->url().toString() << "to disk:" << errorString; + qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of" << unit->fileName() << "to disk:" << errorString; } } @@ -3039,10 +3053,10 @@ void QQmlScriptBlob::done() if (script.script->isError()) { QList<QQmlError> errors = script.script->errors(); QQmlError error; - error.setUrl(finalUrl()); + error.setUrl(url()); error.setLine(script.location.line); error.setColumn(script.location.column); - error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString())); + error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString())); errors.prepend(error); setError(errors); return; diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 22ac61968f..ab32bac7b2 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -129,6 +129,7 @@ public: qreal progress() const; QUrl url() const; + QString urlString() const; QUrl finalUrl() const; QString finalUrlString() const; @@ -207,6 +208,7 @@ private: QUrl m_url; QUrl m_finalUrl; + mutable QString m_urlString; mutable QString m_finalUrlString; // List of QQmlDataBlob's that are waiting for me to complete. diff --git a/src/qml/qml/qqmlvme.cpp b/src/qml/qml/qqmlvme.cpp index c60f4edc80..72d4ab7e8f 100644 --- a/src/qml/qml/qqmlvme.cpp +++ b/src/qml/qml/qqmlvme.cpp @@ -120,18 +120,6 @@ void QQmlVMEGuard::guard(QQmlObjectCreator *creator) m_contexts[0] = creator->parentContextData(); } -void QQmlVMEGuard::unguard(QObject *object) -{ - for (int ii = 0; ii < m_objectCount; ++ii) { - if (m_objects[ii] == object) { - if (ii < m_objectCount - 1) - ::memmove((void *) m_objects[ii], (void *) m_objects[ii + 1], sizeof(QPointer<QObject> *)); - delete m_objects[--m_objectCount]; - break; - } - } -} - void QQmlVMEGuard::clear() { delete [] m_objects; diff --git a/src/qml/qml/qqmlvme_p.h b/src/qml/qml/qqmlvme_p.h index 9585b5b6df..99d63380ad 100644 --- a/src/qml/qml/qqmlvme_p.h +++ b/src/qml/qml/qqmlvme_p.h @@ -131,7 +131,6 @@ public: ~QQmlVMEGuard(); void guard(QQmlObjectCreator *); - void unguard(QObject *); void clear(); bool isOK() const; diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index dc41a16e3b..7a34bab2f5 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -864,7 +864,7 @@ void QQmlDelegateModelPrivate::releaseIncubator(QQDMIncubationTask *incubationTa void QQmlDelegateModelPrivate::removeCacheItem(QQmlDelegateModelItem *cacheItem) { - int cidx = m_cache.indexOf(cacheItem); + int cidx = m_cache.lastIndexOf(cacheItem); if (cidx >= 0) { m_compositor.clearFlags(Compositor::Cache, cidx, 1, Compositor::CacheFlag); m_cache.removeAt(cidx); @@ -1021,11 +1021,11 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ /* If asynchronous is true or the component is being loaded asynchronously due - to an ancestor being loaded asynchronously, item() may return 0. In this - case createdItem() will be emitted when the item is available. The item - at this stage does not have any references, so item() must be called again - to ensure a reference is held. Any call to item() which returns a valid item - must be matched by a call to release() in order to destroy the item. + to an ancestor being loaded asynchronously, object() may return 0. In this + case createdItem() will be emitted when the object is available. The object + at this stage does not have any references, so object() must be called again + to ensure a reference is held. Any call to object() which returns a valid object + must be matched by a call to release() in order to destroy the object. */ QObject *QQmlDelegateModel::object(int index, QQmlIncubator::IncubationMode incubationMode) { |