diff options
70 files changed, 934 insertions, 529 deletions
diff --git a/src/imports/qtquick2/plugins.qmltypes b/src/imports/qtquick2/plugins.qmltypes index a1a3723600..c3a14254f3 100644 --- a/src/imports/qtquick2/plugins.qmltypes +++ b/src/imports/qtquick2/plugins.qmltypes @@ -4628,6 +4628,7 @@ Module { Property { name: "pixelDelta"; type: "QPoint"; isReadonly: true } Property { name: "buttons"; type: "int"; isReadonly: true } Property { name: "modifiers"; type: "int"; isReadonly: true } + Property { name: "inverted"; type: "bool" } Property { name: "accepted"; type: "bool" } } Component { diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp index 47efc9ec15..4153c70fd0 100644 --- a/src/imports/statemachine/signaltransition.cpp +++ b/src/imports/statemachine/signaltransition.cpp @@ -145,7 +145,7 @@ void SignalTransition::invoke() void SignalTransition::connectTriggered() { - if (!m_complete || !m_cdata) + if (!m_complete || !m_compilationUnit) return; QObject *target = senderObject(); @@ -165,7 +165,7 @@ void SignalTransition::connectTriggered() QQmlBoundSignalExpression *expression = ctxtdata ? new QQmlBoundSignalExpression(target, signalIndex, - ctxtdata, this, m_cdata->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]) : 0; + ctxtdata, this, m_compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]) : 0; if (expression) expression->setNotifyOnValueChanged(false); m_signalExpression = expression; @@ -190,10 +190,10 @@ void SignalTransitionParser::verifyBindings(const QV4::CompiledData::Unit *qmlUn } } -void SignalTransitionParser::applyBindings(QObject *object, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings) +void SignalTransitionParser::applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { SignalTransition *st = qobject_cast<SignalTransition*>(object); - st->m_cdata = cdata; + st->m_compilationUnit = compilationUnit; st->m_bindings = bindings; } diff --git a/src/imports/statemachine/signaltransition.h b/src/imports/statemachine/signaltransition.h index 3471aea0c8..4a246e165f 100644 --- a/src/imports/statemachine/signaltransition.h +++ b/src/imports/statemachine/signaltransition.h @@ -90,7 +90,7 @@ private: QJSValue m_signal; QQmlScriptString m_guard; bool m_complete; - QQmlRefPointer<QQmlCompiledData> m_cdata; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit; QList<const QV4::CompiledData::Binding *> m_bindings; QQmlBoundSignalExpressionPointer m_signalExpression; }; @@ -99,7 +99,7 @@ class SignalTransitionParser : public QQmlCustomParser { public: void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props) Q_DECL_OVERRIDE; - void applyBindings(QObject *object, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings) Q_DECL_OVERRIDE; + void applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) Q_DECL_OVERRIDE; }; QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp index a90d03b010..756b6b28be 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp @@ -114,7 +114,7 @@ void QV4DebuggerAgent::addDebugger(QV4Debugger *debugger) debugger->setBreakOnThrow(m_breakOnThrow); - foreach (const BreakPoint &breakPoint, m_breakPoints.values()) + for (const BreakPoint &breakPoint : qAsConst(m_breakPoints)) if (breakPoint.enabled) debugger->addBreakPoint(breakPoint.fileName, breakPoint.lineNr, breakPoint.condition); diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp index a193ddea0b..f161f988de 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp @@ -55,7 +55,7 @@ QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEngin connect(this, SIGNAL(profilingDisabled()), engine->profiler, SLOT(stopProfiling())); connect(this, SIGNAL(profilingDisabledWhileWaiting()), engine->profiler, SLOT(stopProfiling()), Qt::DirectConnection); - connect(this, SIGNAL(dataRequested()), engine->profiler, SLOT(reportData())); + connect(this, SIGNAL(dataRequested(bool)), engine->profiler, SLOT(reportData(bool))); connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)), engine->profiler, SLOT(setTimer(QElapsedTimer))); connect(engine->profiler, @@ -68,49 +68,65 @@ QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEngin // use of QDataStream can skew results // (see tst_qqmldebugtrace::trace() benchmark) static void qQmlProfilerDataToByteArrays(const QQmlProfilerData &d, - const QQmlProfiler::LocationHash &locations, - QList<QByteArray> &messages) + QQmlProfiler::LocationHash &locations, + QList<QByteArray> &messages, + bool trackLocations) { QQmlDebugPacket ds; Q_ASSERT_X((d.messageType & (1 << 31)) == 0, Q_FUNC_INFO, "You can use at most 31 message types."); for (quint32 decodedMessageType = 0; (d.messageType >> decodedMessageType) != 0; ++decodedMessageType) { - if ((d.messageType & (1 << decodedMessageType)) == 0) - continue; - - //### using QDataStream is relatively expensive - ds << d.time << decodedMessageType << static_cast<quint32>(d.detailType); - - QQmlProfiler::Location l = locations.value(d.locationId); + if (decodedMessageType == QQmlProfilerDefinitions::RangeData + || (d.messageType & (1 << decodedMessageType)) == 0) { + continue; // RangeData is sent together with RangeLocation + } - switch (decodedMessageType) { - case QQmlProfilerDefinitions::RangeStart: - case QQmlProfilerDefinitions::RangeEnd: - break; - case QQmlProfilerDefinitions::RangeData: - ds << (l.location.sourceFile.isEmpty() ? l.url.toString() : l.location.sourceFile); - break; - case QQmlProfilerDefinitions::RangeLocation: - ds << (l.url.isEmpty() ? l.location.sourceFile : l.url.toString()) - << static_cast<qint32>(l.location.line) << static_cast<qint32>(l.location.column); - break; - default: - Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message type."); - break; + if (decodedMessageType == QQmlProfilerDefinitions::RangeEnd + || decodedMessageType == QQmlProfilerDefinitions::RangeStart) { + ds << d.time << decodedMessageType << static_cast<quint32>(d.detailType); + if (trackLocations && d.locationId != 0) + ds << static_cast<qint64>(d.locationId); + } else { + auto i = locations.find(d.locationId); + if (i != locations.end()) { + ds << d.time << decodedMessageType << static_cast<quint32>(d.detailType); + ds << (i->url.isEmpty() ? i->location.sourceFile : i->url.toString()) + << static_cast<qint32>(i->location.line) + << static_cast<qint32>(i->location.column); + if (d.messageType & (1 << QQmlProfilerDefinitions::RangeData)) { + // Send both, location and data ... + if (trackLocations) + ds << static_cast<qint64>(d.locationId); + messages.append(ds.squeezedData()); + ds.clear(); + ds << d.time << QQmlProfilerDefinitions::RangeData + << static_cast<quint32>(d.detailType) + << (i->location.sourceFile.isEmpty() ? i->url.toString() : + i->location.sourceFile); + } + if (trackLocations) { + ds << static_cast<qint64>(d.locationId); + locations.erase(i); // ... so that we can erase here without missing anything. + } + } else { + // Skip RangeData and RangeLocation: We've already sent them + continue; + } } messages.append(ds.squeezedData()); ds.clear(); } } -qint64 QQmlProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages) +qint64 QQmlProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages, + bool trackLocations) { while (next != data.length()) { const QQmlProfilerData &nextData = data.at(next); if (nextData.time > until || messages.length() > s_numMessagesPerBatch) return nextData.time; - qQmlProfilerDataToByteArrays(nextData, locations, messages); + qQmlProfilerDataToByteArrays(nextData, locations, messages, trackLocations); ++next; } diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h index 7e13b6c479..96cdcd6d38 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h @@ -60,7 +60,8 @@ class QQmlProfilerAdapter : public QQmlAbstractProfilerAdapter { Q_OBJECT public: QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine); - qint64 sendMessages(qint64 until, QList<QByteArray> &messages) Q_DECL_OVERRIDE; + qint64 sendMessages(qint64 until, QList<QByteArray> &messages, + bool trackLocations) Q_DECL_OVERRIDE; public slots: void receiveData(const QVector<QQmlProfilerData> &new_data, diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp index a639cfb71e..a587188630 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp @@ -57,7 +57,7 @@ Q_QML_DEBUG_PLUGIN_LOADER(QQmlAbstractProfilerAdapter) QQmlProfilerServiceImpl::QQmlProfilerServiceImpl(QObject *parent) : QQmlConfigurableDebugService<QQmlProfilerService>(1, parent), - m_waitingForStop(false) + m_waitingForStop(false), m_useMessageTypes(false) { m_timer.start(); QQmlAbstractProfilerAdapter *quickAdapter = @@ -309,7 +309,7 @@ void QQmlProfilerServiceImpl::stopProfiling(QJSEngine *engine) m_waitingForStop = true; foreach (QQmlAbstractProfilerAdapter *profiler, reporting) - profiler->reportData(); + profiler->reportData(m_useMessageTypes); foreach (QQmlAbstractProfilerAdapter *profiler, stopping) profiler->stopProfiling(); @@ -343,7 +343,8 @@ void QQmlProfilerServiceImpl::sendMessages() m_startTimes.erase(m_startTimes.begin()); qint64 next = first->sendMessages(m_startTimes.isEmpty() ? std::numeric_limits<qint64>::max() : - m_startTimes.begin().key(), messages); + m_startTimes.begin().key(), messages, + m_useMessageTypes); if (next != -1) m_startTimes.insert(next, first); @@ -418,6 +419,8 @@ void QQmlProfilerServiceImpl::messageReceived(const QByteArray &message) disconnect(this, SIGNAL(stopFlushTimer()), &m_flushTimer, SLOT(stop())); } } + if (!stream.atEnd()) + stream >> m_useMessageTypes; // If engineId == -1 objectForId() and then the cast will return 0. if (enabled) @@ -435,14 +438,14 @@ void QQmlProfilerServiceImpl::flush() foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers) { if (profiler->isRunning()) { m_startTimes.insert(-1, profiler); - profiler->reportData(); + profiler->reportData(m_useMessageTypes); } } foreach (QQmlAbstractProfilerAdapter *profiler, m_globalProfilers) { if (profiler->isRunning()) { m_startTimes.insert(-1, profiler); - profiler->reportData(); + profiler->reportData(m_useMessageTypes); } } } diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h index 6490e77f44..42efdefd12 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h @@ -116,6 +116,7 @@ private: QElapsedTimer m_timer; QTimer m_flushTimer; bool m_waitingForStop; + bool m_useMessageTypes; QList<QQmlAbstractProfilerAdapter *> m_globalProfilers; QMultiHash<QJSEngine *, QQmlAbstractProfilerAdapter *> m_engineProfilers; diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp index 68a71a5524..c3bbb86e3a 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp @@ -58,7 +58,7 @@ QV4ProfilerAdapter::QV4ProfilerAdapter(QQmlProfilerService *service, QV4::Execut connect(this, SIGNAL(profilingDisabled()), engine->profiler, SLOT(stopProfiling())); connect(this, SIGNAL(profilingDisabledWhileWaiting()), engine->profiler, SLOT(stopProfiling()), Qt::DirectConnection); - connect(this, SIGNAL(dataRequested()), engine->profiler, SLOT(reportData())); + connect(this, SIGNAL(dataRequested(bool)), engine->profiler, SLOT(reportData(bool))); connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)), engine->profiler, SLOT(setTimer(QElapsedTimer))); connect(engine->profiler, SIGNAL(dataReady(QV4::Profiling::FunctionLocationHash, @@ -105,13 +105,13 @@ qint64 QV4ProfilerAdapter::finalizeMessages(qint64 until, QList<QByteArray> &mes return callNext == -1 ? memoryNext : qMin(callNext, memoryNext); } -qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages) +qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages, + bool trackLocations) { QQmlDebugPacket d; // Make it const, so that we cannot accidentally detach it. const QVector<QV4::Profiling::FunctionCallProperties> &functionCallData = m_functionCallData; - const QV4::Profiling::FunctionLocationHash &functionLocations = m_functionLocations; while (true) { while (!m_stack.isEmpty() && @@ -133,17 +133,27 @@ qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &message return finalizeMessages(until, messages, props.start, d); appendMemoryEvents(props.start, messages, d); - auto location = functionLocations.constFind(props.id); - Q_ASSERT(location != functionLocations.constEnd()); + auto location = m_functionLocations.find(props.id); d << props.start << RangeStart << Javascript; - messages.push_back(d.squeezedData()); - d.clear(); - d << props.start << RangeLocation << Javascript << location->file << location->line - << location->column; - messages.push_back(d.squeezedData()); - d.clear(); - d << props.start << RangeData << Javascript << location->name; + if (trackLocations) + d << static_cast<qint64>(props.id); + if (location != m_functionLocations.end()) { + messages.push_back(d.squeezedData()); + d.clear(); + d << props.start << RangeLocation << Javascript << location->file << location->line + << location->column; + if (trackLocations) + d << static_cast<qint64>(props.id); + messages.push_back(d.squeezedData()); + d.clear(); + d << props.start << RangeData << Javascript << location->name; + + if (trackLocations) { + d << static_cast<qint64>(props.id); + m_functionLocations.erase(location); + } + } messages.push_back(d.squeezedData()); d.clear(); m_stack.push(props.end); diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h index 968825c346..13a595f6eb 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h @@ -67,7 +67,8 @@ class QV4ProfilerAdapter : public QQmlAbstractProfilerAdapter { public: QV4ProfilerAdapter(QQmlProfilerService *service, QV4::ExecutionEngine *engine); - virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages); + virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages, + bool trackLocations) override; signals: void v4ProfilingEnabled(quint64 v4Features); diff --git a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp index 9a2afd367d..bebf8806c6 100644 --- a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp @@ -61,8 +61,8 @@ QQuickProfilerAdapter::QQuickProfilerAdapter(QObject *parent) : QQuickProfiler::s_instance, SLOT(stopProfilingImpl()), Qt::DirectConnection); connect(this, SIGNAL(profilingDisabledWhileWaiting()), QQuickProfiler::s_instance, SLOT(stopProfilingImpl()), Qt::DirectConnection); - connect(this, SIGNAL(dataRequested()), - QQuickProfiler::s_instance, SLOT(reportDataImpl()), Qt::DirectConnection); + connect(this, SIGNAL(dataRequested(bool)), + QQuickProfiler::s_instance, SLOT(reportDataImpl(bool)), Qt::DirectConnection); connect(QQuickProfiler::s_instance, SIGNAL(dataReady(QVector<QQuickProfilerData>)), this, SLOT(receiveData(QVector<QQuickProfilerData>)), Qt::DirectConnection); } @@ -150,8 +150,10 @@ static void qQuickProfilerDataToByteArrays(const QQuickProfilerData &data, } } -qint64 QQuickProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages) +qint64 QQuickProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages, + bool trackLocations) { + Q_UNUSED(trackLocations); while (next < m_data.size()) { if (m_data[next].time <= until && messages.length() <= s_numMessagesPerBatch) qQuickProfilerDataToByteArrays(m_data[next++], messages); diff --git a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h index 0983561d2c..f1ba411ac5 100644 --- a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h +++ b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h @@ -61,7 +61,7 @@ class QQuickProfilerAdapter : public QQmlAbstractProfilerAdapter { public: QQuickProfilerAdapter(QObject *parent = 0); ~QQuickProfilerAdapter(); - qint64 sendMessages(qint64 until, QList<QByteArray> &messages); + qint64 sendMessages(qint64 until, QList<QByteArray> &messages, bool trackLocations) override; public slots: void receiveData(const QVector<QQuickProfilerData> &new_data); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp index 44c2744978..067b0d35f6 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp @@ -1089,7 +1089,7 @@ void QSGD3D12RenderLoop::polishAndSync(WindowData *w, bool inExpose) } // Flush pending touch events. - QQuickWindowPrivate::get(window)->flushDelayedTouchEvent(); + QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents(); // The delivery of the event might have caused the window to stop rendering w = windowFor(windows, window); if (!w || !w->thread || !w->thread->exposedWindow) { diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index a1713dd809..c9a4e21ddd 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -72,23 +72,24 @@ using namespace QmlIR; return false; \ } -void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int id, const QQmlJS::AST::SourceLocation &loc) +void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &loc) { inheritedTypeNameIndex = typeNameIndex; location.line = loc.startLine; location.column = loc.startColumn; - idIndex = id; + idNameIndex = idIndex; + id = -1; indexOfDefaultPropertyOrAlias = -1; defaultPropertyIsAlias = false; + flags = QV4::CompiledData::Object::NoFlag; properties = pool->New<PoolList<Property> >(); aliases = pool->New<PoolList<Alias> >(); qmlSignals = pool->New<PoolList<Signal> >(); bindings = pool->New<PoolList<Binding> >(); functions = pool->New<PoolList<Function> >(); functionsAndExpressions = pool->New<PoolList<CompiledFunctionOrExpression> >(); - runtimeFunctionIndices = 0; declarationsOverride = 0; } @@ -1237,10 +1238,10 @@ bool IRBuilder::setId(const QQmlJS::AST::SourceLocation &idLocation, QQmlJS::AST if (illegalNames.contains(idQString)) COMPILE_EXCEPTION(loc, tr( "ID illegally masks global JavaScript property")); - if (_object->idIndex != emptyStringIndex) + if (_object->idNameIndex != emptyStringIndex) COMPILE_EXCEPTION(idLocation, tr("Property value set multiple times")); - _object->idIndex = registerString(idQString); + _object->idNameIndex = registerString(idQString); _object->locationOfIdProperty.line = idLocation.startLine; _object->locationOfIdProperty.column = idLocation.startColumn; @@ -1368,12 +1369,12 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) const int importSize = sizeof(QV4::CompiledData::Import) * output.imports.count(); const int objectOffsetTableSize = output.objects.count() * sizeof(quint32); - QHash<Object*, quint32> objectOffsets; + QHash<const Object*, quint32> objectOffsets; int objectsSize = 0; foreach (Object *o, output.objects) { objectOffsets.insert(o, unitSize + importSize + objectOffsetTableSize + objectsSize); - objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->aliasCount(), o->signalCount(), o->bindingCount()); + objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->aliasCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count); int signalTableSize = 0; for (const Signal *s = o->firstSignal(); s; s = s->next) @@ -1413,14 +1414,17 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) // write objects quint32 *objectTable = reinterpret_cast<quint32*>(data + qmlUnit->offsetToObjects); char *objectPtr = data + qmlUnit->offsetToObjects + objectOffsetTableSize; - foreach (Object *o, output.objects) { + for (int i = 0; i < output.objects.count(); ++i) { + const Object *o = output.objects.at(i); *objectTable++ = objectOffsets.value(o); QV4::CompiledData::Object *objectToWrite = reinterpret_cast<QV4::CompiledData::Object*>(objectPtr); objectToWrite->inheritedTypeNameIndex = o->inheritedTypeNameIndex; objectToWrite->indexOfDefaultPropertyOrAlias = o->indexOfDefaultPropertyOrAlias; objectToWrite->defaultPropertyIsAlias = o->defaultPropertyIsAlias; - objectToWrite->idIndex = o->idIndex; + objectToWrite->flags = o->flags; + objectToWrite->idNameIndex = o->idNameIndex; + objectToWrite->id = o->id; objectToWrite->location = o->location; objectToWrite->locationOfIdProperty = o->locationOfIdProperty; @@ -1446,9 +1450,13 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) objectToWrite->offsetToBindings = nextOffset; nextOffset += objectToWrite->nBindings * sizeof(QV4::CompiledData::Binding); + objectToWrite->nNamedObjectsInComponent = o->namedObjectsInComponent.count; + objectToWrite->offsetToNamedObjectsInComponent = nextOffset; + nextOffset += objectToWrite->nNamedObjectsInComponent * sizeof(quint32); + quint32 *functionsTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToFunctions); for (const Function *f = o->firstFunction(); f; f = f->next) - *functionsTable++ = o->runtimeFunctionIndices->at(f->index); + *functionsTable++ = o->runtimeFunctionIndices.at(f->index); char *propertiesPtr = objectPtr + objectToWrite->offsetToProperties; for (const Property *p = o->firstProperty(); p; p = p->next) { @@ -1492,7 +1500,12 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) signalPtr += size; } - objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->aliasCount(), o->signalCount(), o->bindingCount()); + quint32 *namedObjectInComponentPtr = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToNamedObjectsInComponent); + for (int i = 0; i < o->namedObjectsInComponent.count; ++i) { + *namedObjectInComponentPtr++ = o->namedObjectsInComponent.at(i); + } + + objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->aliasCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count); objectPtr += signalTableSize; } @@ -1509,7 +1522,7 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output) return qmlUnit; } -char *QmlUnitGenerator::writeBindings(char *bindingPtr, Object *o, BindingFilter filter) const +char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const { for (const Binding *b = o->firstBinding(); b; b = b->next) { if (!(b->*(filter))()) @@ -1517,7 +1530,7 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, Object *o, BindingFilter QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr); *bindingToWrite = *b; if (b->type == QV4::CompiledData::Binding::Type_Script) - bindingToWrite->value.compiledScriptIndex = o->runtimeFunctionIndices->at(b->value.compiledScriptIndex); + bindingToWrite->value.compiledScriptIndex = o->runtimeFunctionIndices.at(b->value.compiledScriptIndex); bindingPtr += sizeof(QV4::CompiledData::Binding); } return bindingPtr; diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index c53004a331..db17c93222 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -170,7 +170,12 @@ class FixedPoolArray public: int count; - void init(QQmlJS::MemoryPool *pool, const QVector<T> &vector) + FixedPoolArray() + : data(0) + , count(0) + {} + + void allocate(QQmlJS::MemoryPool *pool, const QVector<T> &vector) { count = vector.count(); data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); @@ -183,6 +188,16 @@ public: } } + template <typename Container> + void allocate(QQmlJS::MemoryPool *pool, const Container &container) + { + count = container.count(); + data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); + typename Container::ConstIterator it = container.constBegin(); + for (int i = 0; i < count; ++i) + new (data + i) T(*it++); + } + const T &at(int index) const { Q_ASSERT(index >= 0 && index < count); return data[index]; @@ -274,9 +289,11 @@ struct Q_QML_PRIVATE_EXPORT Object Q_DECLARE_TR_FUNCTIONS(Object) public: quint32 inheritedTypeNameIndex; - quint32 idIndex; - int indexOfDefaultPropertyOrAlias : 31; - int defaultPropertyIsAlias : 1; + quint32 idNameIndex; + int id; + int indexOfDefaultPropertyOrAlias; + bool defaultPropertyIsAlias; + int flags; QV4::CompiledData::Location location; QV4::CompiledData::Location locationOfIdProperty; @@ -296,7 +313,7 @@ public: // specified object. Used for declarations inside group properties. Object *declarationsOverride; - void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int id, const QQmlJS::AST::SourceLocation &location = QQmlJS::AST::SourceLocation()); + void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &location = QQmlJS::AST::SourceLocation()); QString sanityCheckFunctionNames(const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation); @@ -312,7 +329,9 @@ public: QString bindingAsString(Document *doc, int scriptIndex) const; PoolList<CompiledFunctionOrExpression> *functionsAndExpressions; - FixedPoolArray<int> *runtimeFunctionIndices; + FixedPoolArray<int> runtimeFunctionIndices; + + FixedPoolArray<quint32> namedObjectsInComponent; private: friend struct IRLoader; @@ -467,7 +486,7 @@ struct Q_QML_PRIVATE_EXPORT QmlUnitGenerator private: typedef bool (Binding::*BindingFilter)() const; - char *writeBindings(char *bindingPtr, Object *o, BindingFilter filter) const; + char *writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const; }; #ifndef V4_BOOTSTRAP diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 0d98aa6e54..19beb13e45 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -65,16 +65,16 @@ QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlCompiledData * bool QQmlTypeCompiler::compile() { - compiledData->importCache = new QQmlTypeNameCache; + importCache = new QQmlTypeNameCache; foreach (const QString &ns, typeData->namespaces()) - compiledData->importCache->add(ns); + importCache->add(ns); // Add any Composite Singletons that were used to the import cache foreach (const QQmlTypeData::TypeReference &singleton, typeData->compositeSingletons()) - compiledData->importCache->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix); + importCache->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix); - typeData->imports().populateCache(compiledData->importCache); + typeData->imports().populateCache(importCache.data()); const QHash<int, QQmlTypeData::TypeReference> &resolvedTypes = typeData->resolvedTypeRefs(); for (QHash<int, QQmlTypeData::TypeReference>::ConstIterator resolvedType = resolvedTypes.constBegin(), end = resolvedTypes.constEnd(); @@ -137,8 +137,6 @@ bool QQmlTypeCompiler::compile() customParsers.insert(it.key(), customParser); } - compiledData->propertyCaches.reserve(document->objects.count()); - { QQmlPropertyCacheCreator propertyCacheBuilder(this); if (!propertyCacheBuilder.buildMetaObjects()) @@ -174,7 +172,8 @@ bool QQmlTypeCompiler::compile() // Collect imported scripts const QList<QQmlTypeData::ScriptReference> &scripts = typeData->resolvedScripts(); - compiledData->scripts.reserve(scripts.count()); + QVector<QQmlScriptData *> dependentScripts; + dependentScripts.reserve(scripts.count()); for (int scriptIndex = 0; scriptIndex < scripts.count(); ++scriptIndex) { const QQmlTypeData::ScriptReference &script = scripts.at(scriptIndex); @@ -187,10 +186,10 @@ bool QQmlTypeCompiler::compile() qualifier = qualifier.mid(lastDotIndex+1); } - compiledData->importCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); + importCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); QQmlScriptData *scriptData = script.script->scriptData(); scriptData->addref(); - compiledData->scripts << scriptData; + dependentScripts << scriptData; } // Resolve component boundaries and aliases @@ -202,6 +201,12 @@ bool QQmlTypeCompiler::compile() return false; } + { + QQmlDeferredAndCustomParserBindingScanner deferredAndCustomParserBindingScanner(this); + if (!deferredAndCustomParserBindingScanner.scanObject()) + return false; + } + // Compile JS binding expressions and signal handlers if (!document->javaScriptCompilationUnit) { { @@ -211,7 +216,7 @@ bool QQmlTypeCompiler::compile() sss.scan(); } - QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, compiledData->importCache, &document->jsGenerator.stringTable); + QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, importCache, &document->jsGenerator.stringTable); QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); if (!jsCodeGen.generateCodeForComponents()) return false; @@ -236,9 +241,12 @@ bool QQmlTypeCompiler::compile() document->javaScriptCompilationUnit->data = qmlUnit; compiledData->compilationUnit = document->javaScriptCompilationUnit; + compiledData->compilationUnit->propertyCaches = m_propertyCaches; + compiledData->compilationUnit->importCache = importCache; + compiledData->compilationUnit->dependentScripts = dependentScripts; // Add to type registry of composites - if (compiledData->isCompositeType()) + if (compiledData->compilationUnit->isCompositeType()) engine->registerInternalCompositeType(compiledData); else { const QV4::CompiledData::Object *obj = qmlUnit->objectAt(qmlUnit->indexOfRootObject); @@ -272,18 +280,18 @@ bool QQmlTypeCompiler::compile() } ++objectCount; if (typeRef->component) { - bindingCount += typeRef->component->totalBindingsCount; - parserStatusCount += typeRef->component->totalParserStatusCount; - objectCount += typeRef->component->totalObjectCount; + bindingCount += typeRef->component->compilationUnit->totalBindingsCount; + parserStatusCount += typeRef->component->compilationUnit->totalParserStatusCount; + objectCount += typeRef->component->compilationUnit->totalObjectCount; } } } - compiledData->totalBindingsCount = bindingCount; - compiledData->totalParserStatusCount = parserStatusCount; - compiledData->totalObjectCount = objectCount; + compiledData->compilationUnit->totalBindingsCount = bindingCount; + compiledData->compilationUnit->totalParserStatusCount = parserStatusCount; + compiledData->compilationUnit->totalObjectCount = objectCount; - Q_ASSERT(compiledData->propertyCaches.count() == static_cast<int>(compiledData->compilationUnit->data->nObjects)); + Q_ASSERT(compiledData->compilationUnit->propertyCaches.count() == static_cast<int>(compiledData->compilationUnit->data->nObjects)); return errors.isEmpty(); } @@ -337,28 +345,13 @@ int QQmlTypeCompiler::rootObjectIndex() const void QQmlTypeCompiler::setPropertyCaches(const QQmlPropertyCacheVector &caches) { - compiledData->propertyCaches = caches; + m_propertyCaches = caches; Q_ASSERT(caches.count() >= document->indexOfRootObject); } const QQmlPropertyCacheVector &QQmlTypeCompiler::propertyCaches() const { - return compiledData->propertyCaches; -} - -QHash<int, int> *QQmlTypeCompiler::objectIndexToIdForRoot() -{ - return &compiledData->objectIndexToIdForRoot; -} - -QHash<int, QHash<int, int> > *QQmlTypeCompiler::objectIndexToIdPerComponent() -{ - return &compiledData->objectIndexToIdPerComponent; -} - -QHash<int, QBitArray> *QQmlTypeCompiler::customParserBindings() -{ - return &compiledData->customParserBindings; + return m_propertyCaches; } QQmlJS::MemoryPool *QQmlTypeCompiler::memoryPool() @@ -376,11 +369,6 @@ const QV4::Compiler::StringTableGenerator *QQmlTypeCompiler::stringPool() const return &document->jsGenerator.stringTable; } -void QQmlTypeCompiler::setDeferredBindingsPerObject(const QHash<int, QBitArray> &deferredBindingsPerObject) -{ - compiledData->deferredBindingsPerObject = deferredBindingsPerObject; -} - void QQmlTypeCompiler::setBindingPropertyDataPerObject(const QVector<QV4::CompiledData::BindingPropertyData> &propertyData) { compiledData->compilationUnit->bindingPropertyDataPerObject = propertyData; @@ -1324,11 +1312,8 @@ QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *t , qmlObjects(typeCompiler->qmlObjects()) , indexOfRootObject(typeCompiler->rootObjectIndex()) , _componentIndex(-1) - , _objectIndexToIdInScope(0) , resolvedTypes(typeCompiler->resolvedTypes()) , propertyCaches(typeCompiler->propertyCaches()) - , objectIndexToIdForRoot(typeCompiler->objectIndexToIdForRoot()) - , objectIndexToIdPerComponent(typeCompiler->objectIndexToIdPerComponent()) { } @@ -1351,7 +1336,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI if (targetType->metaObject() == &QQmlComponent::staticMetaObject) continue; } else if (tr->component) { - if (tr->component->rootPropertyCache()->firstCppMetaObject() == &QQmlComponent::staticMetaObject) + if (tr->component->compilationUnit->rootPropertyCache()->firstCppMetaObject() == &QQmlComponent::staticMetaObject) continue; } @@ -1382,6 +1367,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>(); syntheticComponent->init(pool, compiler->registerString(QString::fromUtf8(componentType->typeName())), compiler->registerString(QString())); syntheticComponent->location = binding->valueLocation; + syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent; if (!resolvedTypes->contains(syntheticComponent->inheritedTypeNameIndex)) { QQmlCompiledData::TypeReference *typeRef = new QQmlCompiledData::TypeReference; @@ -1408,7 +1394,6 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI binding->value.objectIndex = componentIndex; componentRoots.append(componentIndex); - componentBoundaries.append(syntheticBinding->value.objectIndex); } } @@ -1420,7 +1405,7 @@ bool QQmlComponentAndAliasResolver::resolve() // on the left hand side is of QQmlComponent type. const int objCountWithoutSynthesizedComponents = qmlObjects->count(); for (int i = 0; i < objCountWithoutSynthesizedComponents; ++i) { - const QmlIR::Object *obj = qmlObjects->at(i); + QmlIR::Object *obj = qmlObjects->at(i); QQmlPropertyCache *cache = propertyCaches.at(i).data(); if (obj->inheritedTypeNameIndex == 0 && !cache) continue; @@ -1439,7 +1424,7 @@ bool QQmlComponentAndAliasResolver::resolve() continue; } - componentRoots.append(i); + obj->flags |= QV4::CompiledData::Object::IsComponent; if (obj->functionCount() > 0) COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions.")); @@ -1461,20 +1446,20 @@ bool QQmlComponentAndAliasResolver::resolve() if (rootBinding->next || rootBinding->type != QV4::CompiledData::Binding::Type_Object) COMPILE_EXCEPTION(obj, tr("Invalid component body specification")); - componentBoundaries.append(rootBinding->value.objectIndex); - } + // We are going to collect ids/aliases and resolve them for the root object as a separate + // last pass. + if (i != indexOfRootObject) + componentRoots.append(i); - std::sort(componentBoundaries.begin(), componentBoundaries.end()); + } for (int i = 0; i < componentRoots.count(); ++i) { - const QmlIR::Object *component = qmlObjects->at(componentRoots.at(i)); + QmlIR::Object *component = qmlObjects->at(componentRoots.at(i)); const QmlIR::Binding *rootBinding = component->firstBinding(); _componentIndex = i; _idToObjectIndex.clear(); - _objectIndexToIdInScope = &(*objectIndexToIdPerComponent)[componentRoots.at(i)]; - _objectsWithAliases.clear(); if (!collectIdsAndAliases(rootBinding->value.objectIndex)) @@ -1482,51 +1467,56 @@ bool QQmlComponentAndAliasResolver::resolve() if (!resolveAliases()) return false; + + component->namedObjectsInComponent.allocate(pool, _idToObjectIndex); } // Collect ids and aliases for root _componentIndex = -1; _idToObjectIndex.clear(); - _objectIndexToIdInScope = objectIndexToIdForRoot; _objectsWithAliases.clear(); collectIdsAndAliases(indexOfRootObject); resolveAliases(); + QmlIR::Object *rootComponent = qmlObjects->at(indexOfRootObject); + rootComponent->namedObjectsInComponent.allocate(pool, _idToObjectIndex); + // Implicit component insertion may have added objects and thus we also need // to extend the symmetric propertyCaches. compiler->setPropertyCaches(propertyCaches); + compiler->setComponentRoots(componentRoots); return true; } bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) { - const QmlIR::Object *obj = qmlObjects->at(objectIndex); + QmlIR::Object *obj = qmlObjects->at(objectIndex); - if (obj->idIndex != 0) { - if (_idToObjectIndex.contains(obj->idIndex)) { + if (obj->idNameIndex != 0) { + if (_idToObjectIndex.contains(obj->idNameIndex)) { recordError(obj->locationOfIdProperty, tr("id is not unique")); return false; } - _idToObjectIndex.insert(obj->idIndex, objectIndex); - _objectIndexToIdInScope->insert(objectIndex, _objectIndexToIdInScope->count()); + obj->id = _idToObjectIndex.count(); + _idToObjectIndex.insert(obj->idNameIndex, objectIndex); } if (obj->aliasCount() > 0) _objectsWithAliases.append(objectIndex); + // Stop at Component boundary + if (obj->flags & QV4::CompiledData::Object::IsComponent && objectIndex != compiler->rootObjectIndex()) + return true; + for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { if (binding->type != QV4::CompiledData::Binding::Type_Object && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty && binding->type != QV4::CompiledData::Binding::Type_GroupProperty) continue; - // Stop at Component boundary - if (std::binary_search(componentBoundaries.constBegin(), componentBoundaries.constEnd(), binding->value.objectIndex)) - continue; - if (!collectIdsAndAliases(binding->value.objectIndex)) return false; } @@ -1555,8 +1545,9 @@ bool QQmlComponentAndAliasResolver::resolveAliases() } Q_ASSERT(!(alias->flags & QV4::CompiledData::Alias::Resolved)); alias->flags |= QV4::CompiledData::Alias::Resolved; - Q_ASSERT(_objectIndexToIdInScope->contains(targetObjectIndex)); - alias->targetObjectId = _objectIndexToIdInScope->value(targetObjectIndex); + const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex); + Q_ASSERT(targetObject->id >= 0); + alias->targetObjectId = targetObject->id; const QString aliasPropertyValue = stringAt(alias->propertyNameIndex); @@ -1668,6 +1659,126 @@ bool QQmlComponentAndAliasResolver::resolveAliases() return true; } +QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler) + : QQmlCompilePass(typeCompiler) + , qmlObjects(typeCompiler->qmlObjects()) + , propertyCaches(typeCompiler->propertyCaches()) + , customParsers(typeCompiler->customParserCache()) + , _seenObjectWithId(false) +{ +} + +bool QQmlDeferredAndCustomParserBindingScanner::scanObject() +{ + return scanObject(compiler->rootObjectIndex()); +} + +bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) +{ + QmlIR::Object *obj = qmlObjects->at(objectIndex); + if (obj->idNameIndex != 0) + _seenObjectWithId = true; + + if (obj->flags & QV4::CompiledData::Object::IsComponent) { + Q_ASSERT(obj->bindingCount() == 1); + const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); + Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); + return scanObject(componentBinding->value.objectIndex); + } + + QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex).data(); + if (!propertyCache) + return true; + + QString defaultPropertyName; + QQmlPropertyData *defaultProperty = 0; + if (obj->indexOfDefaultPropertyOrAlias != -1) { + QQmlPropertyCache *cache = propertyCache->parent(); + defaultPropertyName = cache->defaultPropertyName(); + defaultProperty = cache->defaultProperty(); + } else { + defaultPropertyName = propertyCache->defaultPropertyName(); + defaultProperty = propertyCache->defaultProperty(); + } + + QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex); + + QmlIR::PropertyResolver propertyResolver(propertyCache); + + QStringList deferredPropertyNames; + { + const QMetaObject *mo = propertyCache->firstCppMetaObject(); + const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); + if (namesIndex != -1) { + QMetaClassInfo classInfo = mo->classInfo(namesIndex); + deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); + } + } + + for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { + QQmlPropertyData *pd = 0; + QString name = stringAt(binding->propertyNameIndex); + + if (customParser) { + if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) { + binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; + obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; + continue; + } + } else if (QmlIR::IRBuilder::isSignalPropertyName(name) + && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { + obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; + binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; + continue; + } + } + + if (name.isEmpty()) { + pd = defaultProperty; + name = defaultPropertyName; + } else { + if (name.constData()->isUpper()) + continue; + + bool notInRevision = false; + pd = propertyResolver.property(name, ¬InRevision, QmlIR::PropertyResolver::CheckRevision); + } + + bool seenSubObjectWithId = false; + + if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { + qSwap(_seenObjectWithId, seenSubObjectWithId); + const bool subObjectValid = scanObject(binding->value.objectIndex); + qSwap(_seenObjectWithId, seenSubObjectWithId); + if (!subObjectValid) + return false; + _seenObjectWithId |= seenSubObjectWithId; + } + + if (!seenSubObjectWithId + && !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) { + + binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding; + obj->flags |= QV4::CompiledData::Object::HasDeferredBindings; + } + + if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) + continue; + + if (!pd) { + if (customParser) { + obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings; + binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding; + } + } + } + + return true; +} + + QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler) : QQmlCompilePass(typeCompiler) , enginePrivate(typeCompiler->enginePrivate()) @@ -1675,9 +1786,6 @@ QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler) , resolvedTypes(*typeCompiler->resolvedTypes()) , customParsers(typeCompiler->customParserCache()) , propertyCaches(typeCompiler->propertyCaches()) - , objectIndexToIdPerComponent(*typeCompiler->objectIndexToIdPerComponent()) - , customParserBindingsPerObject(typeCompiler->customParserBindings()) - , _seenObjectWithId(false) { } @@ -1686,7 +1794,6 @@ bool QQmlPropertyValidator::validate() _bindingPropertyDataPerObject.resize(qmlUnit->nObjects); if (!validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0)) return false; - compiler->setDeferredBindingsPerObject(_deferredBindingsPerObject); compiler->setBindingPropertyDataPerObject(_bindingPropertyDataPerObject); return true; } @@ -1717,10 +1824,8 @@ struct BindingFinder bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const { const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex); - if (obj->idIndex != 0) - _seenObjectWithId = true; - if (isComponent(objectIndex)) { + if (obj->flags & QV4::CompiledData::Object::IsComponent) { Q_ASSERT(obj->nBindings == 1); const QV4::CompiledData::Binding *componentBinding = obj->bindingTable(); Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); @@ -1764,9 +1869,6 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD groupProperties.insert(pos, binding); } - QBitArray customParserBindings(obj->nBindings); - QBitArray deferredBindings; - QmlIR::PropertyResolver propertyResolver(propertyCache); QString defaultPropertyName; @@ -1790,13 +1892,11 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) { customBindings << binding; - customParserBindings.setBit(i); continue; } } else if (QmlIR::IRBuilder::isSignalPropertyName(name) && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { customBindings << binding; - customParserBindings.setBit(i); continue; } } @@ -1845,24 +1945,10 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD return false; } - bool seenSubObjectWithId = false; - if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { - qSwap(_seenObjectWithId, seenSubObjectWithId); const bool subObjectValid = validateObject(binding->value.objectIndex, binding, pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)); - qSwap(_seenObjectWithId, seenSubObjectWithId); if (!subObjectValid) return false; - _seenObjectWithId |= seenSubObjectWithId; - } - - if (!seenSubObjectWithId - && !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) { - - if (deferredBindings.isEmpty()) - deferredBindings.resize(obj->nBindings); - - deferredBindings.setBit(i); } // Signal handlers were resolved and checked earlier in the signal handler conversion pass. @@ -1947,7 +2033,6 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD } else { if (customParser) { customBindings << binding; - customParserBindings.setBit(i); continue; } if (bindingToDefaultProperty) { @@ -1958,7 +2043,7 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD } } - if (obj->idIndex) { + if (obj->idNameIndex) { bool notInRevision = false; collectedBindingPropertyData << propertyResolver.property(QStringLiteral("id"), ¬InRevision); } @@ -1972,7 +2057,6 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD customParser->validator = 0; customParser->engine = 0; customParser->imports = (QQmlImports*)0; - customParserBindingsPerObject->insert(objectIndex, customParserBindings); const QList<QQmlError> parserErrors = customParser->errors(); if (!parserErrors.isEmpty()) { foreach (const QQmlError &error, parserErrors) @@ -1981,9 +2065,6 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD } } - if (!deferredBindings.isEmpty()) - _deferredBindingsPerObject.insert(objectIndex, deferredBindings); - _bindingPropertyDataPerObject[objectIndex] = collectedBindingPropertyData; return true; @@ -2328,7 +2409,7 @@ bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, co } } return true; - } else if (isComponent(binding->value.objectIndex)) { + } else if (qmlUnit->objectAt(binding->value.objectIndex)->flags & QV4::CompiledData::Object::IsComponent) { return true; } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { return true; @@ -2365,7 +2446,6 @@ bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, co QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen) : QQmlCompilePass(typeCompiler) - , objectIndexToIdPerComponent(*typeCompiler->objectIndexToIdPerComponent()) , resolvedTypes(*typeCompiler->resolvedTypes()) , customParsers(typeCompiler->customParserCache()) , qmlObjects(*typeCompiler->qmlObjects()) @@ -2376,46 +2456,40 @@ QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR:: bool QQmlJSCodeGenerator::generateCodeForComponents() { - const QHash<int, QHash<int, int> > &objectIndexToIdPerComponent = *compiler->objectIndexToIdPerComponent(); - for (QHash<int, QHash<int, int> >::ConstIterator component = objectIndexToIdPerComponent.constBegin(), end = objectIndexToIdPerComponent.constEnd(); - component != end; ++component) { - if (!compileComponent(component.key(), component.value())) + const QVector<quint32> &componentRoots = compiler->componentRoots(); + for (int i = 0; i < componentRoots.count(); ++i) { + if (!compileComponent(componentRoots.at(i))) return false; } - return compileComponent(compiler->rootObjectIndex(), *compiler->objectIndexToIdForRoot()); + return compileComponent(compiler->rootObjectIndex()); } -bool QQmlJSCodeGenerator::compileComponent(int contextObject, const QHash<int, int> &objectIndexToId) +bool QQmlJSCodeGenerator::compileComponent(int contextObject) { - if (isComponent(contextObject)) { - const QmlIR::Object *component = qmlObjects.at(contextObject); - Q_ASSERT(component->bindingCount() == 1); - const QV4::CompiledData::Binding *componentBinding = component->firstBinding(); + const QmlIR::Object *obj = qmlObjects.at(contextObject); + if (obj->flags & QV4::CompiledData::Object::IsComponent) { + Q_ASSERT(obj->bindingCount() == 1); + const QV4::CompiledData::Binding *componentBinding = obj->firstBinding(); Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); contextObject = componentBinding->value.objectIndex; } QmlIR::JSCodeGen::ObjectIdMapping idMapping; - if (!objectIndexToId.isEmpty()) { - idMapping.reserve(objectIndexToId.count()); - - for (QHash<int, int>::ConstIterator idIt = objectIndexToId.constBegin(), end = objectIndexToId.constEnd(); - idIt != end; ++idIt) { - - const int objectIndex = idIt.key(); - QmlIR::JSCodeGen::IdMapping m; - const QmlIR::Object *obj = qmlObjects.at(objectIndex); - m.name = stringAt(obj->idIndex); - m.idIndex = idIt.value(); - m.type = propertyCaches.at(objectIndex).data(); + idMapping.reserve(obj->namedObjectsInComponent.count); + for (int i = 0; i < obj->namedObjectsInComponent.count; ++i) { + const int objectIndex = obj->namedObjectsInComponent.at(i); + QmlIR::JSCodeGen::IdMapping m; + const QmlIR::Object *obj = qmlObjects.at(objectIndex); + m.name = stringAt(obj->idNameIndex); + m.idIndex = obj->id; + m.type = propertyCaches.at(objectIndex).data(); - QQmlCompiledData::TypeReference *tref = resolvedTypes.value(obj->inheritedTypeNameIndex); - if (tref && tref->isFullyDynamicType) - m.type = 0; + QQmlCompiledData::TypeReference *tref = resolvedTypes.value(obj->inheritedTypeNameIndex); + if (tref && tref->isFullyDynamicType) + m.type = 0; - idMapping << m; - } + idMapping << m; } v4CodeGen->beginContextScope(idMapping, propertyCaches.at(contextObject).data()); @@ -2427,10 +2501,10 @@ bool QQmlJSCodeGenerator::compileComponent(int contextObject, const QHash<int, i bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex) { - if (isComponent(objectIndex)) + QmlIR::Object *object = qmlObjects.at(objectIndex); + if (object->flags & QV4::CompiledData::Object::IsComponent) return true; - QmlIR::Object *object = qmlObjects.at(objectIndex); if (object->functionsAndExpressions->count > 0) { QQmlPropertyCache *scopeObject = propertyCaches.at(scopeObjectIndex).data(); v4CodeGen->beginObjectScope(scopeObject); @@ -2451,8 +2525,7 @@ bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIn } QQmlJS::MemoryPool *pool = compiler->memoryPool(); - object->runtimeFunctionIndices = pool->New<QmlIR::FixedPoolArray<int> >(); - object->runtimeFunctionIndices->init(pool, runtimeFunctionIndices); + object->runtimeFunctionIndices.allocate(pool, runtimeFunctionIndices); } for (const QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) { @@ -2551,7 +2624,7 @@ void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBinding if (binding->type != QV4::CompiledData::Binding::Type_Script) continue; - const int irFunctionIndex = obj->runtimeFunctionIndices->at(binding->value.compiledScriptIndex); + const int irFunctionIndex = obj->runtimeFunctionIndices.at(binding->value.compiledScriptIndex); QV4::IR::Function *irFunction = jsModule->functions.at(irFunctionIndex); if (simplifyBinding(irFunction, binding)) { irFunctionsToRemove.append(irFunctionIndex); @@ -2838,10 +2911,8 @@ void QQmlIRFunctionCleanser::clean() } foreach (QmlIR::Object *obj, *compiler->qmlObjects()) { - if (!obj->runtimeFunctionIndices) - continue; - for (int i = 0; i < obj->runtimeFunctionIndices->count; ++i) - (*obj->runtimeFunctionIndices)[i] = newFunctionIndices[obj->runtimeFunctionIndices->at(i)]; + for (int i = 0; i < obj->runtimeFunctionIndices.count; ++i) + obj->runtimeFunctionIndices[i] = newFunctionIndices[obj->runtimeFunctionIndices.at(i)]; } } diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 968902dae1..5abfc7c18d 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -101,13 +101,11 @@ public: int rootObjectIndex() const; void setPropertyCaches(const QQmlPropertyCacheVector &caches); const QQmlPropertyCacheVector &propertyCaches() const; - QHash<int, int> *objectIndexToIdForRoot(); - QHash<int, QHash<int, int> > *objectIndexToIdPerComponent(); - QHash<int, QBitArray> *customParserBindings(); + void setComponentRoots(const QVector<quint32> &roots) { m_componentRoots = roots; } + const QVector<quint32> &componentRoots() const { return m_componentRoots; } QQmlJS::MemoryPool *memoryPool(); QStringRef newStringRef(const QString &string); const QV4::Compiler::StringTableGenerator *stringPool() const; - void setDeferredBindingsPerObject(const QHash<int, QBitArray> &deferredBindingsPerObject); void setBindingPropertyDataPerObject(const QVector<QV4::CompiledData::BindingPropertyData> &propertyData); const QHash<int, QQmlCustomParser*> &customParserCache() const { return customParsers; } @@ -119,9 +117,14 @@ private: QQmlEnginePrivate *engine; QQmlCompiledData *compiledData; QQmlTypeData *typeData; + QQmlRefPointer<QQmlTypeNameCache> importCache; QmlIR::Document *document; // index is string index of type name (use obj->inheritedTypeNameIndex) QHash<int, QQmlCustomParser*> customParsers; + + // index in first hash is component index, vector inside contains object indices of objects with id property + QVector<quint32> m_componentRoots; + QQmlPropertyCacheVector m_propertyCaches; }; struct QQmlCompilePass @@ -268,20 +271,31 @@ protected: const int indexOfRootObject; // indices of the objects that are actually Component {} - QVector<int> componentRoots; - // indices of objects that are the beginning of a new component - // scope. This is sorted and used for binary search. - QVector<quint32> componentBoundaries; + QVector<quint32> componentRoots; int _componentIndex; QHash<int, int> _idToObjectIndex; - QHash<int, int> *_objectIndexToIdInScope; QList<int> _objectsWithAliases; QHash<int, QQmlCompiledData::TypeReference*> *resolvedTypes; QQmlPropertyCacheVector propertyCaches; - QHash<int, int> *objectIndexToIdForRoot; - QHash<int, QHash<int, int> > *objectIndexToIdPerComponent; +}; + +class QQmlDeferredAndCustomParserBindingScanner : public QQmlCompilePass +{ +public: + QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler); + + bool scanObject(); + +private: + bool scanObject(int objectIndex); + + QList<QmlIR::Object*> *qmlObjects; + QQmlPropertyCacheVector propertyCaches; + const QHash<int, QQmlCustomParser*> &customParsers; + + bool _seenObjectWithId; }; class QQmlPropertyValidator : public QQmlCompilePass @@ -300,8 +314,6 @@ private: bool validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const; bool validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const; - bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); } - bool canCoerce(int to, QQmlPropertyCache *fromMo) const; QQmlEnginePrivate *enginePrivate; @@ -309,12 +321,8 @@ private: const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes; const QHash<int, QQmlCustomParser*> &customParsers; const QQmlPropertyCacheVector &propertyCaches; - const QHash<int, QHash<int, int> > objectIndexToIdPerComponent; - QHash<int, QBitArray> *customParserBindingsPerObject; // collected state variables, essentially write-only - mutable QHash<int, QBitArray> _deferredBindingsPerObject; - mutable bool _seenObjectWithId; mutable QVector<QV4::CompiledData::BindingPropertyData> _bindingPropertyDataPerObject; }; @@ -327,12 +335,9 @@ public: bool generateCodeForComponents(); private: - bool compileComponent(int componentRoot, const QHash<int, int> &objectIndexToId); + bool compileComponent(int componentRoot); bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex); - bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); } - - const QHash<int, QHash<int, int> > &objectIndexToIdPerComponent; const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes; const QHash<int, QQmlCustomParser*> &customParsers; const QList<QmlIR::Object*> &qmlObjects; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index ded2c1a9f8..dc29d1b941 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -47,6 +47,7 @@ #include <private/qv4lookup_p.h> #include <private/qv4regexpobject_p.h> #include <private/qqmlpropertycache_p.h> +#include <private/qqmltypeloader_p.h> #endif #include <private/qqmlirbuilder_p.h> #include <QCoreApplication> @@ -67,6 +68,9 @@ CompilationUnit::CompilationUnit() , runtimeLookups(0) , runtimeRegularExpressions(0) , runtimeClasses(0) + , totalBindingsCount(0) + , totalParserStatusCount(0) + , totalObjectCount(0) {} CompilationUnit::~CompilationUnit() @@ -165,6 +169,18 @@ void CompilationUnit::unlink() { if (engine) engine->compilationUnits.erase(engine->compilationUnits.find(this)); + + for (int ii = 0; ii < propertyCaches.count(); ++ii) + if (propertyCaches.at(ii).data()) + propertyCaches.at(ii)->release(); + propertyCaches.clear(); + + for (int ii = 0; ii < dependentScripts.count(); ++ii) + dependentScripts.at(ii)->release(); + dependentScripts.clear(); + + importCache = nullptr; + engine = 0; free(runtimeStrings); runtimeStrings = 0; @@ -189,6 +205,22 @@ void CompilationUnit::markObjects(QV4::ExecutionEngine *e) } } +IdentifierHash<int> CompilationUnit::namedObjectsPerComponent(int componentObjectIndex) +{ + auto it = namedObjectsPerComponentCache.find(componentObjectIndex); + if (it == namedObjectsPerComponentCache.end()) { + IdentifierHash<int> namedObjectCache(engine); + const CompiledData::Object *component = data->objectAt(componentObjectIndex); + const quint32 *namedObjectIndexPtr = component->namedObjectsInComponentTable(); + for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) { + const CompiledData::Object *namedObject = data->objectAt(*namedObjectIndexPtr); + namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id); + } + it = namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache); + } + return *it; +} + #endif // V4_BOOTSTRAP Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 3c2d895bff..8beb932fc8 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -60,11 +60,22 @@ #include <private/qv4executableallocator_p.h> #include <private/qqmlrefcount_p.h> #include <private/qqmlnullablevalue_p.h> +#include <private/qv4identifier_p.h> +#include <private/qflagpointer_p.h> +#ifndef V4_BOOTSTRAP +#include <private/qqmltypenamecache_p.h> +#endif QT_BEGIN_NAMESPACE class QQmlPropertyCache; class QQmlPropertyData; +class QQmlTypeNameCache; +class QQmlScriptData; + +// The vector is indexed by QV4::CompiledData::Object index and the flag +// indicates whether instantiation of the object requires a VME meta-object. +typedef QVector<QFlagPointer<QQmlPropertyCache>> QQmlPropertyCacheVector; namespace QmlIR { struct Document; @@ -234,7 +245,9 @@ struct Q_QML_PRIVATE_EXPORT Binding InitializerForReadOnlyDeclaration = 0x8, IsResolvedEnum = 0x10, IsListItem = 0x20, - IsBindingToAlias = 0x40 + IsBindingToAlias = 0x40, + IsDeferredBinding = 0x80, + IsCustomParserBinding = 0x100, }; quint32 flags : 16; @@ -391,13 +404,22 @@ struct Alias { struct Object { + enum Flags { + NoFlag = 0x0, + IsComponent = 0x1, // object was identified to be an explicit or implicit component boundary + HasDeferredBindings = 0x2, // any of the bindings are deferred + HasCustomParserBindings = 0x4 + }; + // Depending on the use, this may be the type name to instantiate before instantiating this // object. For grouped properties the type name will be empty and for attached properties // it will be the name of the attached type. quint32 inheritedTypeNameIndex; - quint32 idIndex; - qint32 indexOfDefaultPropertyOrAlias : 31; // -1 means no default property declared in this object + quint32 idNameIndex; + qint32 id : 16; + qint32 flags : 15; quint32 defaultPropertyIsAlias : 1; + qint32 indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object quint32 nFunctions; quint32 offsetToFunctions; quint32 nProperties; @@ -408,6 +430,8 @@ struct Object quint32 offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects quint32 nBindings; quint32 offsetToBindings; + quint32 nNamedObjectsInComponent; + quint32 offsetToNamedObjectsInComponent; Location location; Location locationOfIdProperty; // Function[] @@ -415,7 +439,7 @@ struct Object // Signal[] // Binding[] - static int calculateSizeExcludingSignals(int nFunctions, int nProperties, int nAliases, int nSignals, int nBindings) + static int calculateSizeExcludingSignals(int nFunctions, int nProperties, int nAliases, int nSignals, int nBindings, int nNamedObjectsInComponent) { return ( sizeof(Object) + nFunctions * sizeof(quint32) @@ -423,6 +447,7 @@ struct Object + nAliases * sizeof(Alias) + nSignals * sizeof(quint32) + nBindings * sizeof(Binding) + + nNamedObjectsInComponent * sizeof(int) + 0x7 ) & ~0x7; } @@ -453,6 +478,11 @@ struct Object const uint offset = offsetTable[idx]; return reinterpret_cast<const Signal*>(reinterpret_cast<const char*>(this) + offset); } + + const quint32 *namedObjectsInComponentTable() const + { + return reinterpret_cast<const quint32*>(reinterpret_cast<const char *>(this) + offsetToNamedObjectsInComponent); + } }; struct Import @@ -641,11 +671,29 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount QVector<QV4::Function *> runtimeFunctions; mutable QQmlNullableValue<QUrl> m_url; + // QML specific fields + QQmlPropertyCacheVector propertyCaches; + QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(data->indexOfRootObject).data(); } + bool isCompositeType() const { return propertyCaches.at(data->indexOfRootObject).flag(); } + + QQmlRefPointer<QQmlTypeNameCache> importCache; + // index is object index. This allows fast access to the // property data when initializing bindings, avoiding expensive // lookups by string (property name). QVector<BindingPropertyData> bindingPropertyDataPerObject; + // mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects + // this is initialized on-demand by QQmlContextData + QHash<int, IdentifierHash<int>> namedObjectsPerComponentCache; + IdentifierHash<int> namedObjectsPerComponent(int componentObjectIndex); + + int totalBindingsCount; // Number of bindings used in this type + int totalParserStatusCount; // Number of instantiated types that are QQmlParserStatus subclasses + int totalObjectCount; // Number of objects explicitly instantiated + + QVector<QQmlScriptData *> dependentScripts; + QV4::Function *linkToEngine(QV4::ExecutionEngine *engine); void unlink(); diff --git a/src/qml/debugger/qqmlabstractprofileradapter_p.h b/src/qml/debugger/qqmlabstractprofileradapter_p.h index 1104608055..8820c4311a 100644 --- a/src/qml/debugger/qqmlabstractprofileradapter_p.h +++ b/src/qml/debugger/qqmlabstractprofileradapter_p.h @@ -71,13 +71,13 @@ public: virtual ~QQmlAbstractProfilerAdapter() {} void setService(QQmlProfilerService *new_service) { service = new_service; } - virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages) = 0; + virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages, bool trackLocations) = 0; void startProfiling(quint64 features); void stopProfiling(); - void reportData() { emit dataRequested(); } + void reportData(bool trackLocations) { emit dataRequested(trackLocations); } void stopWaiting() { waiting = false; } void startWaiting() { waiting = true; } @@ -94,7 +94,7 @@ signals: void profilingDisabled(); void profilingDisabledWhileWaiting(); - void dataRequested(); + void dataRequested(bool trackLocations); void referenceTimeKnown(const QElapsedTimer &timer); protected: diff --git a/src/qml/debugger/qqmlprofiler.cpp b/src/qml/debugger/qqmlprofiler.cpp index 629d5cb7b8..ffba731b13 100644 --- a/src/qml/debugger/qqmlprofiler.cpp +++ b/src/qml/debugger/qqmlprofiler.cpp @@ -59,19 +59,21 @@ void QQmlProfiler::startProfiling(quint64 features) void QQmlProfiler::stopProfiling() { featuresEnabled = false; - reportData(); + reportData(true); + m_locations.clear(); } -void QQmlProfiler::reportData() +void QQmlProfiler::reportData(bool trackLocations) { LocationHash resolved; resolved.reserve(m_locations.size()); - for (auto it = m_locations.constBegin(), end = m_locations.constEnd(); it != end; ++it) - resolved.insert(it.key(), it.value()); - - // This unrefs all the objects. We have to make sure we do this in the GUI thread. Also, it's - // a good idea to release the memory before creating the packets to be sent. - m_locations.clear(); + for (auto it = m_locations.begin(), end = m_locations.end(); it != end; ++it) { + if (!trackLocations || !it->sent) { + resolved.insert(it.key(), it.value()); + if (trackLocations) + it->sent = true; + } + } QVector<QQmlProfilerData> data; data.swap(m_data); diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index 1380599fb7..60851ac444 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -146,26 +146,27 @@ public: // Unfortunately we have to resolve the locations right away because the QML context might not // be available anymore when we send the data. struct RefLocation : public Location { - RefLocation() : Location(), locationType(MaximumRangeType), ref(nullptr) + RefLocation() : Location(), locationType(MaximumRangeType), ref(nullptr), sent(false) {} RefLocation(QQmlBinding *binding, QV4::FunctionObject *function) : Location(function->sourceLocation()), locationType(Binding), - ref(new BindingRefCount(binding), QQmlRefPointer<QQmlRefCount>::Adopt) + ref(new BindingRefCount(binding), QQmlRefPointer<QQmlRefCount>::Adopt), sent(false) {} RefLocation(QQmlCompiledData *ref, const QUrl &url, const QV4::CompiledData::Object *obj, const QString &type) : Location(QQmlSourceLocation(type, obj->location.line, obj->location.column), url), - locationType(Creating), ref(ref) + locationType(Creating), ref(ref), sent(false) {} RefLocation(QQmlBoundSignalExpression *ref) : - Location(ref->sourceLocation()), locationType(HandlingSignal), ref(ref) + Location(ref->sourceLocation()), locationType(HandlingSignal), ref(ref), sent(false) {} RefLocation(QQmlDataBlob *ref) : - Location(QQmlSourceLocation(), ref->url()), locationType(Compiling), ref(ref) + Location(QQmlSourceLocation(), ref->url()), locationType(Compiling), ref(ref), + sent(false) {} bool isValid() const @@ -175,6 +176,7 @@ public: RangeType locationType; QQmlRefPointer<QQmlRefCount> ref; + bool sent; }; typedef QHash<quintptr, Location> LocationHash; @@ -217,11 +219,6 @@ public: location = RefLocation(expression); } - void startCreating() - { - m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(), 1 << RangeStart, Creating)); - } - void startCreating(const QV4::CompiledData::Object *obj) { m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(), @@ -233,10 +230,6 @@ public: const QUrl &url, const QString &type) { quintptr locationId(id(obj)); - m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(), - (1 << RangeLocation | 1 << RangeData), - Creating, locationId)); - RefLocation &location = m_locations[locationId]; if (!location.isValid()) location = RefLocation(ref, url, obj, type); @@ -261,7 +254,7 @@ public: public slots: void startProfiling(quint64 features); void stopProfiling(); - void reportData(); + void reportData(bool trackLocations); void setTimer(const QElapsedTimer &timer) { m_timer = timer; } signals: @@ -363,9 +356,10 @@ private: class QQmlObjectCreationProfiler { public: - QQmlObjectCreationProfiler(QQmlProfiler *profiler) : profiler(profiler) + QQmlObjectCreationProfiler(QQmlProfiler *profiler, const QV4::CompiledData::Object *obj) + : profiler(profiler) { - Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCreating, profiler, startCreating()); + Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCreating, profiler, startCreating(obj)); } ~QQmlObjectCreationProfiler() diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp index 626648067f..6260fd0cc8 100644 --- a/src/qml/jsruntime/qv4identifier.cpp +++ b/src/qml/jsruntime/qv4identifier.cpp @@ -64,12 +64,33 @@ IdentifierHashData::IdentifierHashData(int numBits) memset(entries, 0, alloc*sizeof(IdentifierHashEntry)); } +IdentifierHashData::IdentifierHashData(IdentifierHashData *other) + : size(other->size) + , numBits(other->numBits) + , identifierTable(other->identifierTable) +{ + refCount.store(1); + alloc = other->alloc; + entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); + memcpy(entries, other->entries, alloc*sizeof(IdentifierHashEntry)); +} + IdentifierHashBase::IdentifierHashBase(ExecutionEngine *engine) { d = new IdentifierHashData(3); d->identifierTable = engine->identifierTable; } +void IdentifierHashBase::detach() +{ + if (!d || d->refCount == 1) + return; + IdentifierHashData *newData = new IdentifierHashData(d); + if (d && !d->refCount.deref()) + delete d; + d = newData; +} + IdentifierHashEntry *IdentifierHashBase::addEntry(const Identifier *identifier) { diff --git a/src/qml/jsruntime/qv4identifier_p.h b/src/qml/jsruntime/qv4identifier_p.h index a3abfd8e13..2695bbc875 100644 --- a/src/qml/jsruntime/qv4identifier_p.h +++ b/src/qml/jsruntime/qv4identifier_p.h @@ -85,6 +85,7 @@ struct IdentifierHashEntry { struct IdentifierHashData { IdentifierHashData(int numBits); + explicit IdentifierHashData(IdentifierHashData *other); ~IdentifierHashData() { free(entries); } @@ -115,6 +116,8 @@ struct IdentifierHashBase bool contains(const QString &str) const; bool contains(String *str) const; + void detach(); + protected: IdentifierHashEntry *addEntry(const Identifier *i); const IdentifierHashEntry *lookup(const Identifier *identifier) const; @@ -141,6 +144,7 @@ struct IdentifierHash : public IdentifierHashBase } void add(const QString &str, const T &value); + void add(Heap::String *str, const T &value); inline T value(const QString &str) const; inline T value(String *str) const; @@ -198,6 +202,13 @@ void IdentifierHash<T>::add(const QString &str, const T &value) } template<typename T> +void IdentifierHash<T>::add(Heap::String *str, const T &value) +{ + IdentifierHashEntry *e = addEntry(toIdentifier(str)); + e->value = value; +} + +template<typename T> inline T IdentifierHash<T>::value(const QString &str) const { return IdentifierHashEntry::get(lookup(str), (T*)0); @@ -223,7 +234,6 @@ QString IdentifierHash<T>::findId(T value) const return QString(); } - } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp index 72475c5416..349ec48e06 100644 --- a/src/qml/jsruntime/qv4profiling.cpp +++ b/src/qml/jsruntime/qv4profiling.cpp @@ -78,7 +78,8 @@ Profiler::Profiler(QV4::ExecutionEngine *engine) : featuresEnabled(0), m_engine( void Profiler::stopProfiling() { featuresEnabled = 0; - reportData(); + reportData(true); + m_sentLocations.clear(); } bool operator<(const FunctionCall &call1, const FunctionCall &call2) @@ -88,7 +89,7 @@ bool operator<(const FunctionCall &call1, const FunctionCall &call2) (call1.m_end == call2.m_end && call1.m_function < call2.m_function))); } -void Profiler::reportData() +void Profiler::reportData(bool trackLocations) { std::sort(m_data.begin(), m_data.end()); QVector<FunctionCallProperties> properties; @@ -97,9 +98,15 @@ void Profiler::reportData() foreach (const FunctionCall &call, m_data) { properties.append(call.properties()); - FunctionLocation &location = locations[properties.constLast().id]; - if (!location.isValid()) - location = call.resolveLocation(); + Function *function = call.function(); + SentMarker &marker = m_sentLocations[reinterpret_cast<quintptr>(function)]; + if (!trackLocations || !marker.isValid()) { + FunctionLocation &location = locations[properties.constLast().id]; + if (!location.isValid()) + location = call.resolveLocation(); + if (trackLocations) + marker.setFunction(function); + } } emit dataReady(locations, properties, m_memory_data); diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index bb128b7cf3..01fdf2951e 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -134,6 +134,11 @@ public: return *this; } + Function *function() const + { + return m_function; + } + FunctionLocation resolveLocation() const; FunctionCallProperties properties() const; @@ -165,6 +170,46 @@ class Q_QML_EXPORT Profiler : public QObject { Q_OBJECT Q_DISABLE_COPY(Profiler) public: + struct SentMarker { + SentMarker() : m_function(nullptr) {} + + SentMarker(const SentMarker &other) : m_function(other.m_function) + { + if (m_function) + m_function->compilationUnit->addref(); + } + + ~SentMarker() + { + if (m_function) + m_function->compilationUnit->release(); + } + + SentMarker &operator=(const SentMarker &other) + { + if (&other != this) { + if (m_function) + m_function->compilationUnit->release(); + m_function = other.m_function; + m_function->compilationUnit->addref(); + } + return *this; + } + + void setFunction(Function *function) + { + Q_ASSERT(m_function == nullptr); + m_function = function; + m_function->compilationUnit->addref(); + } + + bool isValid() const + { return m_function != nullptr; } + + private: + Function *m_function; + }; + Profiler(QV4::ExecutionEngine *engine); size_t trackAlloc(size_t size, MemoryType type) @@ -186,7 +231,7 @@ public: public slots: void stopProfiling(); void startProfiling(quint64 features); - void reportData(); + void reportData(bool trackLocations); void setTimer(const QElapsedTimer &timer) { m_timer = timer; } signals: @@ -199,6 +244,7 @@ private: QElapsedTimer m_timer; QVector<FunctionCall> m_data; QVector<MemoryAllocationProperties> m_memory_data; + QHash<quintptr, SentMarker> m_sentLocations; friend class FunctionCallProfiler; }; @@ -237,6 +283,7 @@ Q_DECLARE_TYPEINFO(QV4::Profiling::MemoryAllocationProperties, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCallProperties, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCall, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionLocation, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QV4::Profiling::Profiler::SentMarker, Q_MOVABLE_TYPE); QT_END_NAMESPACE Q_DECLARE_METATYPE(QV4::Profiling::FunctionLocationHash) diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index fe94a11082..4fb2acebc5 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -68,7 +68,7 @@ #include <pthread_np.h> #endif -#define MIN_UNMANAGED_HEAPSIZE_GC_LIMIT (std::size_t)128*1024 +#define MIN_UNMANAGED_HEAPSIZE_GC_LIMIT std::size_t(128 * 1024) using namespace WTF; @@ -108,29 +108,20 @@ using namespace QV4; struct MemoryManager::Data { + const size_t pageSize; + struct ChunkHeader { Heap::Base freeItems; ChunkHeader *nextNonFull; char *itemStart; char *itemEnd; - int itemSize; + unsigned itemSize; }; - bool gcBlocked; - bool aggressiveGC; - bool gcStats; ExecutionEngine *engine; - enum { MaxItemSize = 512 }; - ChunkHeader *nonFullChunks[MaxItemSize/16]; - uint nChunks[MaxItemSize/16]; - uint availableItems[MaxItemSize/16]; - uint allocCount[MaxItemSize/16]; - int totalItems; - int totalAlloc; - uint maxShift; std::size_t maxChunkSize; - QVector<PageAllocation> heapChunks; + std::vector<PageAllocation> heapChunks; std::size_t unmanagedHeapSize; // the amount of bytes of heap that is not managed by the memory manager, but which is held onto by managed items. std::size_t unmanagedHeapSizeGCLimit; @@ -147,24 +138,39 @@ struct MemoryManager::Data LargeItem *largeItems; std::size_t totalLargeItemsAllocated; + enum { MaxItemSize = 512 }; + ChunkHeader *nonFullChunks[MaxItemSize/16]; + uint nChunks[MaxItemSize/16]; + uint availableItems[MaxItemSize/16]; + uint allocCount[MaxItemSize/16]; + int totalItems; + int totalAlloc; + uint maxShift; + + bool gcBlocked; + bool aggressiveGC; + bool gcStats; + bool unused; // suppress padding warning + // statistics: #ifdef DETAILED_MM_STATS QVector<unsigned> allocSizeCounters; #endif // DETAILED_MM_STATS Data() - : gcBlocked(false) - , aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC")) - , gcStats(!qEnvironmentVariableIsEmpty("QV4_MM_STATS")) + : pageSize(WTF::pageSize()) , engine(0) - , totalItems(0) - , totalAlloc(0) - , maxShift(maxShiftValue()) , maxChunkSize(maxChunkSizeValue()) , unmanagedHeapSize(0) , unmanagedHeapSizeGCLimit(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT) , largeItems(0) , totalLargeItemsAllocated(0) + , totalItems(0) + , totalAlloc(0) + , maxShift(maxShiftValue()) + , gcBlocked(false) + , aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC")) + , gcStats(!qEnvironmentVariableIsEmpty("QV4_MM_STATS")) { memset(nonFullChunks, 0, sizeof(nonFullChunks)); memset(nChunks, 0, sizeof(nChunks)); @@ -174,7 +180,7 @@ struct MemoryManager::Data ~Data() { - for (QVector<PageAllocation>::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) { + for (std::vector<PageAllocation>::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) { Q_V4_PROFILE_DEALLOC(engine, 0, i->size(), Profiling::HeapPage); i->deallocate(); } @@ -198,7 +204,7 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec // qDebug("chunk @ %p, in use: %s, mark bit: %s", // item, (m->inUse() ? "yes" : "no"), (m->isMarked() ? "true" : "false")); - Q_ASSERT((qintptr) item % 16 == 0); + Q_ASSERT(qintptr(item) % 16 == 0); if (m->isMarked()) { Q_ASSERT(m->inUse()); @@ -324,14 +330,15 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize if (shift > m_d->maxShift) shift = m_d->maxShift; std::size_t allocSize = m_d->maxChunkSize*(size_t(1) << shift); - allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize); + allocSize = roundUpToMultipleOf(m_d->pageSize, allocSize); PageAllocation allocation = PageAllocation::allocate( Q_V4_PROFILE_ALLOC(engine, allocSize, Profiling::HeapPage), OSAllocator::JSGCHeapPages); - m_d->heapChunks.append(allocation); + m_d->heapChunks.push_back(allocation); header = reinterpret_cast<Data::ChunkHeader *>(allocation.base()); - header->itemSize = int(size); + Q_ASSERT(size <= UINT_MAX); + header->itemSize = unsigned(size); header->itemStart = reinterpret_cast<char *>(allocation.base()) + roundUpToMultipleOf(16, sizeof(Data::ChunkHeader)); header->itemEnd = reinterpret_cast<char *>(allocation.base()) + allocation.size() - header->itemSize; @@ -347,7 +354,8 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize } last->setNextFree(0); m = header->freeItems.nextFree(); - const size_t increase = (header->itemEnd - header->itemStart) / header->itemSize; + Q_ASSERT(header->itemEnd >= header->itemStart); + const size_t increase = quintptr(header->itemEnd - header->itemStart) / header->itemSize; m_d->availableItems[pos] += uint(increase); m_d->totalItems += int(increase); #ifdef V4_USE_VALGRIND @@ -465,22 +473,23 @@ void MemoryManager::sweep(bool lastSweep) } } - bool *chunkIsEmpty = (bool *)alloca(m_d->heapChunks.size() * sizeof(bool)); + bool *chunkIsEmpty = static_cast<bool *>(alloca(m_d->heapChunks.size() * sizeof(bool))); uint itemsInUse[MemoryManager::Data::MaxItemSize/16]; memset(itemsInUse, 0, sizeof(itemsInUse)); memset(m_d->nonFullChunks, 0, sizeof(m_d->nonFullChunks)); - for (int i = 0; i < m_d->heapChunks.size(); ++i) { + for (size_t i = 0; i < m_d->heapChunks.size(); ++i) { Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(m_d->heapChunks[i].base()); chunkIsEmpty[i] = sweepChunk(header, &itemsInUse[header->itemSize >> 4], engine, &m_d->unmanagedHeapSize); } - QVector<PageAllocation>::iterator chunkIter = m_d->heapChunks.begin(); - for (int i = 0; i < m_d->heapChunks.size(); ++i) { + std::vector<PageAllocation>::iterator chunkIter = m_d->heapChunks.begin(); + for (size_t i = 0; i < m_d->heapChunks.size(); ++i) { Q_ASSERT(chunkIter != m_d->heapChunks.end()); Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(chunkIter->base()); const size_t pos = header->itemSize >> 4; - const size_t decrease = (header->itemEnd - header->itemStart) / header->itemSize; + Q_ASSERT(header->itemEnd >= header->itemStart); + const size_t decrease = quintptr(header->itemEnd - header->itemStart) / header->itemSize; // Release that chunk if it could have been spared since the last GC run without any difference. if (chunkIsEmpty[i] && m_d->availableItems[pos] - decrease >= itemsInUse[pos]) { @@ -561,7 +570,7 @@ void MemoryManager::runGC() t.restart(); const size_t usedBefore = getUsedMem(); const size_t largeItemsBefore = getLargeItemsMem(); - int chunksBefore = m_d->heapChunks.size(); + size_t chunksBefore = m_d->heapChunks.size(); sweep(); const size_t usedAfter = getUsedMem(); const size_t largeItemsAfter = getLargeItemsMem(); @@ -589,11 +598,11 @@ void MemoryManager::runGC() size_t MemoryManager::getUsedMem() const { size_t usedMem = 0; - for (QVector<PageAllocation>::const_iterator i = m_d->heapChunks.cbegin(), ei = m_d->heapChunks.cend(); i != ei; ++i) { + for (std::vector<PageAllocation>::const_iterator i = m_d->heapChunks.cbegin(), ei = m_d->heapChunks.cend(); i != ei; ++i) { Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(i->base()); for (char *item = header->itemStart; item <= header->itemEnd; item += header->itemSize) { Heap::Base *m = reinterpret_cast<Heap::Base *>(item); - Q_ASSERT((qintptr) item % 16 == 0); + Q_ASSERT(qintptr(item) % 16 == 0); if (m->inUse()) usedMem += header->itemSize; } @@ -604,7 +613,7 @@ size_t MemoryManager::getUsedMem() const size_t MemoryManager::getAllocatedMem() const { size_t total = 0; - for (int i = 0; i < m_d->heapChunks.size(); ++i) + for (size_t i = 0; i < m_d->heapChunks.size(); ++i) total += m_d->heapChunks.at(i).size(); return total; } diff --git a/src/qml/qml/qqmlcompileddata.cpp b/src/qml/qml/qqmlcompileddata.cpp index 1037b5da51..b25792054c 100644 --- a/src/qml/qml/qqmlcompileddata.cpp +++ b/src/qml/qml/qqmlcompileddata.cpp @@ -55,8 +55,7 @@ QT_BEGIN_NAMESPACE QQmlCompiledData::QQmlCompiledData(QQmlEngine *engine) -: engine(engine), importCache(0), metaTypeId(-1), listMetaTypeId(-1), isRegisteredWithEngine(false), - totalBindingsCount(0), totalParserStatusCount(0) +: engine(engine), metaTypeId(-1), listMetaTypeId(-1), isRegisteredWithEngine(false) { Q_ASSERT(engine); } @@ -85,16 +84,6 @@ QQmlCompiledData::~QQmlCompiledData() } qDeleteAll(resolvedTypes); resolvedTypes.clear(); - - for (int ii = 0; ii < scripts.count(); ++ii) - scripts.at(ii)->release(); - - if (importCache) - importCache->release(); - - for (int ii = 0; ii < propertyCaches.count(); ++ii) - if (propertyCaches.at(ii).data()) - propertyCaches.at(ii)->release(); } void QQmlCompiledData::clear() @@ -109,7 +98,7 @@ QQmlPropertyCache *QQmlCompiledData::TypeReference::propertyCache() const if (type) return typePropertyCache; else - return component->rootPropertyCache(); + return component->compilationUnit->rootPropertyCache(); } /*! @@ -124,7 +113,7 @@ QQmlPropertyCache *QQmlCompiledData::TypeReference::createPropertyCache(QQmlEngi typePropertyCache->addref(); return typePropertyCache; } else { - return component->rootPropertyCache(); + return component->compilationUnit->rootPropertyCache(); } } @@ -146,7 +135,7 @@ void QQmlCompiledData::TypeReference::doDynamicTypeCheck() else if (type) mo = type->metaObject(); else if (component) - mo = component->rootPropertyCache()->firstCppMetaObject(); + mo = component->compilationUnit->rootPropertyCache()->firstCppMetaObject(); isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo); } diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h index c2b244f5e3..2c9e04d5e0 100644 --- a/src/qml/qml/qqmlcompiler_p.h +++ b/src/qml/qml/qqmlcompiler_p.h @@ -80,10 +80,6 @@ class QQmlComponent; class QQmlContext; class QQmlContextData; -// The vector is indexed by QV4::CompiledData::Object index and the flag -// indicates whether instantiation of the object requires a VME meta-object. -typedef QVector<QFlagPointer<QQmlPropertyCache>> QQmlPropertyCacheVector; - // ### Merge with QV4::CompiledData::CompilationUnit class Q_AUTOTEST_EXPORT QQmlCompiledData : public QQmlRefCount, public QQmlCleanup { @@ -93,8 +89,6 @@ public: QQmlEngine *engine; - QQmlTypeNameCache *importCache; - int metaTypeId; int listMetaTypeId; bool isRegisteredWithEngine; @@ -126,23 +120,7 @@ public: // map from name index QHash<int, TypeReference*> resolvedTypes; - QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(compilationUnit->data->indexOfRootObject).data(); } - QQmlPropertyCacheVector propertyCaches; - QList<QQmlScriptData *> scripts; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; - // index in first hash is component index, hash inside maps from object index in that scope to integer id - QHash<int, QHash<int, int> > objectIndexToIdPerComponent; - QHash<int, int> objectIndexToIdForRoot; - // hash key is object index, value is indicies of bindings covered by custom parser - QHash<int, QBitArray> customParserBindings; - QHash<int, QBitArray> deferredBindingsPerObject; // index is object index - int totalBindingsCount; // Number of bindings used in this type - int totalParserStatusCount; // Number of instantiated types that are QQmlParserStatus subclasses - int totalObjectCount; // Number of objects explicitly instantiated - - bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); } - bool isCompositeType() const { return propertyCaches.at(compilationUnit->data->indexOfRootObject).flag(); } bool isInitialized() const { return hasEngine(); } void initialize(QQmlEngine *); diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index af06405376..b3081ddbe6 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -310,7 +310,7 @@ void QQmlContext::setContextProperty(const QString &name, const QVariant &value) } } - QV4::IdentifierHash<int> &properties = data->propertyNames(); + QV4::IdentifierHash<int> &properties = data->detachedPropertyNames(); int idx = properties.value(name); if (idx == -1) { properties.add(name, data->idValueCount + d->propertyValues.count()); @@ -346,7 +346,7 @@ void QQmlContext::setContextProperty(const QString &name, QObject *value) return; } - QV4::IdentifierHash<int> &properties = data->propertyNames(); + QV4::IdentifierHash<int> &properties = data->detachedPropertyNames(); int idx = properties.value(name); if (idx == -1) { @@ -523,7 +523,7 @@ QQmlContextData::QQmlContextData() QQmlContextData::QQmlContextData(QQmlContext *ctxt) : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false), isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false), - publicContext(ctxt), activeVMEData(0), + publicContext(ctxt), activeVMEData(0), componentObjectIndex(-1), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0), expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0), componentAttached(0) @@ -760,15 +760,6 @@ void QQmlContextData::setIdProperty(int idx, QObject *obj) idValues[idx].context = this; } -void QQmlContextData::setIdPropertyData(const QHash<int, int> &data) -{ - Q_ASSERT(objectIndexToId.isEmpty()); - objectIndexToId = data; - Q_ASSERT(propertyNameCache.isEmpty()); - idValueCount = data.count(); - idValues = new ContextGuard[idValueCount]; -} - QString QQmlContextData::findObjectId(const QObject *obj) const { const QV4::IdentifierHash<int> &properties = propertyNames(); @@ -804,21 +795,33 @@ QQmlContextPrivate *QQmlContextData::asQQmlContextPrivate() return QQmlContextPrivate::get(asQQmlContext()); } -QV4::IdentifierHash<int> &QQmlContextData::propertyNames() const +void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, int subComponentIndex) +{ + typeCompilationUnit = unit; + componentObjectIndex = subComponentIndex == -1 ? typeCompilationUnit->data->indexOfRootObject : subComponentIndex; + Q_ASSERT(!idValues); + idValueCount = typeCompilationUnit->data->objectAt(componentObjectIndex)->nNamedObjectsInComponent; + idValues = new ContextGuard[idValueCount]; +} + +const QV4::IdentifierHash<int> &QQmlContextData::propertyNames() const { if (propertyNameCache.isEmpty()) { - propertyNameCache = QV4::IdentifierHash<int>(QV8Engine::getV4(engine->handle())); - for (QHash<int, int>::ConstIterator it = objectIndexToId.cbegin(), end = objectIndexToId.cend(); - it != end; ++it) { - const QV4::CompiledData::Object *obj = typeCompilationUnit->data->objectAt(it.key()); - const QString name = typeCompilationUnit->data->stringAt(obj->idIndex); - propertyNameCache.add(name, it.value()); - } - objectIndexToId.clear(); + if (typeCompilationUnit) + propertyNameCache = typeCompilationUnit->namedObjectsPerComponent(componentObjectIndex); + else + propertyNameCache = QV4::IdentifierHash<int>(QV8Engine::getV4(engine)); } return propertyNameCache; } +QV4::IdentifierHash<int> &QQmlContextData::detachedPropertyNames() +{ + propertyNames(); + propertyNameCache.detach(); + return propertyNameCache; +} + QUrl QQmlContextData::url() const { if (typeCompilationUnit) diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h index 48d596418d..05ce39401e 100644 --- a/src/qml/qml/qqmlcontext_p.h +++ b/src/qml/qml/qqmlcontext_p.h @@ -151,9 +151,15 @@ public: // Compilation unit for contexts that belong to a compiled type. QQmlRefPointer<QV4::CompiledData::CompilationUnit> typeCompilationUnit; - mutable QHash<int, int> objectIndexToId; + // object index in CompiledData::Unit to component that created this context + int componentObjectIndex; + + void initFromTypeCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, int subComponentIndex); + + // flag indicates whether the context owns the cache (after mutation) or not. mutable QV4::IdentifierHash<int> propertyNameCache; - QV4::IdentifierHash<int> &propertyNames() const; + const QV4::IdentifierHash<int> &propertyNames() const; + QV4::IdentifierHash<int> &detachedPropertyNames(); // Context object QObject *contextObject; @@ -201,7 +207,6 @@ public: ContextGuard *idValues; int idValueCount; void setIdProperty(int, QObject *); - void setIdPropertyData(const QHash<int, int> &); // Linked contexts. this owns linkedContext. QQmlContextData *linkedContext; diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h index d8e25c55fd..9aa052c1ad 100644 --- a/src/qml/qml/qqmlcustomparser_p.h +++ b/src/qml/qml/qqmlcustomparser_p.h @@ -82,7 +82,7 @@ public: Flags flags() const { return m_flags; } virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &) = 0; - virtual void applyBindings(QObject *, QQmlCompiledData *, const QList<const QV4::CompiledData::Binding *> &) = 0; + virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &) = 0; QList<QQmlError> errors() const { return exceptions; } diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 2baca619cc..fd1795499e 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -2227,7 +2227,7 @@ QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const Locker locker(this); QHash<int, QQmlCompiledData *>::ConstIterator iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return QQmlMetaObject((*iter)->rootPropertyCache()); + return QQmlMetaObject((*iter)->compilationUnit->rootPropertyCache()); } else { QQmlType *type = QQmlMetaType::qmlType(t); return QQmlMetaObject(type?type->baseMetaObject():0); @@ -2239,7 +2239,7 @@ QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const Locker locker(this); QHash<int, QQmlCompiledData *>::ConstIterator iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return QQmlMetaObject((*iter)->rootPropertyCache()); + return QQmlMetaObject((*iter)->compilationUnit->rootPropertyCache()); } else { QQmlType *type = QQmlMetaType::qmlType(t); return QQmlMetaObject(type?type->metaObject():0); @@ -2251,7 +2251,7 @@ QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t) Locker locker(this); QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return (*iter)->rootPropertyCache(); + return (*iter)->compilationUnit->rootPropertyCache(); } else { QQmlType *type = QQmlMetaType::qmlType(t); locker.unlock(); @@ -2264,7 +2264,7 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t) Locker locker(this); QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.constFind(t); if (iter != m_compositeTypes.cend()) { - return (*iter)->rootPropertyCache(); + return (*iter)->compilationUnit->rootPropertyCache(); } else { QQmlType *type = QQmlMetaType::qmlType(t); locker.unlock(); @@ -2274,7 +2274,7 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t) void QQmlEnginePrivate::registerInternalCompositeType(QQmlCompiledData *data) { - QByteArray name = data->rootPropertyCache()->className(); + QByteArray name = data->compilationUnit->rootPropertyCache()->className(); QByteArray ptr = name + '*'; QByteArray lst = "QQmlListProperty<" + name + '>'; diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index f3f4c41775..1b3997baae 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -494,7 +494,7 @@ QQmlType *QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const if (!td || !td->isComplete()) return 0; QQmlCompiledData *cd = td->compiledData(); - const QMetaObject *mo = cd->rootPropertyCache()->firstCppMetaObject(); + const QMetaObject *mo = cd->compilationUnit->rootPropertyCache()->firstCppMetaObject(); return QQmlMetaType::qmlType(mo); } diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 86693af3d3..dcd35bccad 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -73,7 +73,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompile : phase(Startup) , compiledData(compiledData) , resolvedTypes(compiledData->resolvedTypes) - , propertyCaches(compiledData->propertyCaches) + , propertyCaches(compiledData->compilationUnit->propertyCaches) , activeVMEDataForRootContext(activeVMEDataForRootContext) { init(parentContext); @@ -81,23 +81,23 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompile sharedState = new QQmlObjectCreatorSharedState; topLevelCreator = true; sharedState->componentAttached = 0; - sharedState->allCreatedBindings.allocate(compiledData->totalBindingsCount); - sharedState->allParserStatusCallbacks.allocate(compiledData->totalParserStatusCount); - sharedState->allCreatedObjects.allocate(compiledData->totalObjectCount); + sharedState->allCreatedBindings.allocate(compiledData->compilationUnit->totalBindingsCount); + sharedState->allParserStatusCallbacks.allocate(compiledData->compilationUnit->totalParserStatusCount); + sharedState->allCreatedObjects.allocate(compiledData->compilationUnit->totalObjectCount); sharedState->allJavaScriptObjects = 0; sharedState->creationContext = creationContext; sharedState->rootContext = 0; QQmlProfiler *profiler = QQmlEnginePrivate::get(engine)->profiler; Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, profiler, - sharedState->profiler.init(profiler, compiledData->totalParserStatusCount)); + sharedState->profiler.init(profiler, compiledData->compilationUnit->totalParserStatusCount)); } QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData, QQmlObjectCreatorSharedState *inheritedSharedState) : phase(Startup) , compiledData(compiledData) , resolvedTypes(compiledData->resolvedTypes) - , propertyCaches(compiledData->propertyCaches) + , propertyCaches(compiledData->compilationUnit->propertyCaches) , activeVMEDataForRootContext(0) { init(parentContext); @@ -158,19 +158,17 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI int objectToCreate; if (subComponentIndex == -1) { - objectIndexToId = compiledData->objectIndexToIdForRoot; objectToCreate = qmlUnit->indexOfRootObject; } else { - objectIndexToId = compiledData->objectIndexToIdPerComponent[subComponentIndex]; const QV4::CompiledData::Object *compObj = qmlUnit->objectAt(subComponentIndex); objectToCreate = compObj->bindingTable()->value.objectIndex; } context = new QQmlContextData; context->isInternal = true; - context->imports = compiledData->importCache; + context->imports = compiledData->compilationUnit->importCache; context->imports->addref(); - context->typeCompilationUnit = compiledData->compilationUnit; + context->initFromTypeCompilationUnit(compiledData->compilationUnit, subComponentIndex); context->setParent(parentContext); if (!sharedState->rootContext) { @@ -183,16 +181,14 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI Q_ASSERT(sharedState->allJavaScriptObjects || topLevelCreator); if (topLevelCreator) - sharedState->allJavaScriptObjects = scope.alloc(compiledData->totalObjectCount); + sharedState->allJavaScriptObjects = scope.alloc(compiledData->compilationUnit->totalObjectCount); - context->setIdPropertyData(objectIndexToId); - - if (subComponentIndex == -1 && compiledData->scripts.count()) { - QV4::ScopedObject scripts(scope, v4->newArrayObject(compiledData->scripts.count())); + if (subComponentIndex == -1 && compiledData->compilationUnit->dependentScripts.count()) { + QV4::ScopedObject scripts(scope, v4->newArrayObject(compiledData->compilationUnit->dependentScripts.count())); context->importedScripts.set(v4, scripts); QV4::ScopedValue v(scope); - for (int i = 0; i < compiledData->scripts.count(); ++i) { - QQmlScriptData *s = compiledData->scripts.at(i); + for (int i = 0; i < compiledData->compilationUnit->dependentScripts.count(); ++i) { + QQmlScriptData *s = compiledData->compilationUnit->dependentScripts.at(i); scripts->putIndexed(i, (v = s->scriptValueForContext(context))); } } else if (sharedState->creationContext) { @@ -240,7 +236,7 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance) Q_ASSERT(topLevelCreator); Q_ASSERT(!sharedState->allJavaScriptObjects); - sharedState->allJavaScriptObjects = valueScope.alloc(compiledData->totalObjectCount); + sharedState->allJavaScriptObjects = valueScope.alloc(compiledData->compilationUnit->totalObjectCount); QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc(1)); @@ -259,11 +255,7 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance) qSwap(_bindingTarget, bindingTarget); qSwap(_vmeMetaObject, vmeMetaObject); - QBitArray bindingSkipList = compiledData->deferredBindingsPerObject.value(_compiledObjectIndex); - for (int i = 0; i < bindingSkipList.count(); ++i) - bindingSkipList.setBit(i, !bindingSkipList.testBit(i)); - - setupBindings(bindingSkipList); + setupBindings(/*applyDeferredBindings=*/true); qSwap(_vmeMetaObject, vmeMetaObject); qSwap(_bindingTarget, bindingTarget); @@ -630,14 +622,14 @@ static QQmlType *qmlTypeForObject(QObject *object) return type; } -void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip) +void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) { QQmlListProperty<void> savedList; qSwap(_currentList, savedList); const QV4::CompiledData::BindingPropertyData &propertyData = compiledData->compilationUnit->bindingPropertyDataPerObject.at(_compiledObjectIndex); - if (_compiledObject->idIndex) { + if (_compiledObject->idNameIndex) { const QQmlPropertyData *idProperty = propertyData.last(); Q_ASSERT(!idProperty || !idProperty->isValid() || idProperty->name(_qobject) == QLatin1String("id")); if (idProperty && idProperty->isValid() && idProperty->isWritable() && idProperty->propType == QMetaType::QString) { @@ -645,7 +637,7 @@ void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip) idBinding.propertyNameIndex = 0; // Not used idBinding.flags = 0; idBinding.type = QV4::CompiledData::Binding::Type_String; - idBinding.stringIndex = _compiledObject->idIndex; + idBinding.stringIndex = _compiledObject->idNameIndex; idBinding.location = _compiledObject->location; // ### setPropertyValue(idProperty, &idBinding); } @@ -681,9 +673,17 @@ void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip) const QV4::CompiledData::Binding *binding = _compiledObject->bindingTable(); for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) { - if (static_cast<int>(i) < bindingsToSkip.size() && bindingsToSkip.testBit(i)) + if (binding->flags & QV4::CompiledData::Binding::IsCustomParserBinding) continue; + if (binding->flags & QV4::CompiledData::Binding::IsDeferredBinding) { + if (!applyDeferredBindings) + continue; + } else { + if (applyDeferredBindings) + continue; + } + const QQmlPropertyData *property = propertyData.at(i); if (property && property->isQList()) { @@ -1005,11 +1005,10 @@ void QQmlObjectCreator::recordError(const QV4::CompiledData::Location &location, errors << error; } -void QQmlObjectCreator::registerObjectWithContextById(int objectIndex, QObject *instance) const +void QQmlObjectCreator::registerObjectWithContextById(const QV4::CompiledData::Object *object, QObject *instance) const { - QHash<int, int>::ConstIterator idEntry = objectIndexToId.find(objectIndex); - if (idEntry != objectIndexToId.constEnd()) - context->setIdProperty(idEntry.value(), instance); + if (object->id >= 0) + context->setIdProperty(object->id, instance); } QV4::Heap::QmlContext *QQmlObjectCreator::currentQmlContext() @@ -1022,7 +1021,9 @@ QV4::Heap::QmlContext *QQmlObjectCreator::currentQmlContext() QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isContextObject) { - QQmlObjectCreationProfiler profiler(sharedState->profiler.profiler); + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); + QQmlObjectCreationProfiler profiler(sharedState->profiler.profiler, obj); + ActiveOCRestorer ocRestorer(this, QQmlEnginePrivate::get(engine)); bool isComponent = false; @@ -1032,8 +1033,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo QQmlParserStatus *parserStatus = 0; bool installPropertyCache = true; - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); - if (compiledData->isComponent(index)) { + if (obj->flags & QV4::CompiledData::Object::IsComponent) { isComponent = true; QQmlComponent *component = new QQmlComponent(engine, compiledData, index, parent); Q_QML_OC_PROFILE(sharedState->profiler, profiler.update( @@ -1125,28 +1125,26 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo if (isContextObject) context->contextObject = instance; - QBitArray bindingsToSkip; - if (customParser) { - QHash<int, QBitArray>::ConstIterator customParserBindings = compiledData->customParserBindings.constFind(index); - if (customParserBindings != compiledData->customParserBindings.constEnd()) { - customParser->engine = QQmlEnginePrivate::get(engine); - customParser->imports = compiledData->importCache; - - QList<const QV4::CompiledData::Binding *> bindings; - const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); - for (int i = 0; i < customParserBindings->count(); ++i) - if (customParserBindings->testBit(i)) - bindings << obj->bindingTable() + i; - customParser->applyBindings(instance, compiledData, bindings); - - customParser->engine = 0; - customParser->imports = (QQmlTypeNameCache*)0; - bindingsToSkip = *customParserBindings; + if (customParser && obj->flags & QV4::CompiledData::Object::HasCustomParserBindings) { + customParser->engine = QQmlEnginePrivate::get(engine); + customParser->imports = compiledData->compilationUnit->importCache; + + QList<const QV4::CompiledData::Binding *> bindings; + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); + const QV4::CompiledData::Binding *binding = obj->bindingTable(); + for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { + if (binding->flags & QV4::CompiledData::Binding::IsCustomParserBinding) { + bindings << binding; + } } + customParser->applyBindings(instance, compiledData->compilationUnit.data(), bindings); + + customParser->engine = 0; + customParser->imports = (QQmlTypeNameCache*)0; } if (isComponent) { - registerObjectWithContextById(index, instance); + registerObjectWithContextById(obj, instance); return instance; } @@ -1171,7 +1169,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo qSwap(_qmlContext, qmlContext); - bool result = populateInstance(index, instance, /*binding target*/instance, /*value type property*/0, bindingsToSkip); + bool result = populateInstance(index, instance, /*binding target*/instance, /*value type property*/0); qSwap(_qmlContext, qmlContext); qSwap(_scopeObject, scopeObject); @@ -1266,7 +1264,7 @@ void QQmlObjectCreator::clear() phase = Done; } -bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty, const QBitArray &bindingsToSkip) +bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty) { QQmlData *declarativeData = QQmlData::get(instance, /*create*/true); @@ -1298,33 +1296,23 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * vmeMetaObject = QQmlVMEMetaObject::get(_qobject); } - registerObjectWithContextById(_compiledObjectIndex, _qobject); + registerObjectWithContextById(_compiledObject, _qobject); qSwap(_propertyCache, cache); qSwap(_vmeMetaObject, vmeMetaObject); - QBitArray bindingSkipList = bindingsToSkip; - { - QHash<int, QBitArray>::ConstIterator deferredBindings = compiledData->deferredBindingsPerObject.constFind(_compiledObjectIndex); - if (deferredBindings != compiledData->deferredBindingsPerObject.constEnd()) { - if (bindingSkipList.isEmpty()) - bindingSkipList.resize(deferredBindings->count()); - - for (int i = 0; i < deferredBindings->count(); ++i) - if (deferredBindings->testBit(i)) - bindingSkipList.setBit(i); - QQmlData::DeferredData *deferData = new QQmlData::DeferredData; - deferData->deferredIdx = _compiledObjectIndex; - deferData->compiledData = compiledData; - deferData->compiledData->addref(); - deferData->context = context; - _ddata->deferredData = deferData; - } + if (_compiledObject->flags & QV4::CompiledData::Object::HasDeferredBindings) { + QQmlData::DeferredData *deferData = new QQmlData::DeferredData; + deferData->deferredIdx = _compiledObjectIndex; + deferData->compiledData = compiledData; + deferData->compiledData->addref(); + deferData->context = context; + _ddata->deferredData = deferData; } if (_compiledObject->nFunctions > 0) setupFunctions(); - setupBindings(bindingSkipList); + setupBindings(); qSwap(_vmeMetaObject, vmeMetaObject); qSwap(_bindingTarget, bindingTarget); diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 8045281cbb..f51cf3a2ca 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -111,10 +111,9 @@ private: QObject *createInstance(int index, QObject *parent = 0, bool isContextObject = false); bool populateInstance(int index, QObject *instance, - QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty, - const QBitArray &bindingsToSkip = QBitArray()); + QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty); - void setupBindings(const QBitArray &bindingsToSkip); + void setupBindings(bool applyDeferredBindings = false); bool setPropertyBinding(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding); void setPropertyValue(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding); void setupFunctions(); @@ -122,7 +121,7 @@ private: QString stringAt(int idx) const { return qmlUnit->stringAt(idx); } void recordError(const QV4::CompiledData::Location &location, const QString &description); - void registerObjectWithContextById(int objectIndex, QObject *instance) const; + void registerObjectWithContextById(const QV4::CompiledData::Object *object, QObject *instance) const; QV4::Heap::QmlContext *currentQmlContext(); @@ -143,7 +142,6 @@ private: QQmlContextData *context; const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes; const QQmlPropertyCacheVector &propertyCaches; - QHash<int, int> objectIndexToId; QExplicitlySharedDataPointer<QQmlObjectCreatorSharedState> sharedState; bool topLevelCreator; void *activeVMEDataForRootContext; diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 4ac13d1cb4..08d51ecfa1 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -452,7 +452,12 @@ QQmlPropertyCache *QQmlPropertyCache::parent() const void QQmlPropertyCache::setParent(QQmlPropertyCache *newParent) { + if (_parent == newParent) + return; + if (_parent) + _parent->release(); _parent = newParent; + _parent->addref(); } // Returns the first C++ type's QMetaObject - that is, the first QMetaObject not created by diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index a16acac3ab..6f7f13823e 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -67,7 +67,7 @@ public: bool ignoreUnknownSignals; bool componentcomplete; - QQmlRefPointer<QQmlCompiledData> cdata; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; QList<const QV4::CompiledData::Binding *> bindings; }; @@ -258,11 +258,11 @@ void QQmlConnectionsParser::verifyBindings(const QV4::CompiledData::Unit *qmlUni } } -void QQmlConnectionsParser::applyBindings(QObject *object, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings) +void QQmlConnectionsParser::applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { QQmlConnectionsPrivate *p = static_cast<QQmlConnectionsPrivate *>(QObjectPrivate::get(object)); - p->cdata = cdata; + p->compilationUnit = compilationUnit; p->bindings = bindings; } @@ -278,7 +278,7 @@ void QQmlConnections::connectSignals() QQmlData *ddata = QQmlData::get(this); QQmlContextData *ctxtdata = ddata ? ddata->outerContext : 0; - const QV4::CompiledData::Unit *qmlUnit = d->cdata->compilationUnit->data; + const QV4::CompiledData::Unit *qmlUnit = d->compilationUnit->data; foreach (const QV4::CompiledData::Binding *binding, d->bindings) { Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script); QString propName = qmlUnit->stringAt(binding->propertyNameIndex); @@ -291,7 +291,7 @@ void QQmlConnections::connectSignals() QQmlBoundSignalExpression *expression = ctxtdata ? new QQmlBoundSignalExpression(target, signalIndex, - ctxtdata, this, d->cdata->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]) : 0; + ctxtdata, this, d->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]) : 0; signal->takeExpression(expression); d->boundsignals += signal; } else { diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h index ff128a0ca6..d454affba8 100644 --- a/src/qml/types/qqmlconnections_p.h +++ b/src/qml/types/qqmlconnections_p.h @@ -99,7 +99,7 @@ class QQmlConnectionsParser : public QQmlCustomParser { public: virtual void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props); - virtual void applyBindings(QObject *object, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings); + virtual void applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings); }; diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 9cbda4516e..69066d1c69 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -2401,7 +2401,7 @@ bool QQmlListModelParser::verifyProperty(const QV4::CompiledData::Unit *qmlUnit, listElementTypeName = objName; // cache right name for next time } - if (!qmlUnit->stringAt(target->idIndex).isEmpty()) { + if (!qmlUnit->stringAt(target->idNameIndex).isEmpty()) { error(target->locationOfIdProperty, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property")); return false; } @@ -2508,13 +2508,13 @@ void QQmlListModelParser::verifyBindings(const QV4::CompiledData::Unit *qmlUnit, } } -void QQmlListModelParser::applyBindings(QObject *obj, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings) +void QQmlListModelParser::applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { QQmlListModel *rv = static_cast<QQmlListModel *>(obj); rv->m_engine = QV8Engine::getV4(qmlEngine(rv)); - const QV4::CompiledData::Unit *qmlUnit = cdata->compilationUnit->data; + const QV4::CompiledData::Unit *qmlUnit = compilationUnit->data; bool setRoles = false; diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h index e71b1e24b8..220b0e54b5 100644 --- a/src/qml/types/qqmllistmodel_p.h +++ b/src/qml/types/qqmllistmodel_p.h @@ -188,7 +188,7 @@ public: QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {} void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings) Q_DECL_OVERRIDE; - void applyBindings(QObject *obj, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings) Q_DECL_OVERRIDE; + void applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) Q_DECL_OVERRIDE; private: bool verifyProperty(const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding); diff --git a/src/qmldebug/qqmlprofilerclient.cpp b/src/qmldebug/qqmlprofilerclient.cpp index 6f6b04ade9..29ccbb33b6 100644 --- a/src/qmldebug/qqmlprofilerclient.cpp +++ b/src/qmldebug/qqmlprofilerclient.cpp @@ -70,7 +70,7 @@ void QQmlProfilerClient::sendRecordingStatus(bool record, int engineId, quint32 Q_D(const QQmlProfilerClient); QPacket stream(d->connection->currentDataStreamVersion()); - stream << record << engineId << d->features << flushInterval; + stream << record << engineId << d->features << flushInterval << true; sendMessage(stream.data()); } @@ -205,7 +205,7 @@ inline QQmlProfilerDefinitions::ProfileFeature featureFromRangeType( void QQmlProfilerClient::messageReceived(const QByteArray &data) { - Q_D(const QQmlProfilerClient); + Q_D(QQmlProfilerClient); QPacket stream(d->connection->currentDataStreamVersion(), data); @@ -333,12 +333,25 @@ void QQmlProfilerClient::messageReceived(const QByteArray &data) !(d->features & one << featureFromRangeType(rangeType))) return; + qint64 typeId = 0; if (messageType == QQmlProfilerDefinitions::RangeStart) { rangeStart(rangeType, time); + if (!stream.atEnd()) { + stream >> typeId; + auto i = d->types.constFind(typeId); + if (i != d->types.constEnd()) { + rangeLocation(rangeType, time, i->location); + rangeData(rangeType, time, i->name); + } + } } else if (messageType == QQmlProfilerDefinitions::RangeData) { QString data; stream >> data; rangeData(rangeType, time, data); + if (!stream.atEnd()) { + stream >> typeId; + d->types[typeId].name = data; + } } else if (messageType == QQmlProfilerDefinitions::RangeLocation) { QQmlEventLocation location; stream >> location.filename >> location.line; @@ -347,6 +360,10 @@ void QQmlProfilerClient::messageReceived(const QByteArray &data) stream >> location.column; rangeLocation(rangeType, time, location); + if (!stream.atEnd()) { + stream >> typeId; + d->types[typeId].location = location; + } } else if (messageType == QQmlProfilerDefinitions::RangeEnd) { rangeEnd(rangeType, time); } else { diff --git a/src/qmldebug/qqmlprofilerclient_p_p.h b/src/qmldebug/qqmlprofilerclient_p_p.h index 8238c97dd8..9c44113aa8 100644 --- a/src/qmldebug/qqmlprofilerclient_p_p.h +++ b/src/qmldebug/qqmlprofilerclient_p_p.h @@ -56,12 +56,20 @@ QT_BEGIN_NAMESPACE +struct QQmlProfilerRangeType +{ + QQmlEventLocation location; + QString name; +}; + class QQmlProfilerClientPrivate : public QQmlDebugClientPrivate { Q_DECLARE_PUBLIC(QQmlProfilerClient) public: QQmlProfilerClientPrivate(QQmlDebugConnection *connection); quint64 features; + + QHash<qint64, QQmlProfilerRangeType> types; }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 5061c19f1e..14c0adf393 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -415,4 +415,26 @@ Item { \endqml */ +/*! + \qmlproperty int QtQuick::WheelEvent::inverted + + Returns whether the delta values delivered with the event are inverted. + + Normally, a vertical wheel will produce a WheelEvent with positive delta + values if the top of the wheel is rotating away from the hand operating it. + Similarly, a horizontal wheel movement will produce a QWheelEvent with + positive delta values if the top of the wheel is moved to the left. + + However, on some platforms this is configurable, so that the same + operations described above will produce negative delta values (but with the + same magnitude). For instance, in a QML component (such as a tumbler or a + slider) where it is appropriate to synchronize the movement or rotation of + an item with the direction of the wheel, regardless of the system settings, + the wheel event handler can use the inverted property to decide whether to + negate the angleDelta or pixelDelta values. + + \note Many platforms provide no such information. On such platforms + \l inverted always returns false. +*/ + QT_END_NAMESPACE diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index b28ab555b0..6d4b49b3cd 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -73,10 +73,22 @@ class QQuickKeyEvent : public QObject Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) public: - QQuickKeyEvent(QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, const QString &text=QString(), bool autorep=false, ushort count=1) - : event(type, key, modifiers, text, autorep, count) { event.setAccepted(false); } - QQuickKeyEvent(const QKeyEvent &ke) - : event(ke) { event.setAccepted(false); } + QQuickKeyEvent() + : event(QEvent::None, 0, 0) + {} + + void reset(QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, + const QString &text = QString(), bool autorep = false, ushort count = 1) + { + event = QKeyEvent(type, key, modifiers, text, autorep, count); + event.setAccepted(false); + } + + void reset(const QKeyEvent &ke) + { + event = ke; + event.setAccepted(false); + } int key() const { return event.key(); } QString text() const { return event.text(); } @@ -109,10 +121,21 @@ class Q_QUICK_PRIVATE_EXPORT QQuickMouseEvent : public QObject Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) public: - QQuickMouseEvent(qreal x, qreal y, Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers - , bool isClick=false, bool wasHeld=false) - : _x(x), _y(y), _button(button), _buttons(buttons), _modifiers(modifiers) - , _source(Qt::MouseEventNotSynthesized), _wasHeld(wasHeld), _isClick(isClick), _accepted(true) {} + QQuickMouseEvent() {} + + void reset(qreal x, qreal y, Qt::MouseButton button, Qt::MouseButtons buttons, + Qt::KeyboardModifiers modifiers, bool isClick = false, bool wasHeld = false) + { + _x = x; + _y = y; + _button = button; + _buttons = buttons; + _modifiers = modifiers; + _source = Qt::MouseEventNotSynthesized; + _wasHeld = wasHeld; + _isClick = isClick; + _accepted = true; + } qreal x() const { return _x; } qreal y() const { return _y; } @@ -139,9 +162,9 @@ private: Qt::MouseButtons _buttons; Qt::KeyboardModifiers _modifiers; Qt::MouseEventSource _source; - bool _wasHeld; - bool _isClick; - bool _accepted; + bool _wasHeld : 1; + bool _isClick : 1; + bool _accepted : 1; }; class QQuickWheelEvent : public QObject @@ -153,13 +176,24 @@ class QQuickWheelEvent : public QObject Q_PROPERTY(QPoint pixelDelta READ pixelDelta) Q_PROPERTY(int buttons READ buttons) Q_PROPERTY(int modifiers READ modifiers) + Q_PROPERTY(bool inverted READ inverted) Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) public: - QQuickWheelEvent(qreal x, qreal y, const QPoint& angleDelta, const QPoint& pixelDelta, - Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) - : _x(x), _y(y), _angleDelta(angleDelta), _pixelDelta(pixelDelta), _buttons(buttons), - _modifiers(modifiers), _accepted(true) {} + QQuickWheelEvent() {} + + void reset(qreal x, qreal y, const QPoint &angleDelta, const QPoint &pixelDelta, + Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, bool inverted) + { + _x = x; + _y = y; + _angleDelta = angleDelta; + _pixelDelta = pixelDelta; + _buttons = buttons; + _modifiers = modifiers; + _accepted = true; + _inverted = inverted; + } qreal x() const { return _x; } qreal y() const { return _y; } @@ -167,7 +201,7 @@ public: QPoint pixelDelta() const { return _pixelDelta; } int buttons() const { return _buttons; } int modifiers() const { return _modifiers; } - + bool inverted() const { return _inverted; } bool isAccepted() { return _accepted; } void setAccepted(bool accepted) { _accepted = accepted; } @@ -178,6 +212,7 @@ private: QPoint _pixelDelta; Qt::MouseButtons _buttons; Qt::KeyboardModifiers _modifiers; + bool _inverted; bool _accepted; }; diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index d0d10a5849..9a13198d7d 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -1355,7 +1355,8 @@ void QQuickKeysAttached::keyPressed(QKeyEvent *event, bool post) d->inPress = false; } - QQuickKeyEvent ke(*event); + QQuickKeyEvent &ke = d->theKeyEvent; + ke.reset(*event); QByteArray keySignal = keyToSignal(event->key()); if (!keySignal.isEmpty()) { keySignal += "(QQuickKeyEvent*)"; @@ -1398,7 +1399,8 @@ void QQuickKeysAttached::keyReleased(QKeyEvent *event, bool post) d->inRelease = false; } - QQuickKeyEvent ke(*event); + QQuickKeyEvent &ke = d->theKeyEvent; + ke.reset(*event); emit released(&ke); event->setAccepted(ke.isAccepted()); diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 846063a58b..477071f7b3 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -56,6 +56,7 @@ #include "qquickanchors_p.h" #include "qquickanchors_p_p.h" #include "qquickitemchangelistener_p.h" +#include "qquickevents_p_p.h" #include "qquickwindow_p.h" @@ -777,6 +778,7 @@ public: QQuickItem *imeItem; QList<QQuickItem *> targets; QQuickItem *item; + QQuickKeyEvent theKeyEvent; }; class QQuickKeysAttached : public QObject, public QQuickItemKeyFilter diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index 920a86881b..66aff5c2f4 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -40,7 +40,6 @@ #include "qquickmousearea_p.h" #include "qquickmousearea_p_p.h" #include "qquickwindow.h" -#include "qquickevents_p_p.h" #include "qquickdrag_p.h" #include <private/qqmldata_p.h> @@ -761,7 +760,8 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event) } #endif - QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); + QQuickMouseEvent &me = d->quickMouseEvent; + me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); me.setSource(event->source()); emit mouseXChanged(&me); me.setPosition(d->lastPos); @@ -802,7 +802,8 @@ void QQuickMouseArea::mouseDoubleClickEvent(QMouseEvent *event) Q_D(QQuickMouseArea); if (d->enabled) { d->saveEvent(event); - QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false); + QQuickMouseEvent &me = d->quickMouseEvent; + me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false); me.setSource(event->source()); me.setAccepted(d->isDoubleClickConnected()); emit this->doubleClicked(&me); @@ -822,7 +823,8 @@ void QQuickMouseArea::hoverEnterEvent(QHoverEvent *event) d->lastPos = event->posF(); d->lastModifiers = event->modifiers(); setHovered(true); - QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false); + QQuickMouseEvent &me = d->quickMouseEvent; + me.reset(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false); emit mouseXChanged(&me); me.setPosition(d->lastPos); emit mouseYChanged(&me); @@ -835,10 +837,11 @@ void QQuickMouseArea::hoverMoveEvent(QHoverEvent *event) Q_D(QQuickMouseArea); if (!d->enabled && !d->pressed) { QQuickItem::hoverMoveEvent(event); - } else { + } else if (d->lastPos != event->posF()) { d->lastPos = event->posF(); d->lastModifiers = event->modifiers(); - QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false); + QQuickMouseEvent &me = d->quickMouseEvent; + me.reset(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false); emit mouseXChanged(&me); me.setPosition(d->lastPos); emit mouseYChanged(&me); @@ -865,8 +868,9 @@ void QQuickMouseArea::wheelEvent(QWheelEvent *event) return; } - QQuickWheelEvent we(event->posF().x(), event->posF().y(), event->angleDelta(), - event->pixelDelta(), event->buttons(), event->modifiers()); + QQuickWheelEvent &we = d->quickWheelEvent; + we.reset(event->posF().x(), event->posF().y(), event->angleDelta(), event->pixelDelta(), + event->buttons(), event->modifiers(), event->inverted()); we.setAccepted(d->isWheelConnected()); emit wheel(&we); if (!we.isAccepted()) @@ -997,7 +1001,8 @@ void QQuickMouseArea::timerEvent(QTimerEvent *event) #endif if (d->pressed && dragged == false && d->hovered == true) { d->longPress = true; - QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); + QQuickMouseEvent &me = d->quickMouseEvent; + me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); me.setSource(Qt::MouseEventSynthesizedByQt); me.setAccepted(d->isPressAndHoldConnected()); emit pressAndHold(&me); @@ -1078,8 +1083,7 @@ void QQuickMouseArea::setHoverEnabled(bool h) \qmlproperty bool QtQuick::MouseArea::containsMouse This property holds whether the mouse is currently inside the mouse area. - \warning This property is not updated if the area moves under the mouse: \e containsMouse will not change. - In addition, if hoverEnabled is false, containsMouse will only be valid + \warning If hoverEnabled is false, containsMouse will only be valid when the mouse is pressed while the mouse cursor is inside the MouseArea. */ bool QQuickMouseArea::hovered() const @@ -1175,7 +1179,8 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS Qt::MouseButtons oldPressed = d->pressed; if (wasPressed != p) { - QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress); + QQuickMouseEvent &me = d->quickMouseEvent; + me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress); me.setSource(source); if (p) { d->pressed |= button; diff --git a/src/quick/items/qquickmousearea_p_p.h b/src/quick/items/qquickmousearea_p_p.h index 2d841b9ae1..794a8cc6ff 100644 --- a/src/quick/items/qquickmousearea_p_p.h +++ b/src/quick/items/qquickmousearea_p_p.h @@ -52,6 +52,7 @@ // #include "qquickitem_p.h" +#include "qquickevents_p_p.h" #include <QtGui/qevent.h> #include <QtCore/qbasictimer.h> @@ -107,6 +108,8 @@ public: #ifndef QT_NO_CURSOR QCursor *cursor; #endif + QQuickMouseEvent quickMouseEvent; + QQuickWheelEvent quickWheelEvent; }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickopenglshadereffectnode.cpp b/src/quick/items/qquickopenglshadereffectnode.cpp index c4dd8d5c63..02b76b2dbc 100644 --- a/src/quick/items/qquickopenglshadereffectnode.cpp +++ b/src/quick/items/qquickopenglshadereffectnode.cpp @@ -456,7 +456,7 @@ void QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache() { QQuickOpenGLShaderEffectMaterialCache *cache = QQuickOpenGLShaderEffectMaterialCache::get(false); if (cache) { - qDeleteAll(cache->cache.values()); + qDeleteAll(cache->cache); delete cache; } } diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp index ec9a104812..dbe1add345 100644 --- a/src/quick/items/qquickrendercontrol.cpp +++ b/src/quick/items/qquickrendercontrol.cpp @@ -254,7 +254,7 @@ void QQuickRenderControl::polishItems() return; QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window); - cd->flushDelayedTouchEvent(); + cd->flushFrameSynchronousEvents(); if (!d->window) return; cd->polishItems(); diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index b9fb0721b3..6333bf704a 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2028,7 +2028,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) } } -void QQuickWindowPrivate::flushDelayedTouchEvent() +void QQuickWindowPrivate::flushFrameSynchronousEvents() { if (delayedTouch) { deliverDelayedTouchEvent(); @@ -2039,6 +2039,17 @@ void QQuickWindowPrivate::flushDelayedTouchEvent() if (ut && ut->hasStartAnimationPending()) ut->startAnimations(); } + + // Once per frame, send a synthetic hover, in case items have changed position. + // For instance, during animation (including the case of a ListView + // whose delegates contain MouseAreas), a MouseArea needs to know + // whether it has moved into a position where it is now under the cursor. + if (!mouseGrabberItem && !lastMousePosition.isNull()) { + bool accepted = false; + bool delivered = deliverHoverEvent(contentItem, lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), accepted); + if (!delivered) + clearHover(); // take care of any exits + } } void QQuickWindowPrivate::reallyDeliverTouchEvent(QTouchEvent *event) diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 1486e20e1e..daff9ef473 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -160,7 +160,7 @@ public: void reallyDeliverTouchEvent(QTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); void deliverDelayedTouchEvent(); - void flushDelayedTouchEvent(); + void flushFrameSynchronousEvents(); bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted); bool deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints, QSet<QQuickItem*> *filtered); static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds = false); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp index bfcc99c4c2..300ce6920c 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp @@ -126,7 +126,7 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window) data.updatePending = false; if (!data.grabOnly) { - cd->flushDelayedTouchEvent(); + cd->flushFrameSynchronousEvents(); // Event delivery/processing triggered the window to be deleted or stop rendering. if (!m_windows.contains(window)) return; diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 5af79747a7..e8e1ec7db6 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -846,7 +846,7 @@ Renderer::~Renderer() for (int i=0; i<m_batchPool.size(); ++i) qsg_wipeBatch(m_batchPool.at(i), this); } - foreach (Node *n, m_nodes.values()) + for (Node *n : qAsConst(m_nodes)) m_nodeAllocator.release(n); // Remaining elements... diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index cb8f1399a4..1a9b576af4 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -382,7 +382,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) return; if (!data.grabOnly) { - cd->flushDelayedTouchEvent(); + cd->flushFrameSynchronousEvents(); // Event delivery/processing triggered the window to be deleted or stop rendering. if (!m_windows.contains(window)) return; diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 1feba32ab2..3ff32b360d 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -1142,7 +1142,7 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose) } // Flush pending touch events. - QQuickWindowPrivate::get(window)->flushDelayedTouchEvent(); + QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents(); // The delivery of the event might have caused the window to stop rendering w = windowFor(m_windows, window); if (!w || !w->thread || !w->thread->window) { diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index 5e025e9761..371f512c6e 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -445,7 +445,7 @@ void QSGWindowsRenderLoop::renderWindow(QQuickWindow *window) } } - d->flushDelayedTouchEvent(); + d->flushFrameSynchronousEvents(); // Event delivery or processing has caused the window to stop rendering. if (!windowData(window)) return; diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index 597613c9fd..dc4b27d738 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -922,15 +922,13 @@ QQuickPixmapStore::~QQuickPixmapStore() #ifndef QT_NO_DEBUG int leakedPixmaps = 0; #endif - QList<QQuickPixmapData*> cachedData = m_cache.values(); - // Prevent unreferencePixmap() from assuming it needs to kick // off the cache expiry timer, as we're shrinking the cache // manually below after releasing all the pixmaps. m_timerId = -2; // unreference all (leaked) pixmaps - foreach (QQuickPixmapData* pixmap, cachedData) { + for (auto *pixmap : qAsConst(m_cache)) { int currRefCount = pixmap->refCount; if (currRefCount) { #ifndef QT_NO_DEBUG diff --git a/src/quick/util/qquickprofiler.cpp b/src/quick/util/qquickprofiler.cpp index f8d090cc2c..659ffe1d84 100644 --- a/src/quick/util/qquickprofiler.cpp +++ b/src/quick/util/qquickprofiler.cpp @@ -110,8 +110,9 @@ void QQuickProfiler::stopProfilingImpl() m_data.clear(); } -void QQuickProfiler::reportDataImpl() +void QQuickProfiler::reportDataImpl(bool trackLocations) { + Q_UNUSED(trackLocations); QMutexLocker lock(&m_dataMutex); emit dataReady(m_data); m_data.clear(); diff --git a/src/quick/util/qquickprofiler_p.h b/src/quick/util/qquickprofiler_p.h index b58d4f47c1..f1af87f4e6 100644 --- a/src/quick/util/qquickprofiler_p.h +++ b/src/quick/util/qquickprofiler_p.h @@ -353,7 +353,7 @@ signals: protected slots: void startProfilingImpl(quint64 features); void stopProfilingImpl(); - void reportDataImpl(); + void reportDataImpl(bool trackLocations); void setTimer(const QElapsedTimer &t); }; diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp index 0119aecb7e..bd556444de 100644 --- a/src/quick/util/qquickpropertychanges.cpp +++ b/src/quick/util/qquickpropertychanges.cpp @@ -202,7 +202,7 @@ public: QPointer<QObject> object; QList<const QV4::CompiledData::Binding *> bindings; - QQmlRefPointer<QQmlCompiledData> cdata; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; bool decoded : 1; bool restore : 1; @@ -258,7 +258,7 @@ void QQuickPropertyChangesPrivate::decode() return; foreach (const QV4::CompiledData::Binding *binding, bindings) - decodeBinding(QString(), cdata->compilationUnit->data, binding); + decodeBinding(QString(), compilationUnit->data, binding); bindings.clear(); @@ -288,7 +288,7 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix, QQuickReplaceSignalHandler *handler = new QQuickReplaceSignalHandler; handler->property = prop; handler->expression.take(new QQmlBoundSignalExpression(object, QQmlPropertyPrivate::get(prop)->signalIndex(), - QQmlContextData::get(qmlContext(q)), object, cdata->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex])); + QQmlContextData::get(qmlContext(q)), object, compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex])); signalReplacements << handler; return; } @@ -338,12 +338,12 @@ void QQuickPropertyChangesParser::verifyBindings(const QV4::CompiledData::Unit * verifyList(qmlUnit, props.at(ii)); } -void QQuickPropertyChangesParser::applyBindings(QObject *obj, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings) +void QQuickPropertyChangesParser::applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { QQuickPropertyChangesPrivate *p = static_cast<QQuickPropertyChangesPrivate *>(QObjectPrivate::get(obj)); p->bindings = bindings; - p->cdata = cdata; + p->compilationUnit = compilationUnit; p->decoded = false; } @@ -456,7 +456,7 @@ QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions() QQmlBinding *newBinding = 0; if (e.id != QQmlBinding::Invalid) { QV4::Scope scope(QQmlEnginePrivate::getV4Engine(qmlEngine(this))); - QV4::ScopedValue function(scope, QV4::FunctionObject::createQmlFunction(context, object(), d->cdata->compilationUnit->runtimeFunctions[e.id])); + QV4::ScopedValue function(scope, QV4::FunctionObject::createQmlFunction(context, object(), d->compilationUnit->runtimeFunctions[e.id])); newBinding = new QQmlBinding(function, object(), context); } // QQmlBinding *newBinding = e.id != QQmlBinding::Invalid ? QQmlBinding::createBinding(e.id, object(), qmlContext(this)) : 0; diff --git a/src/quick/util/qquickpropertychanges_p.h b/src/quick/util/qquickpropertychanges_p.h index 9d086b7688..0537750338 100644 --- a/src/quick/util/qquickpropertychanges_p.h +++ b/src/quick/util/qquickpropertychanges_p.h @@ -104,7 +104,7 @@ public: void verifyList(const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding); virtual void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props); - virtual void applyBindings(QObject *obj, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings); + virtual void applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings); }; diff --git a/src/quick/util/qquicksmoothedanimation.cpp b/src/quick/util/qquicksmoothedanimation.cpp index d4956983fb..569cb37c95 100644 --- a/src/quick/util/qquicksmoothedanimation.cpp +++ b/src/quick/util/qquicksmoothedanimation.cpp @@ -392,7 +392,7 @@ QQuickSmoothedAnimationPrivate::~QQuickSmoothedAnimationPrivate() void QQuickSmoothedAnimationPrivate::updateRunningAnimations() { - foreach(QSmoothedAnimation* ease, activeAnimations.values()){ + for (QSmoothedAnimation *ease : qAsConst(activeAnimations)) { ease->maximumEasingTime = anim->maximumEasingTime; ease->reversingMode = anim->reversingMode; ease->velocity = anim->velocity; @@ -444,7 +444,8 @@ QAbstractAnimationJob* QQuickSmoothedAnimation::transition(QQuickStateActions &a anims.insert(ease); } - foreach (QSmoothedAnimation *ease, d->activeAnimations.values()){ + const auto copy = d->activeAnimations; + for (QSmoothedAnimation *ease : copy) { if (!anims.contains(ease)) { ease->clearTemplate(); d->activeAnimations.remove(ease->target); diff --git a/src/quick/util/qquickspringanimation.cpp b/src/quick/util/qquickspringanimation.cpp index df077350e9..d2bc3b4ece 100644 --- a/src/quick/util/qquickspringanimation.cpp +++ b/src/quick/util/qquickspringanimation.cpp @@ -585,7 +585,8 @@ QAbstractAnimationJob* QQuickSpringAnimation::transition(QQuickStateActions &act animation->restart(); anims.insert(animation); } - foreach (QSpringAnimation *anim, d->activeAnimations.values()){ + const auto copy = d->activeAnimations; + for (QSpringAnimation *anim : copy) { if (!anims.contains(anim)) { anim->clearTemplate(); d->activeAnimations.remove(anim->target); diff --git a/tests/auto/qml/qqmllanguage/data/rootItemIsComponent.qml b/tests/auto/qml/qqmllanguage/data/rootItemIsComponent.qml new file mode 100644 index 0000000000..dd653352d9 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/rootItemIsComponent.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 +Component { + QtObject { + id: blah + } +} diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index cc39422dce..ccfcfc098e 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -110,11 +110,11 @@ QVariant myCustomVariantTypeConverter(const QString &data) } -void CustomBindingParser::applyBindings(QObject *object, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings) +void CustomBindingParser::applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) { CustomBinding *customBinding = qobject_cast<CustomBinding*>(object); Q_ASSERT(customBinding); - customBinding->cdata = cdata; + customBinding->compilationUnit = compilationUnit; customBinding->bindings = bindings; } @@ -123,14 +123,14 @@ void CustomBinding::componentComplete() Q_ASSERT(m_target); foreach (const QV4::CompiledData::Binding *binding, bindings) { - QString name = cdata->compilationUnit->data->stringAt(binding->propertyNameIndex); + QString name = compilationUnit->data->stringAt(binding->propertyNameIndex); int bindingId = binding->value.compiledScriptIndex; QQmlContextData *context = QQmlContextData::get(qmlContext(this)); QV4::Scope scope(QQmlEnginePrivate::getV4Engine(qmlEngine(this))); - QV4::ScopedValue function(scope, QV4::FunctionObject::createQmlFunction(context, m_target, cdata->compilationUnit->runtimeFunctions[bindingId])); + QV4::ScopedValue function(scope, QV4::FunctionObject::createQmlFunction(context, m_target, compilationUnit->runtimeFunctions[bindingId])); QQmlBinding *qmlBinding = new QQmlBinding(function, m_target, context); QQmlProperty property(m_target, name, qmlContext(this)); @@ -169,7 +169,7 @@ void EnumSupportingCustomParser::verifyBindings(const QV4::CompiledData::Unit *q } } -void SimpleObjectCustomParser::applyBindings(QObject *object, QQmlCompiledData *, const QList<const QV4::CompiledData::Binding *> &bindings) +void SimpleObjectCustomParser::applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &bindings) { SimpleObjectWithCustomParser *o = qobject_cast<SimpleObjectWithCustomParser*>(object); Q_ASSERT(o); diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index 2344b6a03b..788ae42726 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -733,14 +733,14 @@ class MyCustomParserTypeParser : public QQmlCustomParser { public: virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &) {} - virtual void applyBindings(QObject *, QQmlCompiledData *, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &) {} }; class EnumSupportingCustomParser : public QQmlCustomParser { public: virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &); - virtual void applyBindings(QObject *, QQmlCompiledData *, const QList<const QV4::CompiledData::Binding *> &) {} + virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &) {} }; class MyParserStatus : public QObject, public QQmlParserStatus @@ -1170,7 +1170,7 @@ public: void setTarget(QObject *newTarget) { m_target = newTarget; } QPointer<QObject> m_target; - QQmlRefPointer<QQmlCompiledData> cdata; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; QList<const QV4::CompiledData::Binding*> bindings; QByteArray m_bindingData; }; @@ -1178,7 +1178,7 @@ public: class CustomBindingParser : public QQmlCustomParser { virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &) {} - virtual void applyBindings(QObject *, QQmlCompiledData *, const QList<const QV4::CompiledData::Binding *> &); + virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &); }; class SimpleObjectWithCustomParser : public QObject @@ -1224,7 +1224,7 @@ private: class SimpleObjectCustomParser : public QQmlCustomParser { virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &) {} - virtual void applyBindings(QObject *, QQmlCompiledData *, const QList<const QV4::CompiledData::Binding *> &); + virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &); }; class RootObjectInCreationTester : public QObject diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 5bb129fbb8..58a7c39760 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -108,6 +108,7 @@ private slots: void bindTypeToJSValue(); void customParserTypes(); void rootAsQmlComponent(); + void rootItemIsComponent(); void inlineQmlComponents(); void idProperty(); void autoNotifyConnection(); @@ -1195,6 +1196,19 @@ void tst_qqmllanguage::rootAsQmlComponent() QCOMPARE(object->getChildren()->count(), 2); } +void tst_qqmllanguage::rootItemIsComponent() +{ + QQmlComponent component(&engine, testFileUrl("rootItemIsComponent.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> root(component.create()); + QVERIFY(qobject_cast<QQmlComponent*>(root.data())); + QScopedPointer<QObject> other(qobject_cast<QQmlComponent*>(root.data())->create()); + QVERIFY(!other.isNull()); + QQmlContext *context = qmlContext(other.data()); + QVERIFY(context); + QCOMPARE(context->nameForObject(other.data()), QStringLiteral("blah")); +} + // Tests that components can be specified inline void tst_qqmllanguage::inlineQmlComponents() { diff --git a/tests/manual/v4/test262 b/tests/manual/v4/test262 -Subproject 9741ac4655808ac46c127e3d1d8ba3d27ada618 +Subproject 0b5af3dcec772bb06b4d685a20b2859cda59d18 |