diff options
Diffstat (limited to 'src/plugins')
86 files changed, 3273 insertions, 1090 deletions
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 2467239be2..86fdf87650 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -1,4 +1,5 @@ TEMPLATE = subdirs +QT_FOR_CONFIG += qml -!contains(QT_CONFIG, no-qml-debug):SUBDIRS += qmltooling +qtConfig(thread):qtConfig(qml-debug):SUBDIRS += qmltooling qtHaveModule(quick):SUBDIRS += scenegraph diff --git a/src/plugins/qmltooling/packetprotocol/packetprotocol.pro b/src/plugins/qmltooling/packetprotocol/packetprotocol.pro index 383e32b54e..a188b87a81 100644 --- a/src/plugins/qmltooling/packetprotocol/packetprotocol.pro +++ b/src/plugins/qmltooling/packetprotocol/packetprotocol.pro @@ -1,10 +1,11 @@ TARGET = QtPacketProtocol -QT = core-private qml-private +QT = core-private CONFIG += static internal_module HEADERS = \ qpacketprotocol_p.h \ - qpacket_p.h + qpacket_p.h \ + qversionedpacket_p.h SOURCES = \ qpacketprotocol.cpp \ diff --git a/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp b/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp index e541810330..3e75e39f86 100644 --- a/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp +++ b/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp @@ -40,13 +40,13 @@ #include "qpacketprotocol_p.h" #include <QtCore/QElapsedTimer> +#include <QtCore/QtEndian> + #include <private/qiodevice_p.h> #include <private/qobject_p.h> QT_BEGIN_NAMESPACE -static const int MAX_PACKET_SIZE = 0x7FFFFFFF; - /*! \class QPacketProtocol \internal @@ -106,7 +106,10 @@ class QPacketProtocolPrivate : public QObjectPrivate public: QPacketProtocolPrivate(QIODevice *dev); - QList<qint64> sendingPackets; + bool writeToDevice(const char *bytes, qint64 size); + bool readFromDevice(char *buffer, qint64 size); + + QList<qint32> sendingPackets; QList<QByteArray> packets; QByteArray inProgress; qint32 inProgressSize; @@ -125,7 +128,6 @@ QPacketProtocol::QPacketProtocol(QIODevice *dev, QObject *parent) Q_ASSERT(dev); QObject::connect(dev, &QIODevice::readyRead, this, &QPacketProtocol::readyToRead); - QObject::connect(dev, &QIODevice::aboutToClose, this, &QPacketProtocol::aboutToClose); QObject::connect(dev, &QIODevice::bytesWritten, this, &QPacketProtocol::bytesWritten); } @@ -137,18 +139,24 @@ QPacketProtocol::QPacketProtocol(QIODevice *dev, QObject *parent) void QPacketProtocol::send(const QByteArray &data) { Q_D(QPacketProtocol); + static const qint32 maxSize = std::numeric_limits<qint32>::max() - sizeof(qint32); if (data.isEmpty()) return; // We don't send empty packets - qint64 sendSize = data.size() + sizeof(qint32); + if (data.size() > maxSize) { + emit error(); + return; + } + + const qint32 sendSize = data.size() + static_cast<qint32>(sizeof(qint32)); d->sendingPackets.append(sendSize); - qint32 sendSize32 = sendSize; - qint64 writeBytes = d->dev->write((char *)&sendSize32, sizeof(qint32)); - Q_UNUSED(writeBytes); - Q_ASSERT(writeBytes == sizeof(qint32)); - writeBytes = d->dev->write(data); - Q_ASSERT(writeBytes == data.size()); + + qint32 sendSizeLE = qToLittleEndian(sendSize); + if (!d->writeToDevice((const char *)&sendSizeLE, sizeof(qint32)) + || !d->writeToDevice(data.data(), data.size())) { + emit error(); + } } /*! @@ -200,17 +208,6 @@ bool QPacketProtocol::waitForReadyRead(int msecs) } while (true); } -/*! - Return the QIODevice passed to the QPacketProtocol constructor. -*/ -void QPacketProtocol::aboutToClose() -{ - Q_D(QPacketProtocol); - d->inProgress.clear(); - d->sendingPackets.clear(); - d->inProgressSize = -1; -} - void QPacketProtocol::bytesWritten(qint64 bytes) { Q_D(QPacketProtocol); @@ -234,28 +231,40 @@ void QPacketProtocol::readyToRead() // Need to get trailing data if (-1 == d->inProgressSize) { // We need a size header of sizeof(qint32) - if (sizeof(qint32) > (uint)d->dev->bytesAvailable()) + if (static_cast<qint64>(sizeof(qint32)) > d->dev->bytesAvailable()) return; // Read size header - int read = d->dev->read((char *)&d->inProgressSize, sizeof(qint32)); - Q_ASSERT(read == sizeof(qint32)); - Q_UNUSED(read); + qint32 inProgressSizeLE; + if (!d->readFromDevice((char *)&inProgressSizeLE, sizeof(qint32))) { + emit error(); + return; + } + d->inProgressSize = qFromLittleEndian(inProgressSizeLE); // Check sizing constraints - if (d->inProgressSize > MAX_PACKET_SIZE) { + if (d->inProgressSize < qint32(sizeof(qint32))) { disconnect(d->dev, &QIODevice::readyRead, this, &QPacketProtocol::readyToRead); - disconnect(d->dev, &QIODevice::aboutToClose, this, &QPacketProtocol::aboutToClose); disconnect(d->dev, &QIODevice::bytesWritten, this, &QPacketProtocol::bytesWritten); - d->dev = 0; - emit invalidPacket(); + d->dev = nullptr; + emit error(); return; } d->inProgressSize -= sizeof(qint32); } else { - d->inProgress.append(d->dev->read(d->inProgressSize - d->inProgress.size())); + const int bytesToRead = static_cast<int>( + qMin(d->dev->bytesAvailable(), + static_cast<qint64>(d->inProgressSize - d->inProgress.size()))); + + QByteArray toRead(bytesToRead, Qt::Uninitialized); + if (!d->readFromDevice(toRead.data(), toRead.length())) { + emit error(); + return; + } + + d->inProgress.append(toRead); if (d->inProgressSize == d->inProgress.size()) { // Packet has arrived! d->packets.append(d->inProgress); @@ -275,6 +284,30 @@ QPacketProtocolPrivate::QPacketProtocolPrivate(QIODevice *dev) : { } +bool QPacketProtocolPrivate::writeToDevice(const char *bytes, qint64 size) +{ + qint64 totalWritten = 0; + while (totalWritten < size) { + const qint64 chunkSize = dev->write(bytes + totalWritten, size - totalWritten); + if (chunkSize < 0) + return false; + totalWritten += chunkSize; + } + return totalWritten == size; +} + +bool QPacketProtocolPrivate::readFromDevice(char *buffer, qint64 size) +{ + qint64 totalRead = 0; + while (totalRead < size) { + const qint64 chunkSize = dev->read(buffer + totalRead, size - totalRead); + if (chunkSize < 0) + return false; + totalRead += chunkSize; + } + return totalRead == size; +} + /*! \fn void QPacketProtocol::readyRead() diff --git a/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h b/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h index 7fd722f17f..b401a58437 100644 --- a/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h +++ b/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h @@ -63,7 +63,7 @@ class QPacketProtocol : public QObject Q_OBJECT Q_DECLARE_PRIVATE(QPacketProtocol) public: - explicit QPacketProtocol(QIODevice *dev, QObject *parent = 0); + explicit QPacketProtocol(QIODevice *dev, QObject *parent = nullptr); void send(const QByteArray &data); qint64 packetsAvailable() const; @@ -72,10 +72,9 @@ public: Q_SIGNALS: void readyRead(); - void invalidPacket(); + void error(); private: - void aboutToClose(); void bytesWritten(qint64 bytes); void readyToRead(); }; diff --git a/src/plugins/qmltooling/shared/qqmldebugserver.h b/src/plugins/qmltooling/packetprotocol/qversionedpacket_p.h index 109f1e246c..635072adbc 100644 --- a/src/plugins/qmltooling/shared/qqmldebugserver.h +++ b/src/plugins/qmltooling/packetprotocol/qversionedpacket_p.h @@ -37,13 +37,10 @@ ** ****************************************************************************/ -#ifndef QQMLDEBUGSERVER_H -#define QQMLDEBUGSERVER_H +#ifndef QVERSIONEDPACKET_P_H +#define QVERSIONEDPACKET_P_H -#include <private/qqmldebugconnector_p.h> -#include <private/qtqmlglobal_p.h> - -#include <QtCore/QIODevice> +#include "qpacket_p.h" // // W A R N I N G @@ -58,13 +55,15 @@ QT_BEGIN_NAMESPACE -class QQmlDebugServer : public QQmlDebugConnector +// QPacket with a fixed data stream version, centrally set by some Connector +template<class Connector> +class QVersionedPacket : public QPacket { - Q_OBJECT public: - virtual void setDevice(QIODevice *socket) = 0; + QVersionedPacket(const QByteArray &ba) : QPacket(Connector::dataStreamVersion(), ba) {} + QVersionedPacket() : QPacket(Connector::dataStreamVersion()) {} }; QT_END_NAMESPACE -#endif // QQMLDEBUGSERVER_H +#endif // QVERSIONEDPACKET_P_H diff --git a/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro b/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro index f3f8a21ff8..2d8f0ceda2 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro +++ b/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro @@ -12,8 +12,6 @@ SOURCES += \ $$PWD/qv4debugjob.cpp HEADERS += \ - $$PWD/../shared/qqmlconfigurabledebugservice.h \ - $$PWD/../shared/qqmldebugpacket.h \ $$PWD/qqmldebuggerservicefactory.h \ $$PWD/qqmlenginedebugservice.h \ $$PWD/qqmlwatcher.h \ @@ -23,9 +21,6 @@ HEADERS += \ $$PWD/qv4datacollector.h \ $$PWD/qv4debugjob.h -INCLUDEPATH += $$PWD \ - $$PWD/../shared - OTHER_FILES += \ $$PWD/qqmldebuggerservice.json diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp index 9315adf4ce..3851cdc71f 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp @@ -52,7 +52,7 @@ QQmlDebugService *QQmlDebuggerServiceFactory::create(const QString &key) if (key == QV4DebugServiceImpl::s_key) return new QV4DebugServiceImpl(this); - return 0; + return nullptr; } QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index f0bb4de016..4c104f01de 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -39,7 +39,6 @@ #include "qqmlenginedebugservice.h" #include "qqmlwatcher.h" -#include "qqmldebugpacket.h" #include <private/qqmldebugstatesdelegate_p.h> #include <private/qqmlboundsignal_p.h> @@ -56,12 +55,52 @@ #include <QtCore/qdebug.h> #include <QtCore/qmetaobject.h> #include <QtCore/qfileinfo.h> +#include <QtCore/qjsonvalue.h> +#include <QtCore/qjsonobject.h> +#include <QtCore/qjsonarray.h> +#include <QtCore/qjsondocument.h> + #include <private/qmetaobject_p.h> +#include <private/qqmldebugconnector_p.h> +#include <private/qversionedpacket_p.h> QT_BEGIN_NAMESPACE +using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>; + +class NullDevice : public QIODevice +{ +public: + NullDevice() { open(QIODevice::ReadWrite); } + +protected: + qint64 readData(char *data, qint64 maxlen) final; + qint64 writeData(const char *data, qint64 len) final; +}; + +qint64 NullDevice::readData(char *data, qint64 maxlen) +{ + Q_UNUSED(data); + return maxlen; +} + +qint64 NullDevice::writeData(const char *data, qint64 len) +{ + Q_UNUSED(data); + return len; +} + +// check whether the data can be saved +// (otherwise we assert in QVariant::operator<< when actually saving it) +static bool isSaveable(const QVariant &value) +{ + NullDevice nullDevice; + QDataStream fakeStream(&nullDevice); + return QMetaType::save(fakeStream, static_cast<int>(value.type()), value.constData()); +} + QQmlEngineDebugServiceImpl::QQmlEngineDebugServiceImpl(QObject *parent) : - QQmlEngineDebugService(2, parent), m_watch(new QQmlWatcher(this)), m_statesDelegate(0) + QQmlEngineDebugService(2, parent), m_watch(new QQmlWatcher(this)), m_statesDelegate(nullptr) { connect(m_watch, &QQmlWatcher::propertyChanged, this, &QQmlEngineDebugServiceImpl::propertyChanged); @@ -98,13 +137,7 @@ QDataStream &operator<<(QDataStream &ds, const QQmlEngineDebugServiceImpl::QQmlObjectProperty &data) { ds << (int)data.type << data.name; - // check first whether the data can be saved - // (otherwise we assert in QVariant::operator<<) - QQmlDebugPacket fakeStream; - if (QMetaType::save(fakeStream, data.value.type(), data.value.constData())) - ds << data.value; - else - ds << QVariant(); + ds << (isSaveable(data.value) ? data.value : QVariant()); ds << data.valueTypeName << data.binding << data.hasNotifySignal; return ds; } @@ -208,21 +241,40 @@ QVariant QQmlEngineDebugServiceImpl::valueContents(QVariant value) const return contents; } - if (QQmlValueTypeFactory::isValueType(userType)) { - const QMetaObject *mo = QQmlValueTypeFactory::metaObjectForMetaType(userType); - if (mo) { - int toStringIndex = mo->indexOfMethod("toString"); - if (toStringIndex != -1) { - QMetaMethod mm = mo->method(toStringIndex); - QMetaType info(userType); - QString s; - if (info.flags() & QMetaType::IsGadget - && mm.invokeOnGadget(value.data(), Q_RETURN_ARG(QString, s))) - return s; + switch (userType) { + case QMetaType::QRect: + case QMetaType::QRectF: + case QMetaType::QPoint: + case QMetaType::QPointF: + case QMetaType::QSize: + case QMetaType::QSizeF: + case QMetaType::QFont: + // Don't call the toString() method on those. The stream operators are better. + return value; + case QMetaType::QJsonValue: + return value.toJsonValue().toVariant(); + case QMetaType::QJsonObject: + return value.toJsonObject().toVariantMap(); + case QMetaType::QJsonArray: + return value.toJsonArray().toVariantList(); + case QMetaType::QJsonDocument: + return value.toJsonDocument().toVariant(); + default: + if (QQmlValueTypeFactory::isValueType(userType)) { + const QMetaObject *mo = QQmlValueTypeFactory::metaObjectForMetaType(userType); + if (mo) { + int toStringIndex = mo->indexOfMethod("toString()"); + if (toStringIndex != -1) { + QMetaMethod mm = mo->method(toStringIndex); + QString s; + if (mm.invokeOnGadget(value.data(), Q_RETURN_ARG(QString, s))) + return s; + } } - } - return value; + if (isSaveable(value)) + return value; + } } if (QQmlMetaType::isQObject(userType)) { @@ -337,6 +389,9 @@ void QQmlEngineDebugServiceImpl::buildObjectList(QDataStream &message, QQmlContext *ctxt, const QList<QPointer<QObject> > &instances) { + if (!ctxt->isValid()) + return; + QQmlContextData *p = QQmlContextData::get(ctxt); QString ctxtName = ctxt->objectName(); @@ -399,11 +454,8 @@ QQmlEngineDebugServiceImpl::objectData(QObject *object) } QQmlContext *context = qmlContext(object); - if (context) { - QQmlContextData *cdata = QQmlContextData::get(context); - if (cdata) - rv.idString = cdata->findObjectId(object); - } + if (context && context->isValid()) + rv.idString = QQmlContextData::get(context)->findObjectId(object); rv.objectName = object->objectName(); rv.objectId = QQmlDebugService::idForObject(object); @@ -564,14 +616,14 @@ void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message) QObject *object = QQmlDebugService::objectForId(objectId); QQmlContext *context = qmlContext(object); - if (!context) { + if (!context || !context->isValid()) { QQmlEngine *engine = qobject_cast<QQmlEngine *>( QQmlDebugService::objectForId(engineId)); if (engine && m_engines.contains(engine)) context = engine->rootContext(); } QVariant result; - if (context) { + if (context && context->isValid()) { QQmlExpression exprObj(context, object, expr); bool undefined = false; QVariant value = exprObj.evaluate(&undefined); @@ -632,7 +684,7 @@ bool QQmlEngineDebugServiceImpl::setBinding(int objectId, QObject *object = objectForId(objectId); QQmlContext *context = qmlContext(object); - if (object && context) { + if (object && context && context->isValid()) { QQmlProperty property(object, propertyName, context); if (property.isValid()) { @@ -677,7 +729,7 @@ bool QQmlEngineDebugServiceImpl::resetBinding(int objectId, const QString &prope QObject *object = objectForId(objectId); QQmlContext *context = qmlContext(object); - if (object && context) { + if (object && context && context->isValid()) { QStringRef parentPropertyRef(&propertyName); const int idx = parentPropertyRef.indexOf(QLatin1Char('.')); if (idx != -1) @@ -695,8 +747,9 @@ bool QQmlEngineDebugServiceImpl::resetBinding(int objectId, const QString &prope property.reset(); } else { // overwrite with default value - if (QQmlType *objType = QQmlMetaType::qmlType(object->metaObject())) { - if (QObject *emptyObject = objType->create()) { + QQmlType objType = QQmlMetaType::qmlType(object->metaObject()); + if (objType.isValid()) { + if (QObject *emptyObject = objType.create()) { if (emptyObject->property(parentProperty).isValid()) { QVariant defaultValue = QQmlProperty(emptyObject, propertyName).read(); if (defaultValue.isValid()) { @@ -712,7 +765,7 @@ bool QQmlEngineDebugServiceImpl::resetBinding(int objectId, const QString &prope if (hasValidSignal(object, propertyName)) { QQmlProperty property(object, propertyName, context); - QQmlPropertyPrivate::setSignalExpression(property, 0); + QQmlPropertyPrivate::setSignalExpression(property, nullptr); return true; } @@ -731,11 +784,9 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth { QObject *object = objectForId(objectId); QQmlContext *context = qmlContext(object); - if (!object || !context || !context->engine()) + if (!object || !context || !context->isValid()) return false; QQmlContextData *contextData = QQmlContextData::get(context); - if (!contextData) - return false; QQmlPropertyData dummy; QQmlPropertyData *prop = @@ -759,7 +810,7 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(object); Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this - QV4::ExecutionEngine *v4 = QV8Engine::getV4(qmlEngine(object)->handle()); + QV4::ExecutionEngine *v4 = qmlEngine(object)->handle(); QV4::Scope scope(v4); int lineNumber = 0; @@ -800,7 +851,8 @@ void QQmlEngineDebugServiceImpl::engineAboutToBeRemoved(QJSEngine *engine) void QQmlEngineDebugServiceImpl::objectCreated(QJSEngine *engine, QObject *object) { Q_ASSERT(engine); - Q_ASSERT(m_engines.contains(engine)); + if (!m_engines.contains(engine)) + return; int engineId = QQmlDebugService::idForObject(engine); int objectId = QQmlDebugService::idForObject(object); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h index 2e40eb4de8..c0c24058eb 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h @@ -95,17 +95,17 @@ public: bool hasNotifySignal; }; - void engineAboutToBeAdded(QJSEngine *) Q_DECL_OVERRIDE; - void engineAboutToBeRemoved(QJSEngine *) Q_DECL_OVERRIDE; - void objectCreated(QJSEngine *, QObject *) Q_DECL_OVERRIDE; + void engineAboutToBeAdded(QJSEngine *) override; + void engineAboutToBeRemoved(QJSEngine *) override; + void objectCreated(QJSEngine *, QObject *) override; - void setStatesDelegate(QQmlDebugStatesDelegate *) Q_DECL_OVERRIDE; + void setStatesDelegate(QQmlDebugStatesDelegate *) override; signals: void scheduleMessage(const QByteArray &); protected: - virtual void messageReceived(const QByteArray &) Q_DECL_OVERRIDE; + void messageReceived(const QByteArray &) override; private: friend class QQmlDebuggerServiceFactory; diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp index cbbbb2ceb7..86571e6cbe 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp @@ -61,12 +61,12 @@ public: QObject *object, int debugId, const QMetaProperty &prop, - QQmlWatcher *parent = 0); + QQmlWatcher *parent = nullptr); QQmlWatchProxy(int id, QQmlExpression *exp, int debugId, - QQmlWatcher *parent = 0); + QQmlWatcher *parent = nullptr); public slots: void notifyValueChanged(); // Needs to be a slot because of QQmlPropertyPrivate::connect() @@ -86,7 +86,7 @@ QQmlWatchProxy::QQmlWatchProxy(int id, QQmlExpression *exp, int debugId, QQmlWatcher *parent) -: QObject(parent), m_id(id), m_watch(parent), m_object(0), m_debugId(debugId), m_expr(exp) +: QObject(parent), m_id(id), m_watch(parent), m_object(nullptr), m_debugId(debugId), m_expr(exp) { QObject::connect(m_expr, &QQmlExpression::valueChanged, this, &QQmlWatchProxy::notifyValueChanged); @@ -97,7 +97,7 @@ QQmlWatchProxy::QQmlWatchProxy(int id, int debugId, const QMetaProperty &prop, QQmlWatcher *parent) -: QObject(parent), m_id(id), m_watch(parent), m_object(object), m_debugId(debugId), m_property(prop), m_expr(0) +: QObject(parent), m_id(id), m_watch(parent), m_object(object), m_debugId(debugId), m_property(prop), m_expr(nullptr) { static int refreshIdx = -1; if(refreshIdx == -1) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp index e89b7a63d4..506ecb64bb 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -46,6 +46,7 @@ #include <private/qv4objectiterator_p.h> #include <private/qv4identifier_p.h> #include <private/qv4runtime_p.h> +#include <private/qv4identifiertable_p.h> #include <private/qqmlcontext_p.h> #include <private/qqmlengine_p.h> @@ -55,47 +56,39 @@ QT_BEGIN_NAMESPACE -QV4::SimpleCallContext *QV4DataCollector::findContext(int frame) +QV4::CppStackFrame *QV4DataCollector::findFrame(int frame) { - QV4::ExecutionContext *ctx = engine()->currentContext; - while (ctx) { - QV4::SimpleCallContext *cCtxt = ctx->asSimpleCallContext(); - if (cCtxt && cCtxt->d()->v4Function) { - if (frame < 1) - return cCtxt; - --frame; - } - ctx = engine()->parentContext(ctx); + QV4::CppStackFrame *f = engine()->currentStackFrame; + while (f && frame) { + --frame; + f = f->parent; } - - return 0; + return f; } -QV4::Heap::SimpleCallContext *QV4DataCollector::findScope(QV4::ExecutionContext *ctxt, int scope) +QV4::Heap::ExecutionContext *QV4DataCollector::findContext(int frame) { - if (!ctxt) - return 0; + QV4::CppStackFrame *f = findFrame(frame); - QV4::Scope s(ctxt); - QV4::ScopedContext ctx(s, ctxt); + return f ? f->context()->d() : nullptr; +} + +QV4::Heap::ExecutionContext *QV4DataCollector::findScope(QV4::Heap::ExecutionContext *ctx, int scope) +{ for (; scope > 0 && ctx; --scope) - ctx = ctx->d()->outer; + ctx = ctx->outer; - return (ctx && ctx->d()) ? ctx->asSimpleCallContext()->d() : 0; + return ctx; } QVector<QV4::Heap::ExecutionContext::ContextType> QV4DataCollector::getScopeTypes(int frame) { QVector<QV4::Heap::ExecutionContext::ContextType> types; - QV4::Scope scope(engine()); - QV4::SimpleCallContext *sctxt = findContext(frame); - if (!sctxt || sctxt->d()->type < QV4::Heap::ExecutionContext::Type_QmlContext) - return types; + QV4::Heap::ExecutionContext *it = findFrame(frame)->context()->d(); - QV4::ScopedContext it(scope, sctxt); - for (; it; it = it->d()->outer) - types.append(QV4::Heap::ExecutionContext::ContextType(it->d()->type)); + for (; it; it = it->outer) + types.append(QV4::Heap::ExecutionContext::ContextType(it->type)); return types; } @@ -104,58 +97,51 @@ int QV4DataCollector::encodeScopeType(QV4::Heap::ExecutionContext::ContextType s { switch (scopeType) { case QV4::Heap::ExecutionContext::Type_GlobalContext: - return 0; - case QV4::Heap::ExecutionContext::Type_CatchContext: - return 4; + break; case QV4::Heap::ExecutionContext::Type_WithContext: return 2; - case QV4::Heap::ExecutionContext::Type_SimpleCallContext: case QV4::Heap::ExecutionContext::Type_CallContext: return 1; case QV4::Heap::ExecutionContext::Type_QmlContext: - default: - return -1; + return 3; + case QV4::Heap::ExecutionContext::Type_BlockContext: + return 4; } + return 0; } QV4DataCollector::QV4DataCollector(QV4::ExecutionEngine *engine) - : m_engine(engine), m_namesAsObjects(true), m_redundantRefs(true) + : m_engine(engine) { m_values.set(engine, engine->newArrayObject()); } -// TODO: Directly call addRef() once we don't need to support redundantRefs anymore -QV4DataCollector::Ref QV4DataCollector::collect(const QV4::ScopedValue &value) +QV4DataCollector::Ref QV4DataCollector::addValueRef(const QV4::ScopedValue &value) { - Ref ref = addRef(value); - if (m_redundantRefs) - m_collectedRefs.append(ref); - return ref; + return addRef(value); } const QV4::Object *collectProperty(const QV4::ScopedValue &value, QV4::ExecutionEngine *engine, QJsonObject &dict) { QV4::Scope scope(engine); - QV4::ScopedValue typeString(scope, QV4::Runtime::method_typeofValue(engine, value)); + QV4::ScopedValue typeString(scope, QV4::Runtime::TypeofValue::call(engine, value)); dict.insert(QStringLiteral("type"), typeString->toQStringNoThrow()); const QLatin1String valueKey("value"); switch (value->type()) { case QV4::Value::Empty_Type: Q_ASSERT(!"empty Value encountered"); - return 0; + return nullptr; case QV4::Value::Undefined_Type: dict.insert(valueKey, QJsonValue::Undefined); - return 0; + return nullptr; case QV4::Value::Null_Type: - // "null" is not the correct type, but we leave this in until QtC can deal with "object" - dict.insert(QStringLiteral("type"), QStringLiteral("null")); dict.insert(valueKey, QJsonValue::Null); - return 0; + return nullptr; case QV4::Value::Boolean_Type: dict.insert(valueKey, value->booleanValue()); - return 0; + return nullptr; case QV4::Value::Managed_Type: if (const QV4::String *s = value->as<QV4::String>()) { dict.insert(valueKey, s->toQString()); @@ -168,25 +154,22 @@ const QV4::Object *collectProperty(const QV4::ScopedValue &value, QV4::Execution int numProperties = 0; QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly); QV4::PropertyAttributes attrs; - uint index; - QV4::ScopedProperty p(scope); - QV4::ScopedString name(scope); + QV4::ScopedPropertyKey name(scope); while (true) { - it.next(name.getRef(), &index, p, &attrs); - if (attrs.isEmpty()) + name = it.next(nullptr, &attrs); + if (!name->isValid()) break; - else - ++numProperties; + ++numProperties; } dict.insert(valueKey, numProperties); return o; } else { Q_UNREACHABLE(); } - return 0; + return nullptr; case QV4::Value::Integer_Type: dict.insert(valueKey, value->integerValue()); - return 0; + return nullptr; default: {// double const double val = value->doubleValue(); if (qIsFinite(val)) @@ -197,66 +180,26 @@ const QV4::Object *collectProperty(const QV4::ScopedValue &value, QV4::Execution dict.insert(valueKey, QStringLiteral("-Infinity")); else dict.insert(valueKey, QStringLiteral("Infinity")); - return 0; + return nullptr; } } } -QJsonObject QV4DataCollector::lookupRef(Ref ref, bool deep) +QJsonObject QV4DataCollector::lookupRef(Ref ref) { QJsonObject dict; - if (m_namesAsObjects) { - if (lookupSpecialRef(ref, &dict)) - return dict; - } - - if (m_redundantRefs) - deep = true; - dict.insert(QStringLiteral("handle"), qint64(ref)); QV4::Scope scope(engine()); QV4::ScopedValue value(scope, getValue(ref)); const QV4::Object *object = collectProperty(value, engine(), dict); - if (deep && object) + if (object) dict.insert(QStringLiteral("properties"), collectProperties(object)); return dict; } -// TODO: Drop this method once we don't need to support namesAsObjects anymore -QV4DataCollector::Ref QV4DataCollector::addFunctionRef(const QString &functionName) -{ - Q_ASSERT(m_namesAsObjects); - Ref ref = addRef(QV4::Primitive::emptyValue(), false); - - QJsonObject dict; - dict.insert(QStringLiteral("handle"), qint64(ref)); - dict.insert(QStringLiteral("type"), QStringLiteral("function")); - dict.insert(QStringLiteral("name"), functionName); - m_specialRefs.insert(ref, dict); - m_collectedRefs.append(ref); - - return ref; -} - -// TODO: Drop this method once we don't need to support namesAsObjects anymore -QV4DataCollector::Ref QV4DataCollector::addScriptRef(const QString &scriptName) -{ - Q_ASSERT(m_namesAsObjects); - Ref ref = addRef(QV4::Primitive::emptyValue(), false); - - QJsonObject dict; - dict.insert(QStringLiteral("handle"), qint64(ref)); - dict.insert(QStringLiteral("type"), QStringLiteral("script")); - dict.insert(QStringLiteral("name"), scriptName); - m_specialRefs.insert(ref, dict); - m_collectedRefs.append(ref); - - return ref; -} - bool QV4DataCollector::isValidRef(QV4DataCollector::Ref ref) const { QV4::Scope scope(engine()); @@ -266,50 +209,35 @@ bool QV4DataCollector::isValidRef(QV4DataCollector::Ref ref) const bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr) { - QStringList names; - QV4::Scope scope(engine()); - QV4::Scoped<QV4::CallContext> ctxt(scope, findScope(findContext(frameNr), scopeNr)); + + QV4::Scoped<QV4::ExecutionContext> ctxt(scope, findScope(findContext(frameNr), scopeNr)); if (!ctxt) return false; - Refs collectedRefs; - QV4::ScopedValue v(scope); - int nFormals = ctxt->formalCount(); - for (unsigned i = 0, ei = nFormals; i != ei; ++i) { - QString qName; - if (QV4::Identifier *name = ctxt->formals()[nFormals - i - 1]) - qName = name->string; - names.append(qName); - v = ctxt->argument(i); - collectedRefs.append(collect(v)); - } - - for (unsigned i = 0, ei = ctxt->variableCount(); i != ei; ++i) { - QString qName; - if (QV4::Identifier *name = ctxt->variables()[i]) - qName = name->string; - names.append(qName); - v = ctxt->d()->locals[i]; - collectedRefs.append(collect(v)); - } - QV4::ScopedObject scopeObject(scope, engine()->newObject()); + if (ctxt->d()->type == QV4::Heap::ExecutionContext::Type_CallContext) { + QStringList names; + Refs collectedRefs; + + QV4::ScopedValue v(scope); + QV4::Heap::InternalClass *ic = ctxt->internalClass(); + for (uint i = 0; i < ic->size; ++i) { + QString name = ic->keyAt(i); + names.append(name); + v = static_cast<QV4::Heap::CallContext *>(ctxt->d())->locals[i]; + collectedRefs.append(addValueRef(v)); + } - Q_ASSERT(names.size() == collectedRefs.size()); - QV4::ScopedString propName(scope); - for (int i = 0, ei = collectedRefs.size(); i != ei; ++i) { - propName = engine()->newString(names.at(i)); - scopeObject->put(propName, QV4::Value::fromReturnedValue(getValue(collectedRefs.at(i)))); + Q_ASSERT(names.size() == collectedRefs.size()); + QV4::ScopedString propName(scope); + for (int i = 0, ei = collectedRefs.size(); i != ei; ++i) { + propName = engine()->newString(names.at(i)); + scopeObject->put(propName, (v = getValue(collectedRefs.at(i)))); + } } - Ref scopeObjectRef = addRef(scopeObject); - if (m_redundantRefs) { - dict->insert(QStringLiteral("ref"), qint64(scopeObjectRef)); - m_collectedRefs.append(scopeObjectRef); - } else { - *dict = lookupRef(scopeObjectRef, true); - } + *dict = lookupRef(addRef(scopeObject)); return true; } @@ -325,13 +253,8 @@ QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int QJsonObject frame; frame[QLatin1String("index")] = frameNr; frame[QLatin1String("debuggerFrame")] = false; - if (m_namesAsObjects) { - frame[QLatin1String("func")] = toRef(addFunctionRef(stackFrame.function)); - frame[QLatin1String("script")] = toRef(addScriptRef(stackFrame.source)); - } else { - frame[QLatin1String("func")] = stackFrame.function; - frame[QLatin1String("script")] = stackFrame.source; - } + frame[QLatin1String("func")] = stackFrame.function; + frame[QLatin1String("script")] = stackFrame.source; frame[QLatin1String("line")] = stackFrame.line - 1; if (stackFrame.column >= 0) frame[QLatin1String("column")] = stackFrame.column; @@ -340,7 +263,7 @@ QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int QV4::Scope scope(engine()); QV4::ScopedContext ctxt(scope, findContext(frameNr)); while (ctxt) { - if (QV4::SimpleCallContext *cCtxt = ctxt->asSimpleCallContext()) { + if (QV4::CallContext *cCtxt = ctxt->asCallContext()) { if (cCtxt->d()->activation) break; } @@ -348,8 +271,8 @@ QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int } if (ctxt) { - QV4::ScopedValue o(scope, ctxt->asSimpleCallContext()->d()->activation); - frame[QLatin1String("receiver")] = toRef(collect(o)); + QV4::ScopedValue o(scope, ctxt->d()->activation); + frame[QLatin1String("receiver")] = toRef(addValueRef(o)); } // Only type and index are used by Qt Creator, so we keep it easy: @@ -370,30 +293,9 @@ QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int return frame; } -// TODO: Drop this method once we don't need to support redundantRefs anymore -QJsonArray QV4DataCollector::flushCollectedRefs() -{ - Q_ASSERT(m_redundantRefs); - QJsonArray refs; - std::sort(m_collectedRefs.begin(), m_collectedRefs.end()); - for (int i = 0, ei = m_collectedRefs.size(); i != ei; ++i) { - QV4DataCollector::Ref ref = m_collectedRefs.at(i); - if (i > 0 && ref == m_collectedRefs.at(i - 1)) - continue; - refs.append(lookupRef(ref, true)); - } - - m_collectedRefs.clear(); - return refs; -} - void QV4DataCollector::clear() { m_values.set(engine(), engine()->newArrayObject()); - m_collectedRefs.clear(); - m_specialRefs.clear(); - m_namesAsObjects = true; - m_redundantRefs = true; } QV4DataCollector::Ref QV4DataCollector::addRef(QV4::Value value, bool deduplicate) @@ -413,18 +315,18 @@ QV4DataCollector::Ref QV4DataCollector::addRef(QV4::Value value, bool deduplicat { std::swap(*hasExceptionLoc, hadException); } }; - // if we wouldn't do this, the putIndexed won't work. + // if we wouldn't do this, the put won't work. ExceptionStateSaver resetExceptionState(engine()); QV4::Scope scope(engine()); QV4::ScopedObject array(scope, m_values.value()); if (deduplicate) { for (Ref i = 0; i < array->getLength(); ++i) { - if (array->getIndexed(i) == value.rawValue() && !m_specialRefs.contains(i)) + if (array->get(i) == value.rawValue()) return i; } } Ref ref = array->getLength(); - array->putIndexed(ref, value); + array->put(ref, value); Q_ASSERT(array->getLength() - 1 == ref); return ref; } @@ -434,23 +336,39 @@ QV4::ReturnedValue QV4DataCollector::getValue(Ref ref) QV4::Scope scope(engine()); QV4::ScopedObject array(scope, m_values.value()); Q_ASSERT(ref < array->getLength()); - return array->getIndexed(ref, Q_NULLPTR); + return array->get(ref, nullptr); } -// TODO: Drop this method once we don't need to support namesAsObjects anymore -bool QV4DataCollector::lookupSpecialRef(Ref ref, QJsonObject *dict) +class CapturePreventer { - Q_ASSERT(m_namesAsObjects); - SpecialRefs::const_iterator it = m_specialRefs.constFind(ref); - if (it == m_specialRefs.cend()) - return false; +public: + CapturePreventer(QV4::ExecutionEngine *engine) + { + if (QQmlEngine *e = engine->qmlEngine()) { + m_engine = QQmlEnginePrivate::get(e); + m_capture = m_engine->propertyCapture; + m_engine->propertyCapture = nullptr; + } + } - *dict = it.value(); - return true; -} + ~CapturePreventer() + { + if (m_engine && m_capture) { + Q_ASSERT(!m_engine->propertyCapture); + m_engine->propertyCapture = m_capture; + } + } + +private: + QQmlEnginePrivate *m_engine = nullptr; + QQmlPropertyCapture *m_capture = nullptr; +}; QJsonArray QV4DataCollector::collectProperties(const QV4::Object *object) { + CapturePreventer capturePreventer(engine()); + Q_UNUSED(capturePreventer); + QJsonArray res; QV4::Scope scope(engine()); @@ -478,8 +396,6 @@ QJsonObject QV4DataCollector::collectAsJson(const QString &name, const QV4::Scop if (value->isManaged() && !value->isString()) { Ref ref = addRef(value); dict.insert(QStringLiteral("ref"), qint64(ref)); - if (m_redundantRefs) - m_collectedRefs.append(ref); } collectProperty(value, engine(), dict); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h index de12e8d527..bc178fa2db 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h @@ -58,50 +58,36 @@ public: typedef uint Ref; typedef QVector<uint> Refs; - static QV4::Heap::SimpleCallContext *findScope(QV4::ExecutionContext *ctxt, int scope); + static QV4::Heap::ExecutionContext *findScope(QV4::Heap::ExecutionContext *ctxt, int scope); static int encodeScopeType(QV4::Heap::ExecutionContext::ContextType scopeType); QVector<QV4::Heap::ExecutionContext::ContextType> getScopeTypes(int frame); - QV4::SimpleCallContext *findContext(int frame); + QV4::Heap::ExecutionContext *findContext(int frame); + QV4::CppStackFrame *findFrame(int frame); QV4DataCollector(QV4::ExecutionEngine *engine); - Ref collect(const QV4::ScopedValue &value); // only for redundantRefs - Ref addFunctionRef(const QString &functionName); // only for namesAsObjects - Ref addScriptRef(const QString &scriptName); // only for namesAsObjects - - void setNamesAsObjects(bool namesAsObjects) { m_namesAsObjects = namesAsObjects; } - bool namesAsObjects() const { return m_namesAsObjects; } - - void setRedundantRefs(bool redundantRefs) { m_redundantRefs = redundantRefs; } - bool redundantRefs() const { return m_redundantRefs; } + Ref addValueRef(const QV4::ScopedValue &value); bool isValidRef(Ref ref) const; - QJsonObject lookupRef(Ref ref, bool deep); + QJsonObject lookupRef(Ref ref); bool collectScope(QJsonObject *dict, int frameNr, int scopeNr); QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr); QV4::ExecutionEngine *engine() const { return m_engine; } - QJsonArray flushCollectedRefs(); // only for redundantRefs void clear(); private: Ref addRef(QV4::Value value, bool deduplicate = true); QV4::ReturnedValue getValue(Ref ref); - bool lookupSpecialRef(Ref ref, QJsonObject *dict); // only for namesAsObjects QJsonArray collectProperties(const QV4::Object *object); QJsonObject collectAsJson(const QString &name, const QV4::ScopedValue &value); void collectArgumentsInContext(); QV4::ExecutionEngine *m_engine; - Refs m_collectedRefs; // only for redundantRefs QV4::PersistentValue m_values; - typedef QHash<Ref, QJsonObject> SpecialRefs; // only for namesAsObjects - SpecialRefs m_specialRefs; // only for namesAsObjects - bool m_namesAsObjects; - bool m_redundantRefs; }; QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp index b82df9c6a9..5521e7628b 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp @@ -70,9 +70,9 @@ QV4Debugger::QV4Debugger(QV4::ExecutionEngine *engine) , m_pauseRequested(false) , m_haveBreakPoints(false) , m_breakOnThrow(false) - , m_returnedValue(engine, QV4::Primitive::undefinedValue()) - , m_gatherSources(0) - , m_runningJob(0) + , m_returnedValue(engine, QV4::Value::undefinedValue()) + , m_gatherSources(nullptr) + , m_runningJob(nullptr) , m_collector(engine) { static int debuggerId = qRegisterMetaType<QV4Debugger*>(); @@ -115,7 +115,7 @@ void QV4Debugger::resume(Speed speed) if (!m_returnedValue.isUndefined()) m_returnedValue.set(m_engine, QV4::Encode::undefined()); - m_currentContext.set(m_engine, *m_engine->currentContext); + m_currentFrame = m_engine->currentStackFrame; m_stepping = speed; m_runningCondition.wakeAll(); } @@ -157,8 +157,8 @@ void QV4Debugger::clearPauseRequest() QV4Debugger::ExecutionState QV4Debugger::currentExecutionState() const { ExecutionState state; - state.fileName = getFunction()->sourceFile(); - state.lineNumber = engine()->current->lineNumber; + state.fileName = QUrl(getFunction()->sourceFile()).fileName(); + state.lineNumber = engine()->currentStackFrame->lineNumber(); return state; } @@ -182,14 +182,14 @@ void QV4Debugger::maybeBreakAtInstruction() if (m_gatherSources) { m_gatherSources->run(); delete m_gatherSources; - m_gatherSources = 0; + m_gatherSources = nullptr; } switch (m_stepping) { case StepOver: - if (m_currentContext.asManaged()->d() != m_engine->current) + if (m_currentFrame != m_engine->currentStackFrame) break; - // fall through + Q_FALLTHROUGH(); case StepIn: pauseAndWait(Step); return; @@ -203,7 +203,8 @@ void QV4Debugger::maybeBreakAtInstruction() pauseAndWait(PauseRequest); } else if (m_haveBreakPoints) { if (QV4::Function *f = getFunction()) { - const int lineNumber = engine()->current->lineNumber; + // lineNumber will be negative for Ret instructions, so those won't match + const int lineNumber = engine()->currentStackFrame->lineNumber(); if (reallyHitTheBreakPoint(f->sourceFile(), lineNumber)) pauseAndWait(BreakPointHit); } @@ -216,9 +217,8 @@ void QV4Debugger::enteringFunction() return; QMutexLocker locker(&m_lock); - if (m_stepping == StepIn) { - m_currentContext.set(m_engine, *m_engine->currentContext); - } + if (m_stepping == StepIn) + m_currentFrame = m_engine->currentStackFrame; } void QV4Debugger::leavingFunction(const QV4::ReturnedValue &retVal) @@ -229,13 +229,8 @@ void QV4Debugger::leavingFunction(const QV4::ReturnedValue &retVal) QMutexLocker locker(&m_lock); - if (m_stepping != NotStepping && m_currentContext.asManaged()->d() == m_engine->current) { - if (QV4::ExecutionContext *parentContext - = m_engine->parentContext(m_engine->currentContext)) { - m_currentContext.set(m_engine, *parentContext); - } else { - m_currentContext.clear(); - } + if (m_stepping != NotStepping && m_currentFrame == m_engine->currentStackFrame) { + m_currentFrame = m_currentFrame->parent; m_stepping = StepOver; m_returnedValue.set(m_engine, retVal); } @@ -255,10 +250,8 @@ void QV4Debugger::aboutToThrow() QV4::Function *QV4Debugger::getFunction() const { - QV4::Scope scope(m_engine); - QV4::ExecutionContext *context = m_engine->currentContext; - if (QV4::Function *function = context->getFunction()) - return function; + if (m_engine->currentStackFrame) + return m_engine->currentStackFrame->v4Function; else return m_engine->globalCode; } @@ -295,18 +288,18 @@ void QV4Debugger::pauseAndWait(PauseReason reason) bool QV4Debugger::reallyHitTheBreakPoint(const QString &filename, int linenr) { QHash<BreakPoint, QString>::iterator it = m_breakPoints.find( - BreakPoint(filename.mid(filename.lastIndexOf('/') + 1), linenr)); + BreakPoint(QUrl(filename).fileName(), linenr)); if (it == m_breakPoints.end()) return false; QString condition = it.value(); if (condition.isEmpty()) return true; - Q_ASSERT(m_runningJob == 0); + Q_ASSERT(m_runningJob == nullptr); EvalJob evilJob(m_engine, condition); m_runningJob = &evilJob; m_runningJob->run(); - m_runningJob = 0; + m_runningJob = nullptr; return evilJob.resultAsBoolean(); } @@ -320,7 +313,7 @@ void QV4Debugger::runInEngine(QV4DebugJob *job) void QV4Debugger::runInEngine_havingLock(QV4DebugJob *job) { Q_ASSERT(job); - Q_ASSERT(m_runningJob == 0); + Q_ASSERT(m_runningJob == nullptr); m_runningJob = job; if (state() == Paused) @@ -328,7 +321,7 @@ void QV4Debugger::runInEngine_havingLock(QV4DebugJob *job) else emit scheduleJob(); m_jobIsRunning.wait(&m_lock); - m_runningJob = 0; + m_runningJob = nullptr; } QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h index cd412e573d..4a755f2b72 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h @@ -129,14 +129,14 @@ public: void runInEngine(QV4DebugJob *job); // compile-time interface - void maybeBreakAtInstruction() Q_DECL_OVERRIDE; + void maybeBreakAtInstruction() override; // execution hooks - void enteringFunction() Q_DECL_OVERRIDE; - void leavingFunction(const QV4::ReturnedValue &retVal) Q_DECL_OVERRIDE; - void aboutToThrow() Q_DECL_OVERRIDE; + void enteringFunction() override; + void leavingFunction(const QV4::ReturnedValue &retVal) override; + void aboutToThrow() override; - bool pauseAtNextOpportunity() const Q_DECL_OVERRIDE; + bool pauseAtNextOpportunity() const override; signals: void debuggerPaused(QV4Debugger *self, QV4Debugger::PauseReason reason); @@ -150,7 +150,7 @@ private: void runJobUnpaused(); QV4::ExecutionEngine *m_engine; - QV4::PersistentValue m_currentContext; + QV4::CppStackFrame *m_currentFrame = 0; QMutex m_lock; QWaitCondition m_runningCondition; State m_state; diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp index 9a34d5770a..71645579c5 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp @@ -56,13 +56,13 @@ QV4Debugger *QV4DebuggerAgent::pausedDebugger() const if (debugger->state() == QV4Debugger::Paused) return debugger; } - return 0; + return nullptr; } bool QV4DebuggerAgent::isRunning() const { // "running" means none of the engines are paused. - return pausedDebugger() == 0; + return pausedDebugger() == nullptr; } void QV4DebuggerAgent::debuggerPaused(QV4Debugger *debugger, QV4Debugger::PauseReason reason) @@ -79,20 +79,19 @@ void QV4DebuggerAgent::debuggerPaused(QV4Debugger *debugger, QV4Debugger::PauseR case QV4Debugger::PauseRequest: case QV4Debugger::BreakPointHit: { event.insert(QStringLiteral("event"), QStringLiteral("break")); - QVector<QV4::StackFrame> frames = debugger->stackTrace(1); - if (frames.isEmpty()) + QV4::CppStackFrame *frame = debugger->engine()->currentStackFrame; + if (!frame) break; - const QV4::StackFrame &topFrame = frames.first(); - body.insert(QStringLiteral("invocationText"), topFrame.function); - body.insert(QStringLiteral("sourceLine"), topFrame.line - 1); - if (topFrame.column > 0) - body.insert(QStringLiteral("sourceColumn"), topFrame.column); + body.insert(QStringLiteral("invocationText"), frame->function()); + body.insert(QStringLiteral("sourceLine"), qAbs(frame->lineNumber()) - 1); +// if (frame->column > 0) +// body.insert(QStringLiteral("sourceColumn"), frame->column); QJsonArray breakPoints; - foreach (int breakPointId, breakPointIds(topFrame.source, topFrame.line)) + foreach (int breakPointId, breakPointIds(frame->source(), frame->lineNumber())) breakPoints.push_back(breakPointId); body.insert(QStringLiteral("breakpoints"), breakPoints); - script.insert(QStringLiteral("name"), topFrame.source); + script.insert(QStringLiteral("name"), frame->source()); } break; case QV4Debugger::Throwing: // TODO: complete this! diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp index 107ec60943..b424ef9f6c 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp @@ -44,6 +44,7 @@ #include <private/qv4qmlcontext_p.h> #include <private/qv4qobjectwrapper_p.h> #include <private/qqmldebugservice_p.h> +#include <private/qv4jscall_p.h> #include <QtQml/qqmlengine.h> @@ -63,26 +64,21 @@ void JavaScriptJob::run() { QV4::Scope scope(engine); - QV4::ExecutionContextSaver saver(scope); - - QV4::ExecutionContext *ctx = engine->currentContext; + QV4::ScopedContext ctx(scope, engine->currentStackFrame ? engine->currentContext() + : engine->scriptContext()); QObject scopeObject; - if (frameNr > 0) { - for (int i = 0; i < frameNr; ++i) { - ctx = engine->parentContext(ctx); - } - engine->pushContext(ctx); - ctx = engine->currentContext; - } + QV4::CppStackFrame *frame = engine->currentStackFrame; + + for (int i = 0; frame && i < frameNr; ++i) + frame = frame->parent; + if (frameNr > 0 && frame) + ctx = static_cast<QV4::ExecutionContext *>(&frame->jsFrame->context); if (context >= 0) { QQmlContext *extraContext = qmlContext(QQmlDebugService::objectForId(context)); - if (extraContext) { - engine->pushContext(QV4::QmlContext::create(ctx, QQmlContextData::get(extraContext), - &scopeObject)); - ctx = engine->currentContext; - } + if (extraContext) + ctx = QV4::QmlContext::create(ctx, QQmlContextData::get(extraContext), &scopeObject); } else if (frameNr < 0) { // Use QML context if available QQmlEngine *qmlEngine = engine->qmlEngine(); if (qmlEngine) { @@ -102,25 +98,28 @@ void JavaScriptJob::run() } } } - if (!engine->qmlContext()) { - engine->pushContext(QV4::QmlContext::create(ctx, QQmlContextData::get(qmlRootContext), - &scopeObject)); - ctx = engine->currentContext; - } - engine->pushContext(ctx->newWithContext(withContext->toObject(engine))); - ctx = engine->currentContext; + if (!engine->qmlContext()) + ctx = QV4::QmlContext::create(ctx, QQmlContextData::get(qmlRootContext), &scopeObject); } } - QV4::Script script(ctx, this->script); - script.strictMode = ctx->d()->strictMode; + QV4::Script script(ctx, QV4::Compiler::ContextType::Eval, this->script); + if (const QV4::Function *function = frame ? frame->v4Function : engine->globalCode) + script.strictMode = function->isStrict(); + // In order for property lookups in QML to work, we need to disable fast v4 lookups. That // is a side-effect of inheritContext. script.inheritContext = true; script.parse(); QV4::ScopedValue result(scope); - if (!scope.engine->hasException) - result = script.run(); + if (!scope.engine->hasException) { + if (frame) { + QV4::ScopedValue thisObject(scope, frame->thisObject()); + result = script.run(thisObject); + } else { + result = script.run(); + } + } if (scope.engine->hasException) { result = scope.engine->catchException(); resultIsException = true; @@ -151,7 +150,6 @@ void BacktraceJob::run() result.insert(QStringLiteral("toFrame"), fromFrame + frameArray.size()); result.insert(QStringLiteral("frames"), frameArray); } - flushRedundantRefs(); } FrameJob::FrameJob(QV4DataCollector *collector, int frameNr) : @@ -166,7 +164,6 @@ void FrameJob::run() success = false; } else { result = collector->buildFrame(frames[frameNr], frameNr); - flushRedundantRefs(); success = true; } } @@ -196,7 +193,6 @@ void ScopeJob::run() result[QLatin1String("index")] = scopeNr; result[QLatin1String("frameIndex")] = frameNr; result[QLatin1String("object")] = object; - flushRedundantRefs(); } bool ScopeJob::wasSuccessful() const @@ -214,23 +210,23 @@ void ValueLookupJob::run() // set if the engine is currently executing QML code. QScopedPointer<QObject> scopeObject; QV4::ExecutionEngine *engine = collector->engine(); + QV4::Scope scope(engine); + QV4::Heap::ExecutionContext *qmlContext = nullptr; if (engine->qmlEngine() && !engine->qmlContext()) { scopeObject.reset(new QObject); - engine->pushContext(QV4::QmlContext::create(engine->currentContext, + qmlContext = QV4::QmlContext::create(engine->currentContext(), QQmlContextData::get(engine->qmlEngine()->rootContext()), - scopeObject.data())); + scopeObject.data()); } + QV4::ScopedStackFrame frame(scope, qmlContext); for (const QJsonValue &handle : handles) { QV4DataCollector::Ref ref = handle.toInt(); if (!collector->isValidRef(ref)) { exception = QString::fromLatin1("Invalid Ref: %1").arg(ref); break; } - result[QString::number(ref)] = collector->lookupRef(ref, true); + result[QString::number(ref)] = collector->lookupRef(ref); } - flushRedundantRefs(); - if (scopeObject) - engine->popContext(); } const QString &ValueLookupJob::exceptionMessage() const @@ -249,9 +245,7 @@ void ExpressionEvalJob::handleResult(QV4::ScopedValue &value) { if (hasExeption()) exception = value->toQStringNoThrow(); - result = collector->lookupRef(collector->collect(value), true); - if (collector->redundantRefs()) - collectedRefs = collector->flushCollectedRefs(); + result = collector->lookupRef(collector->addValueRef(value)); } const QString &ExpressionEvalJob::exceptionMessage() const @@ -264,20 +258,13 @@ const QJsonObject &ExpressionEvalJob::returnValue() const return result; } -// TODO: Drop this method once we don't need to support redundantRefs anymore -const QJsonArray &ExpressionEvalJob::refs() const -{ - Q_ASSERT(collector->redundantRefs()); - return collectedRefs; -} - GatherSourcesJob::GatherSourcesJob(QV4::ExecutionEngine *engine) : engine(engine) {} void GatherSourcesJob::run() { - for (QV4::CompiledData::CompilationUnit *unit : qAsConst(engine->compilationUnits)) { + for (QV4::CompiledData::CompilationUnit *unit : engine->compilationUnits) { QString fileName = unit->fileName(); if (!fileName.isEmpty()) sources.append(fileName); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h index eca8710e15..d1c7495863 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h @@ -78,24 +78,10 @@ class CollectJob : public QV4DebugJob protected: QV4DataCollector *collector; QJsonObject result; - QJsonArray collectedRefs; // only for redundantRefs - - void flushRedundantRefs() - { - if (collector->redundantRefs()) - collectedRefs = collector->flushCollectedRefs(); - } public: CollectJob(QV4DataCollector *collector) : collector(collector) {} const QJsonObject &returnValue() const { return result; } - - // TODO: Drop this method once we don't need to support redundantRefs anymore - const QJsonArray &refs() const - { - Q_ASSERT(collector->redundantRefs()); - return collectedRefs; - } }; class BacktraceJob: public CollectJob @@ -146,7 +132,6 @@ class ExpressionEvalJob: public JavaScriptJob QV4DataCollector *collector; QString exception; QJsonObject result; - QJsonArray collectedRefs; // only for redundantRefs public: ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr, int context, @@ -154,7 +139,6 @@ public: void handleResult(QV4::ScopedValue &value) override; const QString &exceptionMessage() const; const QJsonObject &returnValue() const; - const QJsonArray &refs() const; // only for redundantRefs }; class GatherSourcesJob: public QV4DebugJob diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp index 168a08865c..5866163ca6 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp @@ -40,13 +40,12 @@ #include "qv4debugservice.h" #include "qv4debugjob.h" #include "qqmlengine.h" -#include "qqmldebugpacket.h" #include <private/qv4engine_p.h> -#include <private/qv4isel_moth_p.h> #include <private/qv4function_p.h> #include <private/qqmldebugconnector_p.h> #include <private/qv8engine_p.h> +#include <private/qversionedpacket_p.h> #include <QtCore/QJsonArray> #include <QtCore/QJsonDocument> @@ -68,19 +67,21 @@ const char *const V4_PAUSE = "interrupt"; QT_BEGIN_NAMESPACE -class V8CommandHandler; -class UnknownV8CommandHandler; +class V4CommandHandler; +class UnknownV4CommandHandler; + +using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>; int QV4DebugServiceImpl::sequence = 0; -class V8CommandHandler +class V4CommandHandler { public: - V8CommandHandler(const QString &command) + V4CommandHandler(const QString &command) : cmd(command) {} - virtual ~V8CommandHandler() + virtual ~V4CommandHandler() {} QString command() const { return cmd; } @@ -99,7 +100,7 @@ public: debugService->send(response); } - debugService = 0; + debugService = nullptr; seq = QJsonValue(); req = QJsonObject(); response = QJsonObject(); @@ -121,21 +122,6 @@ protected: response.insert(QStringLiteral("running"), debugService->debuggerAgent.isRunning()); } - QV4DataCollector *saneCollector(QV4Debugger *debugger) - { - QV4DataCollector *collector = debugger->collector(); - collector->setNamesAsObjects(debugService->clientRequiresNamesAsObjects()); - collector->setRedundantRefs(debugService->clientRequiresRedundantRefs()); - return collector; - } - - // TODO: drop this method once we don't need to support redundantRefs anymore. - void addRefs(const QJsonArray &refs) - { - Q_ASSERT(debugService->clientRequiresRedundantRefs()); - response.insert(QStringLiteral("refs"), refs); - } - void createErrorResponse(const QString &msg) { QJsonValue command = req.value(QLatin1String("command")); @@ -157,10 +143,10 @@ protected: QJsonObject response; }; -class UnknownV8CommandHandler: public V8CommandHandler +class UnknownV4CommandHandler: public V4CommandHandler { public: - UnknownV8CommandHandler(): V8CommandHandler(QString()) {} + UnknownV4CommandHandler(): V4CommandHandler(QString()) {} void handleRequest() override { @@ -172,10 +158,10 @@ public: }; namespace { -class V8VersionRequest: public V8CommandHandler +class V4VersionRequest: public V4CommandHandler { public: - V8VersionRequest(): V8CommandHandler(QStringLiteral("version")) {} + V4VersionRequest(): V4CommandHandler(QStringLiteral("version")) {} void handleRequest() override { @@ -188,98 +174,137 @@ public: QLatin1String("this is not V8, this is V4 in Qt " QT_VERSION_STR)); body.insert(QStringLiteral("UnpausedEvaluate"), true); body.insert(QStringLiteral("ContextEvaluate"), true); + body.insert(QStringLiteral("ChangeBreakpoint"), true); addBody(body); } }; -class V8SetBreakPointRequest: public V8CommandHandler +class V4BreakPointRequest: public V4CommandHandler { public: - V8SetBreakPointRequest(): V8CommandHandler(QStringLiteral("setbreakpoint")) {} + V4BreakPointRequest(const QString &name): V4CommandHandler(name) {} - void handleRequest() override + void handleRequest() final { + // Other types are currently not supported + m_type = QStringLiteral("scriptRegExp"); + // decypher the payload: - QJsonObject args = req.value(QLatin1String("arguments")).toObject(); - if (args.isEmpty()) + m_args = req.value(QLatin1String("arguments")).toObject(); + if (m_args.isEmpty()) { + createErrorResponse(QStringLiteral("breakpoint request with empty arguments object")); return; + } + + const int id = handleBreakPointRequest(); + if (id < 0) { + createErrorResponse(m_error); + } else { + // response: + addCommand(); + addRequestSequence(); + addSuccess(true); + addRunning(); + QJsonObject body; + body.insert(QStringLiteral("type"), m_type); + body.insert(QStringLiteral("breakpoint"), id); + addBody(body); + } + } + +protected: + virtual int handleBreakPointRequest() = 0; - QString type = args.value(QLatin1String("type")).toString(); + QJsonObject m_args; + QString m_type; + QString m_error; +}; + +class V4SetBreakPointRequest: public V4BreakPointRequest +{ +public: + V4SetBreakPointRequest(): V4BreakPointRequest(QStringLiteral("setbreakpoint")) {} + + int handleBreakPointRequest() final + { + // decypher the payload: + const QString type = m_args.value(QLatin1String("type")).toString(); if (type != QLatin1String("scriptRegExp")) { - createErrorResponse(QStringLiteral("breakpoint type \"%1\" is not implemented").arg(type)); - return; + m_error = QStringLiteral("breakpoint type \"%1\" is not implemented").arg(type); + return -1; } - QString fileName = args.value(QLatin1String("target")).toString(); + const QString fileName = m_args.value(QLatin1String("target")).toString(); if (fileName.isEmpty()) { - createErrorResponse(QStringLiteral("breakpoint has no file name")); - return; + m_error = QStringLiteral("breakpoint has no file name"); + return -1; } - int line = args.value(QLatin1String("line")).toInt(-1); + + const int line = m_args.value(QLatin1String("line")).toInt(-1); if (line < 0) { - createErrorResponse(QStringLiteral("breakpoint has an invalid line number")); - return; + m_error = QStringLiteral("breakpoint has an invalid line number"); + return -1; } - bool enabled = args.value(QStringLiteral("enabled")).toBool(true); - QString condition = args.value(QStringLiteral("condition")).toString(); + const bool enabled = m_args.value(QStringLiteral("enabled")).toBool(true); + const QString condition = m_args.value(QStringLiteral("condition")).toString(); // set the break point: - int id = debugService->debuggerAgent.addBreakPoint(fileName, line + 1, enabled, condition); + return debugService->debuggerAgent.addBreakPoint(fileName, line + 1, enabled, condition); - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - QJsonObject body; - body.insert(QStringLiteral("type"), type); - body.insert(QStringLiteral("breakpoint"), id); // It's undocumented, but V8 sends back an actual_locations array too. However, our // Debugger currently doesn't tell us when it resolved a breakpoint, so we'll leave them // pending until the breakpoint is hit for the first time. - addBody(body); } }; -class V8ClearBreakPointRequest: public V8CommandHandler +class V4ClearBreakPointRequest: public V4BreakPointRequest { public: - V8ClearBreakPointRequest(): V8CommandHandler(QStringLiteral("clearbreakpoint")) {} + V4ClearBreakPointRequest(): V4BreakPointRequest(QStringLiteral("clearbreakpoint")) {} - void handleRequest() override + int handleBreakPointRequest() final { - // decypher the payload: - QJsonObject args = req.value(QLatin1String("arguments")).toObject(); - if (args.isEmpty()) - return; + const int id = m_args.value(QLatin1String("breakpoint")).toInt(-1); + if (id < 0) + m_error = QStringLiteral("breakpoint has an invalid number"); + else // remove the break point: + debugService->debuggerAgent.removeBreakPoint(id); - int id = args.value(QLatin1String("breakpoint")).toInt(-1); + return id; + } +}; + +class V4ChangeBreakPointRequest: public V4BreakPointRequest +{ +public: + V4ChangeBreakPointRequest(): V4BreakPointRequest(QStringLiteral("changebreakpoint")) {} + + int handleBreakPointRequest() final + { + const int id = m_args.value(QLatin1String("breakpoint")).toInt(-1); if (id < 0) { - createErrorResponse(QStringLiteral("breakpoint has an invalid number")); - return; + m_error = QStringLiteral("breakpoint has an invalid number"); + return id; } - // remove the break point: - debugService->debuggerAgent.removeBreakPoint(id); + const QJsonValue enabled = m_args.value(QLatin1String("enabled")); + if (!enabled.isBool()) { + m_error = QStringLiteral("missing bool \"enabled\" in breakpoint change request"); + return -1; + } - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - QJsonObject body; - body.insert(QStringLiteral("type"), QStringLiteral("scriptRegExp")); - body.insert(QStringLiteral("breakpoint"), id); - addBody(body); + // enable or disable the break point: + debugService->debuggerAgent.enableBreakPoint(id, enabled.toBool()); + return id; } }; -class V8BacktraceRequest: public V8CommandHandler +class V4BacktraceRequest: public V4CommandHandler { public: - V8BacktraceRequest(): V8CommandHandler(QStringLiteral("backtrace")) {} + V4BacktraceRequest(): V4CommandHandler(QStringLiteral("backtrace")) {} void handleRequest() override { @@ -296,7 +321,7 @@ public: return; } - BacktraceJob job(saneCollector(debugger), fromFrame, toFrame); + BacktraceJob job(debugger->collector(), fromFrame, toFrame); debugger->runInEngine(&job); // response: @@ -305,15 +330,13 @@ public: addSuccess(true); addRunning(); addBody(job.returnValue()); - if (debugService->clientRequiresRedundantRefs()) - addRefs(job.refs()); } }; -class V8FrameRequest: public V8CommandHandler +class V4FrameRequest: public V4CommandHandler { public: - V8FrameRequest(): V8CommandHandler(QStringLiteral("frame")) {} + V4FrameRequest(): V4CommandHandler(QStringLiteral("frame")) {} void handleRequest() override { @@ -333,7 +356,7 @@ public: return; } - FrameJob job(saneCollector(debugger), frameNr); + FrameJob job(debugger->collector(), frameNr); debugger->runInEngine(&job); if (!job.wasSuccessful()) { createErrorResponse(QStringLiteral("frame retrieval failed")); @@ -348,15 +371,13 @@ public: addSuccess(true); addRunning(); addBody(job.returnValue()); - if (debugService->clientRequiresRedundantRefs()) - addRefs(job.refs()); } }; -class V8ScopeRequest: public V8CommandHandler +class V4ScopeRequest: public V4CommandHandler { public: - V8ScopeRequest(): V8CommandHandler(QStringLiteral("scope")) {} + V4ScopeRequest(): V4CommandHandler(QStringLiteral("scope")) {} void handleRequest() override { @@ -381,7 +402,7 @@ public: return; } - ScopeJob job(saneCollector(debugger), frameNr, scopeNr); + ScopeJob job(debugger->collector(), frameNr, scopeNr); debugger->runInEngine(&job); if (!job.wasSuccessful()) { createErrorResponse(QStringLiteral("scope retrieval failed")); @@ -394,15 +415,13 @@ public: addSuccess(true); addRunning(); addBody(job.returnValue()); - if (debugService->clientRequiresRedundantRefs()) - addRefs(job.refs()); } }; -class V8LookupRequest: public V8CommandHandler +class V4LookupRequest: public V4CommandHandler { public: - V8LookupRequest(): V8CommandHandler(QStringLiteral("lookup")) {} + V4LookupRequest(): V4CommandHandler(QStringLiteral("lookup")) {} void handleRequest() override { @@ -423,7 +442,7 @@ public: debugger = debuggers.first(); } - ValueLookupJob job(handles, saneCollector(debugger)); + ValueLookupJob job(handles, debugger->collector()); debugger->runInEngine(&job); if (!job.exceptionMessage().isEmpty()) { createErrorResponse(job.exceptionMessage()); @@ -434,16 +453,14 @@ public: addSuccess(true); addRunning(); addBody(job.returnValue()); - if (debugService->clientRequiresRedundantRefs()) - addRefs(job.refs()); } } }; -class V8ContinueRequest: public V8CommandHandler +class V4ContinueRequest: public V4CommandHandler { public: - V8ContinueRequest(): V8CommandHandler(QStringLiteral("continue")) {} + V4ContinueRequest(): V4CommandHandler(QStringLiteral("continue")) {} void handleRequest() override { @@ -486,10 +503,10 @@ public: } }; -class V8DisconnectRequest: public V8CommandHandler +class V4DisconnectRequest: public V4CommandHandler { public: - V8DisconnectRequest(): V8CommandHandler(QStringLiteral("disconnect")) {} + V4DisconnectRequest(): V4CommandHandler(QStringLiteral("disconnect")) {} void handleRequest() override { @@ -504,10 +521,10 @@ public: } }; -class V8SetExceptionBreakRequest: public V8CommandHandler +class V4SetExceptionBreakRequest: public V4CommandHandler { public: - V8SetExceptionBreakRequest(): V8CommandHandler(QStringLiteral("setexceptionbreak")) {} + V4SetExceptionBreakRequest(): V4CommandHandler(QStringLiteral("setexceptionbreak")) {} void handleRequest() override { @@ -544,10 +561,10 @@ public: } }; -class V8ScriptsRequest: public V8CommandHandler +class V4ScriptsRequest: public V4CommandHandler { public: - V8ScriptsRequest(): V8CommandHandler(QStringLiteral("scripts")) {} + V4ScriptsRequest(): V4CommandHandler(QStringLiteral("scripts")) {} void handleRequest() override { @@ -616,10 +633,10 @@ public: // } // // The "value" key in "body" is the result of evaluating the expression in the request. -class V8EvaluateRequest: public V8CommandHandler +class V4EvaluateRequest: public V4CommandHandler { public: - V8EvaluateRequest(): V8CommandHandler(QStringLiteral("evaluate")) {} + V4EvaluateRequest(): V4CommandHandler(QStringLiteral("evaluate")) {} void handleRequest() override { @@ -644,7 +661,7 @@ public: } ExpressionEvalJob job(debugger->engine(), frame, context, expression, - saneCollector(debugger)); + debugger->collector()); debugger->runInEngine(&job); if (job.hasExeption()) { createErrorResponse(job.exceptionMessage()); @@ -654,44 +671,43 @@ public: addSuccess(true); addRunning(); addBody(job.returnValue()); - if (debugService->clientRequiresRedundantRefs()) - addRefs(job.refs()); } } }; } // anonymous namespace -void QV4DebugServiceImpl::addHandler(V8CommandHandler* handler) +void QV4DebugServiceImpl::addHandler(V4CommandHandler* handler) { handlers[handler->command()] = handler; } -V8CommandHandler *QV4DebugServiceImpl::v8CommandHandler(const QString &command) const +V4CommandHandler *QV4DebugServiceImpl::v4CommandHandler(const QString &command) const { - V8CommandHandler *handler = handlers.value(command, 0); + V4CommandHandler *handler = handlers.value(command, 0); if (handler) return handler; else - return unknownV8CommandHandler.data(); + return unknownV4CommandHandler.data(); } QV4DebugServiceImpl::QV4DebugServiceImpl(QObject *parent) : QQmlConfigurableDebugService<QV4DebugService>(1, parent), - debuggerAgent(this), theSelectedFrame(0), redundantRefs(true), namesAsObjects(true), - unknownV8CommandHandler(new UnknownV8CommandHandler) + debuggerAgent(this), theSelectedFrame(0), + unknownV4CommandHandler(new UnknownV4CommandHandler) { - addHandler(new V8VersionRequest); - addHandler(new V8SetBreakPointRequest); - addHandler(new V8ClearBreakPointRequest); - addHandler(new V8BacktraceRequest); - addHandler(new V8FrameRequest); - addHandler(new V8ScopeRequest); - addHandler(new V8LookupRequest); - addHandler(new V8ContinueRequest); - addHandler(new V8DisconnectRequest); - addHandler(new V8SetExceptionBreakRequest); - addHandler(new V8ScriptsRequest); - addHandler(new V8EvaluateRequest); + addHandler(new V4VersionRequest); + addHandler(new V4SetBreakPointRequest); + addHandler(new V4ClearBreakPointRequest); + addHandler(new V4ChangeBreakPointRequest); + addHandler(new V4BacktraceRequest); + addHandler(new V4FrameRequest); + addHandler(new V4ScopeRequest); + addHandler(new V4LookupRequest); + addHandler(new V4ContinueRequest); + addHandler(new V4DisconnectRequest); + addHandler(new V4SetExceptionBreakRequest); + addHandler(new V4ScriptsRequest); + addHandler(new V4EvaluateRequest); } QV4DebugServiceImpl::~QV4DebugServiceImpl() @@ -703,10 +719,9 @@ void QV4DebugServiceImpl::engineAdded(QJSEngine *engine) { QMutexLocker lock(&m_configMutex); if (engine) { - QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); + QV4::ExecutionEngine *ee = engine->handle(); if (QQmlDebugConnector *server = QQmlDebugConnector::instance()) { if (ee) { - ee->iselFactory.reset(new QV4::Moth::ISelFactory); QV4Debugger *debugger = new QV4Debugger(ee); if (state() == Enabled) ee->setDebugger(debugger); @@ -722,7 +737,7 @@ void QV4DebugServiceImpl::engineAboutToBeRemoved(QJSEngine *engine) { QMutexLocker lock(&m_configMutex); if (engine){ - const QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); + const QV4::ExecutionEngine *ee = engine->handle(); if (ee) { QV4Debugger *debugger = qobject_cast<QV4Debugger *>(ee->debugger()); if (debugger) @@ -782,12 +797,7 @@ void QV4DebugServiceImpl::messageReceived(const QByteArray &message) if (type == V4_CONNECT) { QJsonObject parameters = QJsonDocument::fromJson(payload).object(); - namesAsObjects = true; - redundantRefs = true; - if (parameters.contains("namesAsObjects")) - namesAsObjects = parameters.value("namesAsObjects").toBool(); - if (parameters.contains("redundantRefs")) - redundantRefs = parameters.value("redundantRefs").toBool(); + Q_UNUSED(parameters); // For future protocol changes emit messageToClient(name(), packMessage(type)); stopWaiting(); @@ -805,10 +815,10 @@ void QV4DebugServiceImpl::messageReceived(const QByteArray &message) else breakOnSignals.removeOne(signalName); } else if (type == "v8request") { - handleV8Request(payload); + handleV4Request(payload); } else if (type == V4_DISCONNECT) { TRACE_PROTOCOL(qDebug() << "... payload:" << payload.constData()); - handleV8Request(payload); + handleV4Request(payload); } else { sendSomethingToSomebody(type, 0); } @@ -823,7 +833,7 @@ void QV4DebugServiceImpl::sendSomethingToSomebody(const char *type, int magicNum emit messageToClient(name(), packMessage(type, rs.data())); } -void QV4DebugServiceImpl::handleV8Request(const QByteArray &payload) +void QV4DebugServiceImpl::handleV4Request(const QByteArray &payload) { TRACE_PROTOCOL(qDebug() << "v8request, payload:" << payload.constData()); @@ -832,7 +842,7 @@ void QV4DebugServiceImpl::handleV8Request(const QByteArray &payload) QJsonValue type = o.value(QLatin1String("type")); if (type.toString() == QLatin1String("request")) { QJsonValue command = o.value(QLatin1String("command")); - V8CommandHandler *h = v8CommandHandler(command.toString()); + V4CommandHandler *h = v4CommandHandler(command.toString()); if (h) h->handle(o, this); } @@ -846,11 +856,11 @@ QByteArray QV4DebugServiceImpl::packMessage(const QByteArray &command, const QBy return rs.data(); } -void QV4DebugServiceImpl::send(QJsonObject v8Payload) +void QV4DebugServiceImpl::send(QJsonObject v4Payload) { - v8Payload[QLatin1String("seq")] = sequence++; + v4Payload[QLatin1String("seq")] = sequence++; QJsonDocument doc; - doc.setObject(v8Payload); + doc.setObject(v4Payload); #ifdef NO_PROTOCOL_TRACING QByteArray responseData = doc.toJson(QJsonDocument::Compact); #else diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h index bb13890ae4..d0b104dfad 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h @@ -51,9 +51,9 @@ // We mean it. // -#include "qqmlconfigurabledebugservice.h" #include "qv4debuggeragent.h" #include "qv4datacollector.h" +#include <private/qqmlconfigurabledebugservice_p.h> #include <private/qqmldebugserviceinterfaces_p.h> #include <private/qv4debugging_p.h> @@ -64,56 +64,50 @@ QT_BEGIN_NAMESPACE namespace QV4 { struct ExecutionEngine; } class VariableCollector; -class V8CommandHandler; -class UnknownV8CommandHandler; +class V4CommandHandler; +class UnknownV4CommandHandler; class QV4DebugServiceImpl; class QV4DebugServiceImpl : public QQmlConfigurableDebugService<QV4DebugService> { Q_OBJECT public: - explicit QV4DebugServiceImpl(QObject *parent = 0); - ~QV4DebugServiceImpl() Q_DECL_OVERRIDE; + explicit QV4DebugServiceImpl(QObject *parent = nullptr); + ~QV4DebugServiceImpl() override; - void engineAdded(QJSEngine *engine) Q_DECL_OVERRIDE; - void engineAboutToBeRemoved(QJSEngine *engine) Q_DECL_OVERRIDE; + void engineAdded(QJSEngine *engine) override; + void engineAboutToBeRemoved(QJSEngine *engine) override; - void stateAboutToBeChanged(State state) Q_DECL_OVERRIDE; + void stateAboutToBeChanged(State state) override; - void signalEmitted(const QString &signal) Q_DECL_OVERRIDE; - void send(QJsonObject v8Payload); + void signalEmitted(const QString &signal) override; + void send(QJsonObject v4Payload); int selectedFrame() const; void selectFrame(int frameNr); - bool clientRequiresRedundantRefs() const { return redundantRefs; } - bool clientRequiresNamesAsObjects() const { return namesAsObjects; } - QV4DebuggerAgent debuggerAgent; protected: - void messageReceived(const QByteArray &) Q_DECL_OVERRIDE; + void messageReceived(const QByteArray &) override; void sendSomethingToSomebody(const char *type, int magicNumber = 1); private: friend class QQmlDebuggerServiceFactory; - void handleV8Request(const QByteArray &payload); + void handleV4Request(const QByteArray &payload); static QByteArray packMessage(const QByteArray &command, const QByteArray &message = QByteArray()); void processCommand(const QByteArray &command, const QByteArray &data); - V8CommandHandler *v8CommandHandler(const QString &command) const; + V4CommandHandler *v4CommandHandler(const QString &command) const; QStringList breakOnSignals; static int sequence; int theSelectedFrame; - bool redundantRefs; - bool namesAsObjects; - - void addHandler(V8CommandHandler* handler); - QHash<QString, V8CommandHandler*> handlers; - QScopedPointer<UnknownV8CommandHandler> unknownV8CommandHandler; + void addHandler(V4CommandHandler* handler); + QHash<QString, V4CommandHandler*> handlers; + QScopedPointer<UnknownV4CommandHandler> unknownV4CommandHandler; }; QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp index 7145645609..bac4e01df1 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp @@ -40,11 +40,12 @@ #include "globalinspector.h" #include "highlight.h" #include "inspecttool.h" -#include "qqmldebugpacket.h" #include <private/qqmldebugserviceinterfaces_p.h> #include <private/qabstractanimation_p.h> #include <private/qqmlcomponent_p.h> +#include <private/qqmldebugconnector_p.h> +#include <private/qversionedpacket_p.h> #include <QtGui/qwindow.h> @@ -63,6 +64,8 @@ QT_BEGIN_NAMESPACE +using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>; + const char REQUEST[] = "request"; const char RESPONSE[] = "response"; const char EVENT[] = "event"; diff --git a/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp b/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp index c7307db240..c4d7872162 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp @@ -100,7 +100,7 @@ void Highlight::adjust() return; bool success = false; - m_transform = m_item->itemTransform(0, &success); + m_transform = m_item->itemTransform(nullptr, &success); if (!success) m_transform = QTransform(); @@ -188,7 +188,7 @@ void SelectionHighlight::showName(const QPointF &displayPoint) { m_displayPoint = displayPoint; m_nameDisplayActive = true; - QTimer::singleShot(1500, this, SLOT(disableNameDisplay())); + QTimer::singleShot(1500, this, &SelectionHighlight::disableNameDisplay); update(); } diff --git a/src/plugins/qmltooling/qmldbg_inspector/inspecttool.cpp b/src/plugins/qmltooling/qmldbg_inspector/inspecttool.cpp index bc146b176c..1781670cf3 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/inspecttool.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/inspecttool.cpp @@ -62,8 +62,8 @@ InspectTool::InspectTool(QQuickWindowInspector *inspector, QQuickWindow *view) : m_contentItem(view->contentItem()), m_touchTimestamp(0), m_hoverHighlight(new HoverHighlight(inspector->overlay())), - m_lastItem(0), - m_lastClickedItem(0) + m_lastItem(nullptr), + m_lastClickedItem(nullptr) { //Timer to display selected item's name m_nameDisplayTimer.setSingleShot(true); diff --git a/src/plugins/qmltooling/qmldbg_inspector/qmldbg_inspector.pro b/src/plugins/qmltooling/qmldbg_inspector/qmldbg_inspector.pro index a8844944e0..18a61f15b7 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/qmldbg_inspector.pro +++ b/src/plugins/qmltooling/qmldbg_inspector/qmldbg_inspector.pro @@ -1,8 +1,6 @@ TARGET = qmldbg_inspector QT += qml-private quick-private core-private gui-private packetprotocol-private -INCLUDEPATH *= $$PWD $$PWD/../shared - SOURCES += \ $$PWD/globalinspector.cpp \ $$PWD/highlight.cpp \ @@ -11,7 +9,6 @@ SOURCES += \ $$PWD/qquickwindowinspector.cpp HEADERS += \ - $$PWD/../shared/qqmldebugpacket.h \ $$PWD/globalinspector.h \ $$PWD/highlight.h \ $$PWD/inspecttool.h\ diff --git a/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp b/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp index ab1aeebf64..fa27adedfd 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp @@ -49,17 +49,17 @@ class QQmlInspectorServiceImpl : public QQmlInspectorService { Q_OBJECT public: - QQmlInspectorServiceImpl(QObject *parent = 0); + QQmlInspectorServiceImpl(QObject *parent = nullptr); - void addWindow(QQuickWindow *window) Q_DECL_OVERRIDE; - void setParentWindow(QQuickWindow *window, QWindow *parent) Q_DECL_OVERRIDE; - void removeWindow(QQuickWindow *window) Q_DECL_OVERRIDE; + void addWindow(QQuickWindow *window) override; + void setParentWindow(QQuickWindow *window, QWindow *parent) override; + void removeWindow(QQuickWindow *window) override; signals: void scheduleMessage(const QByteArray &message); protected: - virtual void messageReceived(const QByteArray &) Q_DECL_OVERRIDE; + void messageReceived(const QByteArray &) override; private: friend class QQmlInspectorServiceFactory; @@ -72,7 +72,7 @@ private: }; QQmlInspectorServiceImpl::QQmlInspectorServiceImpl(QObject *parent): - QQmlInspectorService(1, parent), m_globalInspector(0) + QQmlInspectorService(1, parent), m_globalInspector(nullptr) { connect(this, &QQmlInspectorServiceImpl::scheduleMessage, this, &QQmlInspectorServiceImpl::messageFromClient, Qt::QueuedConnection); @@ -95,7 +95,7 @@ QmlJSDebugger::GlobalInspector *QQmlInspectorServiceImpl::checkInspector() } } else if (m_globalInspector) { delete m_globalInspector; - m_globalInspector = 0; + m_globalInspector = nullptr; } return m_globalInspector; } @@ -138,7 +138,7 @@ void QQmlInspectorServiceImpl::messageFromClient(const QByteArray &message) QQmlDebugService *QQmlInspectorServiceFactory::create(const QString &key) { - return key == QQmlInspectorServiceImpl::s_key ? new QQmlInspectorServiceImpl(this) : 0; + return key == QQmlInspectorServiceImpl::s_key ? new QQmlInspectorServiceImpl(this) : nullptr; } QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_inspector/qquickwindowinspector.cpp b/src/plugins/qmltooling/qmldbg_inspector/qquickwindowinspector.cpp index 16056addbd..09eb6bfc28 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/qquickwindowinspector.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/qquickwindowinspector.cpp @@ -54,14 +54,14 @@ static QQuickItem *itemAt(QQuickItem *item, const QPointF &pos, QQuickItem *overlay) { if (item == overlay) - return 0; + return nullptr; if (!item->isVisible() || item->opacity() == 0.0) - return 0; + return nullptr; if (item->flags() & QQuickItem::ItemClipsChildrenToShape) { if (!QRectF(0, 0, item->width(), item->height()).contains(pos)) - return 0; + return nullptr; } QList<QQuickItem *> children = QQuickItemPrivate::get(item)->paintOrderChildItems(); @@ -73,10 +73,10 @@ static QQuickItem *itemAt(QQuickItem *item, const QPointF &pos, } if (!(item->flags() & QQuickItem::ItemHasContents)) - return 0; + return nullptr; if (!QRectF(0, 0, item->width(), item->height()).contains(pos)) - return 0; + return nullptr; return item; } @@ -111,8 +111,8 @@ QQuickWindowInspector::QQuickWindowInspector(QQuickWindow *quickWindow, QObject QObject(parent), m_overlay(new QQuickItem), m_window(quickWindow), - m_parentWindow(0), - m_tool(0) + m_parentWindow(nullptr), + m_tool(nullptr) { setParentWindow(quickWindow); @@ -169,13 +169,29 @@ bool QQuickWindowInspector::eventFilter(QObject *obj, QEvent *event) return QObject::eventFilter(obj, event); } +static Qt::WindowFlags fixFlags(Qt::WindowFlags flags) +{ + // If only the type flag is given, some other window flags are automatically assumed. When we + // add a flag, we need to make those explicit. + switch (flags) { + case Qt::Window: + return flags | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint + | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint; + case Qt::Dialog: + case Qt::Tool: + return flags | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint; + default: + return flags; + } +} + void QQuickWindowInspector::setShowAppOnTop(bool appOnTop) { if (!m_parentWindow) return; Qt::WindowFlags flags = m_parentWindow->flags(); - Qt::WindowFlags newFlags = appOnTop ? (flags | Qt::WindowStaysOnTopHint) : + Qt::WindowFlags newFlags = appOnTop ? (fixFlags(flags) | Qt::WindowStaysOnTopHint) : (flags & ~Qt::WindowStaysOnTopHint); if (newFlags != flags) m_parentWindow->setFlags(newFlags); @@ -183,7 +199,7 @@ void QQuickWindowInspector::setShowAppOnTop(bool appOnTop) bool QQuickWindowInspector::isEnabled() const { - return m_tool != 0; + return m_tool != nullptr; } void QQuickWindowInspector::setEnabled(bool enabled) @@ -192,7 +208,7 @@ void QQuickWindowInspector::setEnabled(bool enabled) m_tool = new InspectTool(this, m_window); } else { delete m_tool; - m_tool = 0; + m_tool = nullptr; } } diff --git a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp index 97e4b4e3e4..1708166a8a 100644 --- a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp +++ b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp @@ -38,10 +38,10 @@ ****************************************************************************/ #include "qlocalclientconnectionfactory.h" -#include "qqmldebugserver.h" #include <QtCore/qplugin.h> #include <QtNetwork/qlocalsocket.h> +#include <private/qqmldebugserver_p.h> Q_DECLARE_METATYPE(QLocalSocket::LocalSocketError) @@ -55,7 +55,7 @@ class QLocalClientConnection : public QQmlDebugServerConnection public: QLocalClientConnection(); - ~QLocalClientConnection(); + ~QLocalClientConnection() override; void setServer(QQmlDebugServer *server) override; bool setPortRange(int portFrom, int portTo, bool block, const QString &hostaddress) override; @@ -71,18 +71,13 @@ private: void connectionEstablished(); bool connectToServer(); - bool m_block; + bool m_block = false; QString m_filename; - QLocalSocket *m_socket; - QQmlDebugServer *m_debugServer; + QLocalSocket *m_socket = nullptr; + QQmlDebugServer *m_debugServer = nullptr; }; -QLocalClientConnection::QLocalClientConnection() : - m_block(false), - m_socket(0), - m_debugServer(0) -{ -} +QLocalClientConnection::QLocalClientConnection() { } QLocalClientConnection::~QLocalClientConnection() { @@ -106,7 +101,7 @@ void QLocalClientConnection::disconnect() m_socket->waitForBytesWritten(); m_socket->deleteLater(); - m_socket = 0; + m_socket = nullptr; } bool QLocalClientConnection::setPortRange(int portFrom, int portTo, bool block, @@ -161,7 +156,7 @@ void QLocalClientConnection::connectionEstablished() QQmlDebugServerConnection *QLocalClientConnectionFactory::create(const QString &key) { - return (key == QLatin1String("QLocalClientConnection") ? new QLocalClientConnection : 0); + return (key == QLatin1String("QLocalClientConnection") ? new QLocalClientConnection : nullptr); } QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnectionfactory.h b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnectionfactory.h index b64a1fff95..95bbd8956a 100644 --- a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnectionfactory.h +++ b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnectionfactory.h @@ -40,7 +40,7 @@ #ifndef QLOCALCLIENTCONNECTIONFACTORY_H #define QLOCALCLIENTCONNECTIONFACTORY_H -#include "qqmldebugserverconnection.h" +#include <private/qqmldebugserverconnection_p.h> QT_BEGIN_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_local/qmldbg_local.pro b/src/plugins/qmltooling/qmldbg_local/qmldbg_local.pro index d731e47b7e..71dba262da 100644 --- a/src/plugins/qmltooling/qmldbg_local/qmldbg_local.pro +++ b/src/plugins/qmltooling/qmldbg_local/qmldbg_local.pro @@ -5,12 +5,7 @@ SOURCES += \ $$PWD/qlocalclientconnection.cpp HEADERS += \ - $$PWD/qlocalclientconnectionfactory.h \ - $$PWD/../shared/qqmldebugserver.h \ - $$PWD/../shared/qqmldebugserverconnection.h - -INCLUDEPATH += $$PWD \ - $$PWD/../shared + $$PWD/qlocalclientconnectionfactory.h OTHER_FILES += \ $$PWD/qlocalclientconnection.json diff --git a/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.cpp b/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.cpp index b0f59717ac..4f6cb9364d 100644 --- a/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.cpp +++ b/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.cpp @@ -38,11 +38,14 @@ ****************************************************************************/ #include "qdebugmessageservice.h" -#include "qqmldebugpacket.h" + #include <private/qqmldebugconnector_p.h> +#include <private/qversionedpacket_p.h> QT_BEGIN_NAMESPACE +using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>; + void DebugMessageHandler(QtMsgType type, const QMessageLogContext &ctxt, const QString &buf) { @@ -50,7 +53,7 @@ void DebugMessageHandler(QtMsgType type, const QMessageLogContext &ctxt, } QDebugMessageServiceImpl::QDebugMessageServiceImpl(QObject *parent) : - QDebugMessageService(2, parent), oldMsgHandler(0), + QDebugMessageService(2, parent), oldMsgHandler(nullptr), prevState(QQmlDebugService::NotConnected) { // don't execute stateChanged() in parallel diff --git a/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservicefactory.cpp b/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservicefactory.cpp index 860d654128..177ca1fe80 100644 --- a/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservicefactory.cpp +++ b/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservicefactory.cpp @@ -48,7 +48,7 @@ QQmlDebugService *QDebugMessageServiceFactory::create(const QString &key) if (key == QDebugMessageServiceImpl::s_key) return new QDebugMessageServiceImpl(this); - return 0; + return nullptr; } QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_messages/qmldbg_messages.pro b/src/plugins/qmltooling/qmldbg_messages/qmldbg_messages.pro index 5ddf7c615d..eda6df1a16 100644 --- a/src/plugins/qmltooling/qmldbg_messages/qmldbg_messages.pro +++ b/src/plugins/qmltooling/qmldbg_messages/qmldbg_messages.pro @@ -6,13 +6,9 @@ SOURCES += \ $$PWD/qdebugmessageservicefactory.cpp HEADERS += \ - $$PWD/../shared/qqmldebugpacket.h \ $$PWD/qdebugmessageservice.h \ $$PWD/qdebugmessageservicefactory.h -INCLUDEPATH += $$PWD \ - $$PWD/../shared - OTHER_FILES += \ $$PWD/qdebugmessageservice.json diff --git a/src/plugins/qmltooling/qmldbg_native/qmldbg_native.pro b/src/plugins/qmltooling/qmldbg_native/qmldbg_native.pro index e5489574be..6630a394a0 100644 --- a/src/plugins/qmltooling/qmldbg_native/qmldbg_native.pro +++ b/src/plugins/qmltooling/qmldbg_native/qmldbg_native.pro @@ -2,15 +2,11 @@ TARGET = qmldbg_native QT = qml-private core-private packetprotocol-private HEADERS += \ - $$PWD/../shared/qqmldebugpacket.h \ $$PWD/qqmlnativedebugconnector.h SOURCES += \ $$PWD/qqmlnativedebugconnector.cpp -INCLUDEPATH += $$PWD \ - $$PWD/../shared - OTHER_FILES += \ $$PWD/qqmlnativedebugconnector.json diff --git a/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.cpp b/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.cpp index 388d2e3b22..bf73440a39 100644 --- a/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.cpp +++ b/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.cpp @@ -38,9 +38,9 @@ ****************************************************************************/ #include "qqmlnativedebugconnector.h" -#include "qqmldebugpacket.h" #include <private/qhooks_p.h> +#include <private/qversionedpacket_p.h> #include <QtQml/qjsengine.h> #include <QtCore/qdebug.h> @@ -224,7 +224,7 @@ QQmlDebugService *QQmlNativeDebugConnector::service(const QString &name) const if ((*i)->name() == name) return *i; } - return 0; + return nullptr; } void QQmlNativeDebugConnector::addEngine(QJSEngine *engine) @@ -360,7 +360,7 @@ void QQmlNativeDebugConnector::sendMessages(const QString &name, const QList<QBy QQmlDebugConnector *QQmlNativeDebugConnectorFactory::create(const QString &key) { - return key == QLatin1String("QQmlNativeDebugConnector") ? new QQmlNativeDebugConnector : 0; + return key == QLatin1String("QQmlNativeDebugConnector") ? new QQmlNativeDebugConnector : nullptr; } QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h b/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h index f8b7e1d527..a7f37b0f1e 100644 --- a/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h +++ b/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h @@ -51,16 +51,16 @@ class QQmlNativeDebugConnector : public QQmlDebugConnector public: QQmlNativeDebugConnector(); - ~QQmlNativeDebugConnector() Q_DECL_OVERRIDE; + ~QQmlNativeDebugConnector() override; - bool blockingMode() const Q_DECL_OVERRIDE; - QQmlDebugService *service(const QString &name) const Q_DECL_OVERRIDE; - void addEngine(QJSEngine *engine) Q_DECL_OVERRIDE; - void removeEngine(QJSEngine *engine) Q_DECL_OVERRIDE; - bool hasEngine(QJSEngine *engine) const Q_DECL_OVERRIDE; - bool addService(const QString &name, QQmlDebugService *service) Q_DECL_OVERRIDE; - bool removeService(const QString &name) Q_DECL_OVERRIDE; - bool open(const QVariantHash &configuration) Q_DECL_OVERRIDE; + bool blockingMode() const override; + QQmlDebugService *service(const QString &name) const override; + void addEngine(QJSEngine *engine) override; + void removeEngine(QJSEngine *engine) override; + bool hasEngine(QJSEngine *engine) const override; + bool addService(const QString &name, QQmlDebugService *service) override; + bool removeService(const QString &name) override; + bool open(const QVariantHash &configuration) override; static void setDataStreamVersion(int version); private: diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qmldbg_nativedebugger.pro b/src/plugins/qmltooling/qmldbg_nativedebugger/qmldbg_nativedebugger.pro index 1873a6a77c..1cb5525622 100644 --- a/src/plugins/qmltooling/qmldbg_nativedebugger/qmldbg_nativedebugger.pro +++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qmldbg_nativedebugger.pro @@ -6,13 +6,9 @@ SOURCES += \ $$PWD/qqmlnativedebugservice.cpp HEADERS += \ - $$PWD/../shared/qqmldebugpacket.h \ $$PWD/qqmlnativedebugservicefactory.h \ $$PWD/qqmlnativedebugservice.h \ -INCLUDEPATH += $$PWD \ - $$PWD/../shared - OTHER_FILES += \ $$PWD/qqmlnativedebugservice.json diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp index d536fd51ed..43a48e9d0d 100644 --- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp @@ -38,7 +38,6 @@ ****************************************************************************/ #include "qqmlnativedebugservice.h" -#include "qqmldebugpacket.h" #include <private/qqmldebugconnector_p.h> #include <private/qv4debugging_p.h> @@ -50,8 +49,9 @@ #include <private/qv4objectiterator_p.h> #include <private/qv4identifier_p.h> #include <private/qv4runtime_p.h> -#include <private/qv4isel_moth_p.h> +#include <private/qversionedpacket_p.h> #include <private/qqmldebugserviceinterfaces_p.h> +#include <private/qv4identifiertable_p.h> #include <QtQml/qjsengine.h> #include <QtCore/qjsonarray.h> @@ -66,6 +66,8 @@ QT_BEGIN_NAMESPACE +using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>; + class BreakPoint { public: @@ -188,16 +190,16 @@ public: QV4::ExecutionEngine *engine() const { return m_engine; } - bool pauseAtNextOpportunity() const Q_DECL_OVERRIDE { + bool pauseAtNextOpportunity() const override { return m_pauseRequested || m_service->m_breakHandler->m_haveBreakPoints || m_stepping >= StepOver; } - void maybeBreakAtInstruction() Q_DECL_OVERRIDE; - void enteringFunction() Q_DECL_OVERRIDE; - void leavingFunction(const QV4::ReturnedValue &retVal) Q_DECL_OVERRIDE; - void aboutToThrow() Q_DECL_OVERRIDE; + void maybeBreakAtInstruction() override; + void enteringFunction() override; + void leavingFunction(const QV4::ReturnedValue &retVal) override; + void aboutToThrow() override; void handleCommand(QJsonObject *response, const QString &cmd, const QJsonObject &arguments); @@ -208,7 +210,7 @@ private: void handleDebuggerDeleted(QObject *debugger); - void evaluateExpression(QV4::Scope &scope, const QString &expression); + QV4::ReturnedValue evaluateExpression(const QString &expression); bool checkCondition(const QString &expression); QStringList breakOnSignals; @@ -230,7 +232,7 @@ private: QV4::ExecutionEngine *m_engine; QQmlNativeDebugServiceImpl *m_service; - QV4::PersistentValue m_currentContext; + QV4::CppStackFrame *m_currentFrame = nullptr; Speed m_stepping; bool m_pauseRequested; bool m_runningJob; @@ -241,33 +243,41 @@ private: bool NativeDebugger::checkCondition(const QString &expression) { QV4::Scope scope(m_engine); - evaluateExpression(scope, expression); - return scope.result.booleanValue(); + QV4::ScopedValue r(scope, evaluateExpression(expression)); + return r->booleanValue(); } -void NativeDebugger::evaluateExpression(QV4::Scope &scope, const QString &expression) +QV4::ReturnedValue NativeDebugger::evaluateExpression(const QString &expression) { + QV4::Scope scope(m_engine); m_runningJob = true; - QV4::ExecutionContextSaver saver(scope); - - QV4::ExecutionContext *ctx = m_engine->currentContext; - m_engine->pushContext(ctx); + QV4::ExecutionContext *ctx = m_engine->currentStackFrame ? m_engine->currentContext() + : m_engine->scriptContext(); - QV4::Script script(ctx, expression); - script.strictMode = ctx->d()->strictMode; + QV4::Script script(ctx, QV4::Compiler::ContextType::Eval, expression); + if (const QV4::Function *function = m_engine->currentStackFrame + ? m_engine->currentStackFrame->v4Function : m_engine->globalCode) + script.strictMode = function->isStrict(); // In order for property lookups in QML to work, we need to disable fast v4 lookups. // That is a side-effect of inheritContext. script.inheritContext = true; script.parse(); - if (!m_engine->hasException) - scope.result = script.run(); + if (!m_engine->hasException) { + if (m_engine->currentStackFrame) { + QV4::ScopedValue thisObject(scope, m_engine->currentStackFrame->thisObject()); + script.run(thisObject); + } else { + script.run(); + } + } m_runningJob = false; + return QV4::Encode::undefined(); } NativeDebugger::NativeDebugger(QQmlNativeDebugServiceImpl *service, QV4::ExecutionEngine *engine) - : m_returnedValue(engine, QV4::Primitive::undefinedValue()) + : m_returnedValue(engine, QV4::Value::undefinedValue()) { m_stepping = NotStepping; m_pauseRequested = false; @@ -314,19 +324,19 @@ void NativeDebugger::handleCommand(QJsonObject *response, const QString &cmd, handleContinue(response, NotStepping); } -static QString encodeContext(QV4::ExecutionContext *executionContext) +static QString encodeFrame(QV4::CppStackFrame *f) { QQmlDebugPacket ds; - ds << quintptr(executionContext); + ds << quintptr(f); return QString::fromLatin1(ds.data().toHex()); } -static void decodeContext(const QString &context, QV4::ExecutionContext **executionContext) +static void decodeFrame(const QString &f, QV4::CppStackFrame **frame) { - quintptr rawContext; - QQmlDebugPacket ds(QByteArray::fromHex(context.toLatin1())); - ds >> rawContext; - *executionContext = reinterpret_cast<QV4::ExecutionContext *>(rawContext); + quintptr rawFrame; + QQmlDebugPacket ds(QByteArray::fromHex(f.toLatin1())); + ds >> rawFrame; + *frame = reinterpret_cast<QV4::CppStackFrame *>(rawFrame); } void NativeDebugger::handleBacktrace(QJsonObject *response, const QJsonObject &arguments) @@ -334,25 +344,24 @@ void NativeDebugger::handleBacktrace(QJsonObject *response, const QJsonObject &a int limit = arguments.value(QLatin1String("limit")).toInt(0); QJsonArray frameArray; - QV4::ExecutionContext *executionContext = m_engine->currentContext; - for (int i = 0; i < limit && executionContext; ++i) { - if (QV4::Function *function = executionContext->getFunction()) { + QV4::CppStackFrame *f= m_engine->currentStackFrame; + for (int i = 0; i < limit && f; ++i) { + QV4::Function *function = f->v4Function; - QJsonObject frame; - frame.insert(QStringLiteral("language"), QStringLiteral("js")); - frame.insert(QStringLiteral("context"), encodeContext(executionContext)); + QJsonObject frame; + frame.insert(QStringLiteral("language"), QStringLiteral("js")); + frame.insert(QStringLiteral("context"), encodeFrame(f)); - if (QV4::Heap::String *functionName = function->name()) - frame.insert(QStringLiteral("function"), functionName->toQString()); - frame.insert(QStringLiteral("file"), function->sourceFile()); + if (QV4::Heap::String *functionName = function->name()) + frame.insert(QStringLiteral("function"), functionName->toQString()); + frame.insert(QStringLiteral("file"), function->sourceFile()); - int line = executionContext->d()->lineNumber; - frame.insert(QStringLiteral("line"), (line < 0 ? -line : line)); + int line = f->lineNumber(); + frame.insert(QStringLiteral("line"), (line < 0 ? -line : line)); - frameArray.push_back(frame); - } + frameArray.push_back(frame); - executionContext = m_engine->parentContext(executionContext); + f = f->parent; } response->insert(QStringLiteral("frames"), frameArray); @@ -369,7 +378,7 @@ void Collector::collect(QJsonArray *out, const QString &parentIName, const QStri dict.insert(QStringLiteral("iname"), iname); dict.insert(QStringLiteral("name"), nonEmptyName); - QV4::ScopedValue typeString(scope, QV4::Runtime::method_typeofValue(m_engine, value)); + QV4::ScopedValue typeString(scope, QV4::Runtime::TypeofValue::call(m_engine, value)); dict.insert(QStringLiteral("type"), typeString->toQStringNoThrow()); switch (value.type()) { @@ -406,7 +415,7 @@ void Collector::collect(QJsonArray *out, const QString &parentIName, const QStri if (isExpanded(iname)) { QJsonArray children; for (uint i = 0; i < n; ++i) { - QV4::ReturnedValue v = array->getIndexed(i); + QV4::ReturnedValue v = array->get(i); QV4::ScopedValue sval(scope, v); collect(&children, iname, QString::number(i), *sval); } @@ -418,20 +427,17 @@ void Collector::collect(QJsonArray *out, const QString &parentIName, const QStri qint64 numProperties = 0; QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly); QV4::ScopedProperty p(scope); - QV4::ScopedString name(scope); + QV4::ScopedPropertyKey name(scope); while (true) { QV4::PropertyAttributes attrs; - uint index; - it.next(name.getRef(), &index, p, &attrs); - if (attrs.isEmpty()) + name = it.next(p, &attrs); + if (!name->isValid()) break; - if (name.getPointer()) { + if (name->isStringOrSymbol()) { ++numProperties; if (expanded) { - if (name.getPointer()) { - QV4::Value v = p.property->value; - collect(&children, iname, name->toQStringNoThrow(), v); - } + QV4::Value v = p.property->value; + collect(&children, iname, name->toQString(), v); } } } @@ -457,15 +463,15 @@ void Collector::collect(QJsonArray *out, const QString &parentIName, const QStri void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &arguments) { TRACE_PROTOCOL("Build variables"); - QV4::ExecutionContext *executionContext = 0; - decodeContext(arguments.value(QLatin1String("context")).toString(), &executionContext); - if (!executionContext) { - setError(response, QStringLiteral("No execution context passed")); + QV4::CppStackFrame *frame = nullptr; + decodeFrame(arguments.value(QLatin1String("context")).toString(), &frame); + if (!frame) { + setError(response, QStringLiteral("No stack frame passed")); return; } - TRACE_PROTOCOL("Context: " << executionContext); + TRACE_PROTOCOL("Context: " << frame); - QV4::ExecutionEngine *engine = executionContext->engine(); + QV4::ExecutionEngine *engine = frame->v4Function->internalClass->engine; if (!engine) { setError(response, QStringLiteral("No execution engine passed")); return; @@ -481,28 +487,16 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a QJsonArray output; QV4::Scope scope(engine); - if (QV4::SimpleCallContext *callContext = executionContext->asSimpleCallContext()) { - QV4::Value thisObject = callContext->thisObject(); - collector.collect(&output, QString(), QStringLiteral("this"), thisObject); - QV4::Identifier *const *variables = callContext->variables(); - QV4::Identifier *const *formals = callContext->formals(); - if (callContext->d()->type == QV4::Heap::ExecutionContext::Type_CallContext) { - QV4::CallContext *ctx = static_cast<QV4::CallContext *>(callContext); - for (unsigned i = 0, ei = ctx->variableCount(); i != ei; ++i) { - QString qName; - if (QV4::Identifier *name = variables[i]) - qName = name->string; - QV4::Value val = ctx->d()->locals[i]; - collector.collect(&output, QString(), qName, val); - } - } - for (unsigned i = 0, ei = callContext->formalCount(); i != ei; ++i) { - QString qName; - if (QV4::Identifier *name = formals[i]) - qName = name->string; - QV4::ReturnedValue rval = callContext->argument(i); - QV4::ScopedValue sval(scope, rval); - collector.collect(&output, QString(), qName, *sval); + QV4::ScopedValue thisObject(scope, frame->thisObject()); + collector.collect(&output, QString(), QStringLiteral("this"), thisObject); + QV4::Scoped<QV4::CallContext> callContext(scope, frame->callContext()); + if (callContext) { + QV4::Heap::InternalClass *ic = callContext->internalClass(); + QV4::ScopedValue v(scope); + for (uint i = 0; i < ic->size; ++i) { + QString name = ic->keyAt(i); + v = callContext->d()->locals[i]; + collector.collect(&output, QString(), name, v); } } @@ -512,15 +506,15 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject &arguments) { TRACE_PROTOCOL("Evaluate expressions"); - QV4::ExecutionContext *executionContext = 0; - decodeContext(arguments.value(QLatin1String("context")).toString(), &executionContext); - if (!executionContext) { - setError(response, QStringLiteral("No execution context passed")); + QV4::CppStackFrame *frame = nullptr; + decodeFrame(arguments.value(QLatin1String("context")).toString(), &frame); + if (!frame) { + setError(response, QStringLiteral("No stack frame passed")); return; } TRACE_PROTOCOL("Context: " << executionContext); - QV4::ExecutionEngine *engine = executionContext->engine(); + QV4::ExecutionEngine *engine = frame->v4Function->internalClass->engine; if (!engine) { setError(response, QStringLiteral("No execution engine passed")); return; @@ -543,8 +537,7 @@ void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject TRACE_PROTOCOL("Evaluate expression: " << expression); m_runningJob = true; - evaluateExpression(scope, expression); - QV4::ScopedValue result(scope, scope.result); + QV4::ScopedValue result(scope, evaluateExpression(expression)); m_runningJob = false; if (result->isUndefined()) { @@ -595,7 +588,7 @@ void NativeDebugger::handleContinue(QJsonObject *response, Speed speed) if (!m_returnedValue.isUndefined()) m_returnedValue.set(m_engine, QV4::Encode::undefined()); - m_currentContext.set(m_engine, *m_engine->currentContext); + m_currentFrame = m_engine->currentStackFrame; m_stepping = speed; } @@ -605,7 +598,7 @@ void NativeDebugger::maybeBreakAtInstruction() return; if (m_stepping == StepOver) { - if (m_currentContext.asManaged()->d() == m_engine->current) + if (m_currentFrame == m_engine->currentStackFrame) pauseAndWait(); return; } @@ -623,7 +616,8 @@ void NativeDebugger::maybeBreakAtInstruction() if (m_service->m_breakHandler->m_haveBreakPoints) { if (QV4::Function *function = getFunction()) { - const int lineNumber = m_engine->current->lineNumber; + // lineNumber will be negative for Ret instructions, so those won't match + const int lineNumber = m_engine->currentStackFrame->lineNumber(); if (reallyHitTheBreakPoint(function, lineNumber)) pauseAndWait(); } @@ -636,7 +630,7 @@ void NativeDebugger::enteringFunction() return; if (m_stepping == StepIn) { - m_currentContext.set(m_engine, *m_engine->currentContext); + m_currentFrame = m_engine->currentStackFrame; } } @@ -645,8 +639,8 @@ void NativeDebugger::leavingFunction(const QV4::ReturnedValue &retVal) if (m_runningJob) return; - if (m_stepping != NotStepping && m_currentContext.asManaged()->d() == m_engine->current) { - m_currentContext.set(m_engine, *m_engine->parentContext(m_engine->currentContext)); + if (m_stepping != NotStepping && m_currentFrame == m_engine->currentStackFrame) { + m_currentFrame = m_currentFrame->parent; m_stepping = StepOver; m_returnedValue.set(m_engine, retVal); } @@ -668,9 +662,8 @@ void NativeDebugger::aboutToThrow() QV4::Function *NativeDebugger::getFunction() const { - QV4::ExecutionContext *context = m_engine->currentContext; - if (QV4::Function *function = context->getFunction()) - return function; + if (m_engine->currentStackFrame) + return m_engine->currentStackFrame->v4Function; else return m_engine->globalCode; } @@ -681,12 +674,11 @@ void NativeDebugger::pauseAndWait() event.insert(QStringLiteral("event"), QStringLiteral("break")); event.insert(QStringLiteral("language"), QStringLiteral("js")); - if (QV4::ExecutionContext *executionContext = m_engine->currentContext) { - if (QV4::Function *function = executionContext->getFunction()) { - event.insert(QStringLiteral("file"), function->sourceFile()); - int line = executionContext->d()->lineNumber; - event.insert(QStringLiteral("line"), (line < 0 ? -line : line)); - } + if (QV4::CppStackFrame *frame = m_engine->currentStackFrame) { + QV4::Function *function = frame->v4Function; + event.insert(QStringLiteral("file"), function->sourceFile()); + int line = frame->lineNumber(); + event.insert(QStringLiteral("line"), (line < 0 ? -line : line)); } m_service->emitAsynchronousMessageToClient(event); @@ -697,8 +689,7 @@ bool NativeDebugger::reallyHitTheBreakPoint(const QV4::Function *function, int l for (int i = 0, n = m_service->m_breakHandler->m_breakPoints.size(); i != n; ++i) { const BreakPoint &bp = m_service->m_breakHandler->m_breakPoints.at(i); if (bp.lineNumber == lineNumber) { - const QString fileName = function->sourceFile(); - const QStringRef base = fileName.midRef(fileName.lastIndexOf('/') + 1); + const QString base = QUrl(function->sourceFile()).fileName(); if (bp.fileName.endsWith(base)) { if (bp.condition.isEmpty() || checkCondition(bp.condition)) { BreakPoint &mbp = m_service->m_breakHandler->m_breakPoints[i]; @@ -727,11 +718,10 @@ void QQmlNativeDebugServiceImpl::engineAboutToBeAdded(QJSEngine *engine) { TRACE_PROTOCOL("Adding engine" << engine); if (engine) { - QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); + QV4::ExecutionEngine *ee = engine->handle(); TRACE_PROTOCOL("Adding execution engine" << ee); if (ee) { NativeDebugger *debugger = new NativeDebugger(this, ee); - ee->iselFactory.reset(new QV4::Moth::ISelFactory); if (state() == Enabled) ee->setDebugger(debugger); m_debuggers.append(QPointer<NativeDebugger>(debugger)); @@ -744,7 +734,7 @@ void QQmlNativeDebugServiceImpl::engineAboutToBeRemoved(QJSEngine *engine) { TRACE_PROTOCOL("Removing engine" << engine); if (engine) { - QV4::ExecutionEngine *executionEngine = QV8Engine::getV4(engine->handle()); + QV4::ExecutionEngine *executionEngine = engine->handle(); const auto debuggersCopy = m_debuggers; for (NativeDebugger *debugger : debuggersCopy) { if (debugger->engine() == executionEngine) diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.h b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.h index 58bf1bc94a..4b4661be2f 100644 --- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.h +++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.h @@ -73,14 +73,14 @@ class QQmlNativeDebugServiceImpl : public QQmlNativeDebugService public: QQmlNativeDebugServiceImpl(QObject *parent); - ~QQmlNativeDebugServiceImpl() Q_DECL_OVERRIDE; + ~QQmlNativeDebugServiceImpl() override; - void engineAboutToBeAdded(QJSEngine *engine) Q_DECL_OVERRIDE; - void engineAboutToBeRemoved(QJSEngine *engine) Q_DECL_OVERRIDE; + void engineAboutToBeAdded(QJSEngine *engine) override; + void engineAboutToBeRemoved(QJSEngine *engine) override; - void stateAboutToBeChanged(State state) Q_DECL_OVERRIDE; + void stateAboutToBeChanged(State state) override; - void messageReceived(const QByteArray &message) Q_DECL_OVERRIDE; + void messageReceived(const QByteArray &message) override; void emitAsynchronousMessageToClient(const QJsonObject &message); diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservicefactory.cpp b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservicefactory.cpp index 1841c82d5d..c0b74c74ff 100644 --- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservicefactory.cpp +++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservicefactory.cpp @@ -48,7 +48,7 @@ QQmlDebugService *QQmlNativeDebugServiceFactory::create(const QString &key) if (key == QQmlNativeDebugServiceImpl::s_key) return new QQmlNativeDebugServiceImpl(this); - return 0; + return nullptr; } QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_preview/qmldbg_preview.pro b/src/plugins/qmltooling/qmldbg_preview/qmldbg_preview.pro new file mode 100644 index 0000000000..08686a43e3 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_preview/qmldbg_preview.pro @@ -0,0 +1,29 @@ +QT += core-private qml-private packetprotocol-private network quick-private gui-private + +TARGET = qmldbg_preview + +SOURCES += \ + $$PWD/qqmlpreviewblacklist.cpp \ + $$PWD/qqmlpreviewfileengine.cpp \ + $$PWD/qqmlpreviewfileloader.cpp \ + $$PWD/qqmlpreviewhandler.cpp \ + $$PWD/qqmlpreviewposition.cpp \ + $$PWD/qqmlpreviewservice.cpp \ + $$PWD/qqmlpreviewservicefactory.cpp + +HEADERS += \ + $$PWD/qqmlpreviewblacklist.h \ + $$PWD/qqmlpreviewfileengine.h \ + $$PWD/qqmlpreviewfileloader.h \ + $$PWD/qqmlpreviewhandler.h \ + $$PWD/qqmlpreviewposition.h \ + $$PWD/qqmlpreviewservice.h \ + $$PWD/qqmlpreviewservicefactory.h + +OTHER_FILES += \ + $$PWD/qqmlpreviewservice.json + +PLUGIN_TYPE = qmltooling +PLUGIN_CLASS_NAME = QQmlPreviewServiceFactory + +load(qt_plugin) diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp new file mode 100644 index 0000000000..77fe69821c --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QML preview debug service. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpreviewblacklist.h" + +QT_BEGIN_NAMESPACE + +void QQmlPreviewBlacklist::blacklist(const QString &path) +{ + if (!path.isEmpty()) + m_root.insert(path, 0); +} + +void QQmlPreviewBlacklist::whitelist(const QString &path) +{ + if (!path.isEmpty()) + m_root.remove(path, 0); +} + +bool QQmlPreviewBlacklist::isBlacklisted(const QString &path) const +{ + return path.isEmpty() ? true : m_root.containedPrefixLeaf(path, 0) > 0; +} + +void QQmlPreviewBlacklist::clear() +{ + m_root = Node(); +} + +QQmlPreviewBlacklist::Node::Node() +{ +} + +QQmlPreviewBlacklist::Node::Node(const QQmlPreviewBlacklist::Node &other) : + m_mine(other.m_mine), m_isLeaf(other.m_isLeaf) +{ + for (auto it = other.m_next.begin(), end = other.m_next.end(); it != end; ++it) + m_next.insert(it.key(), new Node(**it)); +} + +QQmlPreviewBlacklist::Node::Node(QQmlPreviewBlacklist::Node &&other) Q_DECL_NOEXCEPT +{ + m_mine.swap(other.m_mine); + m_next.swap(other.m_next); + m_isLeaf = other.m_isLeaf; +} + +QQmlPreviewBlacklist::Node::~Node() +{ + qDeleteAll(m_next); +} + +QQmlPreviewBlacklist::Node &QQmlPreviewBlacklist::Node::operator=( + const QQmlPreviewBlacklist::Node &other) +{ + if (&other != this) { + m_mine = other.m_mine; + for (auto it = other.m_next.begin(), end = other.m_next.end(); it != end; ++it) + m_next.insert(it.key(), new Node(**it)); + m_isLeaf = other.m_isLeaf; + } + return *this; +} + +QQmlPreviewBlacklist::Node &QQmlPreviewBlacklist::Node::operator=( + QQmlPreviewBlacklist::Node &&other) Q_DECL_NOEXCEPT +{ + if (&other != this) { + m_mine.swap(other.m_mine); + m_next.swap(other.m_next); + m_isLeaf = other.m_isLeaf; + } + return *this; +} + +void QQmlPreviewBlacklist::Node::split(QString::iterator it, QString::iterator end) +{ + QString existing; + existing.resize(end - it - 1); + std::copy(it + 1, end, existing.begin()); + + Node *node = new Node(existing, m_next, m_isLeaf); + m_next.clear(); + m_next.insert(*it, node); + m_mine.resize(it - m_mine.begin()); + m_isLeaf = false; +} + +void QQmlPreviewBlacklist::Node::insert(const QString &path, int offset) +{ + for (auto it = m_mine.begin(), end = m_mine.end(); it != end; ++it) { + if (offset == path.size()) { + split(it, end); + m_isLeaf = true; + return; + } + + if (path.at(offset) != *it) { + split(it, end); + + QString inserted; + inserted.resize(path.size() - offset - 1); + std::copy(path.begin() + offset + 1, path.end(), inserted.begin()); + m_next.insert(path.at(offset), new Node(inserted)); + return; + } + + ++offset; + } + + if (offset == path.size()) { + m_isLeaf = true; + return; + } + + Node *&node = m_next[path.at(offset++)]; + if (node == nullptr) { + QString inserted; + inserted.resize(path.size() - offset); + std::copy(path.begin() + offset, path.end(), inserted.begin()); + node = new Node(inserted); + } else { + node->insert(path, offset); + } +} + +void QQmlPreviewBlacklist::Node::remove(const QString &path, int offset) +{ + for (auto it = m_mine.begin(), end = m_mine.end(); it != end; ++it) { + if (offset == path.size() || path.at(offset) != *it) { + split(it, end); + return; + } + ++offset; + } + + m_isLeaf = false; + if (offset == path.size()) + return; + + auto it = m_next.find(path.at(offset)); + if (it != m_next.end()) + (*it)->remove(path, ++offset); +} + +int QQmlPreviewBlacklist::Node::containedPrefixLeaf(const QString &path, int offset) const +{ + if (offset == path.size()) + return (m_mine.isEmpty() && m_isLeaf) ? offset : -1; + + for (auto it = m_mine.begin(), end = m_mine.end(); it != end; ++it) { + if (path.at(offset) != *it) + return -1; + + if (++offset == path.size()) + return (++it == end && m_isLeaf) ? offset : -1; + } + + const QChar c = path.at(offset); + if (m_isLeaf && c == '/') + return offset; + + auto it = m_next.find(c); + if (it == m_next.end()) + return -1; + + return (*it)->containedPrefixLeaf(path, ++offset); +} + +QQmlPreviewBlacklist::Node::Node(const QString &mine, + const QHash<QChar, QQmlPreviewBlacklist::Node *> &next, + bool isLeaf) + : m_mine(mine), m_next(next), m_isLeaf(isLeaf) +{ +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h new file mode 100644 index 0000000000..2f743ca7a6 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QML preview debug service. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPREVIEWBLACKLIST_H +#define QQMLPREVIEWBLACKLIST_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qhash.h> +#include <QtCore/qchar.h> +#include <QtCore/qstring.h> +#include <algorithm> + +QT_BEGIN_NAMESPACE + +class QQmlPreviewBlacklist +{ +public: + void blacklist(const QString &path); + void whitelist(const QString &path); + bool isBlacklisted(const QString &path) const; + void clear(); + +private: + class Node { + public: + Node(); + Node(const Node &other); + Node(Node &&other) Q_DECL_NOEXCEPT; + + ~Node(); + + Node &operator=(const Node &other); + Node &operator=(Node &&other) Q_DECL_NOEXCEPT; + + void split(QString::iterator it, QString::iterator end); + void insert(const QString &path, int offset); + void remove(const QString &path, int offset); + int containedPrefixLeaf(const QString &path, int offset) const; + + private: + Node(const QString &mine, const QHash<QChar, Node *> &next = QHash<QChar, Node *>(), + bool isLeaf = true); + + QString m_mine; + QHash<QChar, Node *> m_next; + bool m_isLeaf = false; + }; + + Node m_root; +}; + +QT_END_NAMESPACE + +#endif // QQMLPREVIEWBLACKLIST_H diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp new file mode 100644 index 0000000000..72de52bbe1 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp @@ -0,0 +1,443 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QML preview debug service. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpreviewfileengine.h" +#include "qqmlpreviewservice.h" + +#include <QtCore/qlibraryinfo.h> +#include <QtCore/qthread.h> +#include <QtCore/qwaitcondition.h> + +#include <cstring> + +QT_BEGIN_NAMESPACE + +static bool isRelative(const QString &path) +{ + if (path.isEmpty()) + return true; + if (path.at(0) == '/') + return false; + if (path.at(0) == ':' && path.length() >= 2 && path.at(1) == '/') + return false; +#ifdef Q_OS_WIN + if (path.length() >= 2 && path.at(1) == ':') + return false; +#endif + return true; +} + +static QString absolutePath(const QString &path) +{ + return QDir::cleanPath(isRelative(path) ? (QDir::currentPath() + '/' + path) : path); +} + +bool isRootPath(const QString &path) +{ + return QFileSystemEntry::isRootPath(path); +} + +class QQmlPreviewFileEngineIterator : public QAbstractFileEngineIterator +{ +public: + QQmlPreviewFileEngineIterator(QDir::Filters filters, const QStringList &filterNames, + const QStringList &m_entries); + ~QQmlPreviewFileEngineIterator(); + + QString next() override; + bool hasNext() const override; + QString currentFileName() const override; + +private: + const QStringList m_entries; + int m_index; +}; + +QQmlPreviewFileEngineIterator::QQmlPreviewFileEngineIterator(QDir::Filters filters, + const QStringList &filterNames, + const QStringList &entries) + : QAbstractFileEngineIterator(filters, filterNames), m_entries(entries), m_index(0) +{ +} + +QQmlPreviewFileEngineIterator::~QQmlPreviewFileEngineIterator() +{ +} + +QString QQmlPreviewFileEngineIterator::next() +{ + if (!hasNext()) + return QString(); + ++m_index; + return currentFilePath(); +} + +bool QQmlPreviewFileEngineIterator::hasNext() const +{ + return m_index < m_entries.size(); +} + +QString QQmlPreviewFileEngineIterator::currentFileName() const +{ + if (m_index == 0 || m_index > m_entries.size()) + return QString(); + return m_entries.at(m_index - 1); +} + +QQmlPreviewFileEngine::QQmlPreviewFileEngine(const QString &file, const QString &absolute, + QQmlPreviewFileLoader *loader) : + m_name(file), m_absolute(absolute), m_loader(loader) +{ + load(); +} + +void QQmlPreviewFileEngine::setFileName(const QString &file) +{ + m_name = file; + m_absolute = absolutePath(file); + m_fallback.reset(); + m_contents.close(); + m_contents.setData(QByteArray()); + m_entries.clear(); + load(); +} + +bool QQmlPreviewFileEngine::open(QIODevice::OpenMode flags) +{ + switch (m_result) { + case QQmlPreviewFileLoader::File: + return m_contents.open(flags); + case QQmlPreviewFileLoader::Directory: + return false; + case QQmlPreviewFileLoader::Fallback: + return m_fallback->open(flags); + default: + Q_UNREACHABLE(); + return false; + } +} + +bool QQmlPreviewFileEngine::close() +{ + switch (m_result) { + case QQmlPreviewFileLoader::Fallback: + return m_fallback->close(); + case QQmlPreviewFileLoader::File: + m_contents.close(); + return true; + case QQmlPreviewFileLoader::Directory: + return false; + default: + Q_UNREACHABLE(); + return false; + } +} + +qint64 QQmlPreviewFileEngine::size() const +{ + return m_fallback ? m_fallback->size() : m_contents.size(); +} + +qint64 QQmlPreviewFileEngine::pos() const +{ + return m_fallback ? m_fallback->pos() : m_contents.pos(); +} + +bool QQmlPreviewFileEngine::seek(qint64 newPos) +{ + return m_fallback? m_fallback->seek(newPos) : m_contents.seek(newPos); +} + +qint64 QQmlPreviewFileEngine::read(char *data, qint64 maxlen) +{ + return m_fallback ? m_fallback->read(data, maxlen) : m_contents.read(data, maxlen); +} + +QAbstractFileEngine::FileFlags QQmlPreviewFileEngine::fileFlags( + QAbstractFileEngine::FileFlags type) const +{ + if (m_fallback) + return m_fallback->fileFlags(type); + + QAbstractFileEngine::FileFlags ret = 0; + + if (type & PermsMask) { + ret |= QAbstractFileEngine::FileFlags( + ReadOwnerPerm | ReadUserPerm | ReadGroupPerm | ReadOtherPerm); + } + + if (type & TypesMask) { + if (m_result == QQmlPreviewFileLoader::Directory) + ret |= DirectoryType; + else + ret |= FileType; + } + + if (type & FlagsMask) { + ret |= ExistsFlag; + if (isRootPath(m_name)) + ret |= RootFlag; + } + + return ret; +} + +QString QQmlPreviewFileEngine::fileName(QAbstractFileEngine::FileName file) const +{ + if (m_fallback) + return m_fallback->fileName(file); + + if (file == BaseName) { + int slashPos = m_name.lastIndexOf('/'); + if (slashPos == -1) + return m_name; + return m_name.mid(slashPos + 1); + } else if (file == PathName || file == AbsolutePathName) { + const QString path = (file == AbsolutePathName) ? m_absolute : m_name; + const int slashPos = path.lastIndexOf('/'); + if (slashPos == -1) + return QString(); + else if (slashPos == 0) + return "/"; + return path.left(slashPos); + } else if (file == CanonicalName || file == CanonicalPathName) { + if (file == CanonicalPathName) { + const int slashPos = m_absolute.lastIndexOf('/'); + if (slashPos != -1) + return m_absolute.left(slashPos); + } + return m_absolute; + } + return m_name; +} + +uint QQmlPreviewFileEngine::ownerId(QAbstractFileEngine::FileOwner owner) const +{ + return m_fallback ? m_fallback->ownerId(owner) : static_cast<uint>(-2); +} + +QAbstractFileEngine::Iterator *QQmlPreviewFileEngine::beginEntryList(QDir::Filters filters, + const QStringList &filterNames) +{ + return m_fallback ? m_fallback->beginEntryList(filters, filterNames) + : new QQmlPreviewFileEngineIterator(filters, filterNames, m_entries); +} + +QAbstractFileEngine::Iterator *QQmlPreviewFileEngine::endEntryList() +{ + return m_fallback ? m_fallback->endEntryList() : nullptr; +} + +bool QQmlPreviewFileEngine::flush() +{ + return m_fallback ? m_fallback->flush() : true; +} + +bool QQmlPreviewFileEngine::syncToDisk() +{ + return m_fallback ? m_fallback->syncToDisk() : false; +} + +bool QQmlPreviewFileEngine::isSequential() const +{ + return m_fallback ? m_fallback->isSequential() : m_contents.isSequential(); +} + +bool QQmlPreviewFileEngine::remove() +{ + return m_fallback ? m_fallback->remove() : false; +} + +bool QQmlPreviewFileEngine::copy(const QString &newName) +{ + return m_fallback ? m_fallback->copy(newName) : false; +} + +bool QQmlPreviewFileEngine::rename(const QString &newName) +{ + return m_fallback ? m_fallback->rename(newName) : false; +} + +bool QQmlPreviewFileEngine::renameOverwrite(const QString &newName) +{ + return m_fallback ? m_fallback->renameOverwrite(newName) : false; +} + +bool QQmlPreviewFileEngine::link(const QString &newName) +{ + return m_fallback ? m_fallback->link(newName) : false; +} + +bool QQmlPreviewFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const +{ + return m_fallback ? m_fallback->mkdir(dirName, createParentDirectories) : false; +} + +bool QQmlPreviewFileEngine::rmdir(const QString &dirName, bool recurseParentDirectories) const +{ + return m_fallback ? m_fallback->rmdir(dirName, recurseParentDirectories) : false; +} + +bool QQmlPreviewFileEngine::setSize(qint64 size) +{ + switch (m_result) { + case QQmlPreviewFileLoader::Fallback: + return m_fallback->setSize(size); + case QQmlPreviewFileLoader::File: + if (size < 0 || size > std::numeric_limits<int>::max()) + return false; + m_contents.buffer().resize(static_cast<int>(size)); + return true; + case QQmlPreviewFileLoader::Directory: + return false; + default: + Q_UNREACHABLE(); + return false; + } +} + +bool QQmlPreviewFileEngine::caseSensitive() const +{ + return m_fallback ? m_fallback->caseSensitive() : true; +} + +bool QQmlPreviewFileEngine::isRelativePath() const +{ + return m_fallback ? m_fallback->isRelativePath() : isRelative(m_name); +} + +QStringList QQmlPreviewFileEngine::entryList(QDir::Filters filters, + const QStringList &filterNames) const +{ + return m_fallback ? m_fallback->entryList(filters, filterNames) + : QAbstractFileEngine::entryList(filters, filterNames); +} + +bool QQmlPreviewFileEngine::setPermissions(uint perms) +{ + return m_fallback ? m_fallback->setPermissions(perms) : false; +} + +QByteArray QQmlPreviewFileEngine::id() const +{ + return m_fallback ? m_fallback->id() : QByteArray(); +} + +QString QQmlPreviewFileEngine::owner(FileOwner owner) const +{ + return m_fallback ? m_fallback->owner(owner) : QString(); +} + +QDateTime QQmlPreviewFileEngine::fileTime(FileTime time) const +{ + // Files we replace are always newer than the ones we had before. This makes the QML engine + // actually recompile them, rather than pick them from the cache. + return m_fallback ? m_fallback->fileTime(time) : QDateTime::currentDateTime(); +} + +int QQmlPreviewFileEngine::handle() const +{ + return m_fallback ? m_fallback->handle() : -1; +} + +qint64 QQmlPreviewFileEngine::readLine(char *data, qint64 maxlen) +{ + return m_fallback ? m_fallback->readLine(data, maxlen) : m_contents.readLine(data, maxlen); +} + +qint64 QQmlPreviewFileEngine::write(const char *data, qint64 len) +{ + return m_fallback ? m_fallback->write(data, len) : m_contents.write(data, len); +} + +bool QQmlPreviewFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output) +{ + return m_fallback ? m_fallback->extension(extension, option, output) : false; +} + +bool QQmlPreviewFileEngine::supportsExtension(Extension extension) const +{ + return m_fallback ? m_fallback->supportsExtension(extension) : false; +} + +void QQmlPreviewFileEngine::load() const +{ + m_result = m_loader->load(m_absolute); + switch (m_result) { + case QQmlPreviewFileLoader::File: + m_contents.setData(m_loader->contents()); + break; + case QQmlPreviewFileLoader::Directory: + m_entries = m_loader->entries(); + break; + case QQmlPreviewFileLoader::Fallback: + m_fallback.reset(QAbstractFileEngine::create(m_name)); + break; + case QQmlPreviewFileLoader::Unknown: + Q_UNREACHABLE(); + break; + } +} + +QQmlPreviewFileEngineHandler::QQmlPreviewFileEngineHandler(QQmlPreviewFileLoader *loader) + : m_loader(loader) +{ +} + +QAbstractFileEngine *QQmlPreviewFileEngineHandler::create(const QString &fileName) const +{ + // Don't load compiled QML/JS over the network + if (fileName.endsWith(".qmlc") || fileName.endsWith(".jsc") || isRootPath(fileName)) { + return nullptr; + } + + QString relative = fileName; + while (relative.endsWith('/')) + relative.chop(1); + + if (relative.isEmpty() || relative == ":") + return nullptr; + + const QString absolute = relative.startsWith(':') ? relative : absolutePath(relative); + + return m_loader->isBlacklisted(absolute) + ? nullptr : new QQmlPreviewFileEngine(relative, absolute, m_loader.data()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.h new file mode 100644 index 0000000000..9a40b6360c --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QML preview debug service. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPREVIEWFILEENGINE_H +#define QQMLPREVIEWFILEENGINE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qqmlpreviewfileloader.h" + +#include <private/qabstractfileengine_p.h> +#include <private/qfsfileengine_p.h> +#include <QtCore/qbuffer.h> + +QT_BEGIN_NAMESPACE + +class QQmlPreviewFileEngine : public QAbstractFileEngine +{ +public: + QQmlPreviewFileEngine(const QString &file, const QString &absolute, + QQmlPreviewFileLoader *loader); + + void setFileName(const QString &file) override; + + bool open(QIODevice::OpenMode flags) override ; + bool close() override; + qint64 size() const override; + qint64 pos() const override; + bool seek(qint64) override; + qint64 read(char *data, qint64 maxlen) override; + + FileFlags fileFlags(FileFlags type) const override; + QString fileName(QAbstractFileEngine::FileName file) const override; + uint ownerId(FileOwner) const override; + + Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override; + Iterator *endEntryList() override; + + // Forwarding to fallback if exists + bool flush() override; + bool syncToDisk() override; + bool isSequential() const override; + bool remove() override; + bool copy(const QString &newName) override; + bool rename(const QString &newName) override; + bool renameOverwrite(const QString &newName) override; + bool link(const QString &newName) override; + bool mkdir(const QString &dirName, bool createParentDirectories) const override; + bool rmdir(const QString &dirName, bool recurseParentDirectories) const override; + bool setSize(qint64 size) override; + bool caseSensitive() const override; + bool isRelativePath() const override; + QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const override; + bool setPermissions(uint perms) override; + QByteArray id() const override; + QString owner(FileOwner) const override; + QDateTime fileTime(FileTime time) const override; + int handle() const override; + qint64 readLine(char *data, qint64 maxlen) override; + qint64 write(const char *data, qint64 len) override; + bool extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output) override; + bool supportsExtension(Extension extension) const override; + +private: + void load() const; + + QString m_name; + QString m_absolute; + QPointer<QQmlPreviewFileLoader> m_loader; + + mutable QBuffer m_contents; + mutable QStringList m_entries; + mutable QScopedPointer<QAbstractFileEngine> m_fallback; + mutable QQmlPreviewFileLoader::Result m_result = QQmlPreviewFileLoader::Unknown; +}; + +class QQmlPreviewFileEngineHandler : public QAbstractFileEngineHandler +{ +public: + QQmlPreviewFileEngineHandler(QQmlPreviewFileLoader *loader); + QAbstractFileEngine *create(const QString &fileName) const override; + +private: + QPointer<QQmlPreviewFileLoader> m_loader; +}; + + + +QT_END_NAMESPACE + +#endif // QQMLPREVIEWFILEENGINE_H diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp new file mode 100644 index 0000000000..9927089e5e --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QML preview debug service. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpreviewfileloader.h" +#include "qqmlpreviewservice.h" + +#include <QtCore/qlibraryinfo.h> +#include <QtCore/qstandardpaths.h> + +QT_BEGIN_NAMESPACE + +QQmlPreviewFileLoader::QQmlPreviewFileLoader(QQmlPreviewServiceImpl *service) : m_service(service) +{ + // Exclude some resource paths used by Qt itself. There is no point in loading those from the + // client as the client will not have the files (or even worse, it may have different ones). + m_blacklist.blacklist(":/qt-project.org"); + m_blacklist.blacklist(":/QtQuick/Controls/Styles"); + m_blacklist.blacklist(":/ExtrasImports/QtQuick/Controls/Styles"); + m_blacklist.blacklist(":/qgradient"); + + // Target specific configuration should not replaced with files from the host. + m_blacklist.blacklist("/etc"); + + for (int loc = QLibraryInfo::PrefixPath; loc < QLibraryInfo::TestsPath; ++loc) { + m_blacklist.blacklist(QLibraryInfo::location( + static_cast<QLibraryInfo::LibraryLocation>(loc))); + } + m_blacklist.blacklist(QLibraryInfo::location(QLibraryInfo::SettingsPath)); + + static const QStandardPaths::StandardLocation blackListLocations[] = { + QStandardPaths::DataLocation, + QStandardPaths::CacheLocation, + QStandardPaths::GenericDataLocation, + QStandardPaths::ConfigLocation, + QStandardPaths::GenericCacheLocation, + QStandardPaths::GenericConfigLocation, + QStandardPaths::AppDataLocation, + QStandardPaths::AppConfigLocation + }; + + for (auto locationType : blackListLocations) { + const QStringList locations = QStandardPaths::standardLocations(locationType); + for (const QString &location : locations) + m_blacklist.blacklist(location); + } + + m_blacklist.whitelist(QLibraryInfo::location(QLibraryInfo::TestsPath)); + + connect(this, &QQmlPreviewFileLoader::request, service, &QQmlPreviewServiceImpl::forwardRequest, + Qt::DirectConnection); + connect(service, &QQmlPreviewServiceImpl::directory, this, &QQmlPreviewFileLoader::directory); + connect(service, &QQmlPreviewServiceImpl::file, this, &QQmlPreviewFileLoader::file); + connect(service, &QQmlPreviewServiceImpl::error, this, &QQmlPreviewFileLoader::error); + connect(service, &QQmlPreviewServiceImpl::clearCache, this, &QQmlPreviewFileLoader::clearCache); + moveToThread(&m_thread); + m_thread.start(); +} + +QQmlPreviewFileLoader::~QQmlPreviewFileLoader() { + m_thread.quit(); + m_thread.wait(); +} + +QQmlPreviewFileLoader::Result QQmlPreviewFileLoader::load(const QString &path) +{ + QMutexLocker locker(&m_mutex); + m_path = path; + + auto fileIterator = m_fileCache.constFind(path); + if (fileIterator != m_fileCache.constEnd()) { + m_result = File; + m_contents = *fileIterator; + m_entries.clear(); + return m_result; + } + + auto dirIterator = m_directoryCache.constFind(path); + if (dirIterator != m_directoryCache.constEnd()) { + m_result = Directory; + m_contents.clear(); + m_entries = *dirIterator; + return m_result; + } + + m_result = Unknown; + m_entries.clear(); + m_contents.clear(); + emit request(path); + m_waitCondition.wait(&m_mutex); + return m_result; +} + +QByteArray QQmlPreviewFileLoader::contents() +{ + QMutexLocker locker(&m_mutex); + return m_contents; +} + +QStringList QQmlPreviewFileLoader::entries() +{ + QMutexLocker locker(&m_mutex); + return m_entries; +} + +void QQmlPreviewFileLoader::whitelist(const QUrl &url) +{ + const QString path = QQmlFile::urlToLocalFileOrQrc(url); + if (!path.isEmpty()) { + QMutexLocker locker(&m_mutex); + m_blacklist.whitelist(path); + } +} + +bool QQmlPreviewFileLoader::isBlacklisted(const QString &path) +{ + QMutexLocker locker(&m_mutex); + return m_blacklist.isBlacklisted(path); +} + +void QQmlPreviewFileLoader::file(const QString &path, const QByteArray &contents) +{ + QMutexLocker locker(&m_mutex); + m_blacklist.whitelist(path); + m_fileCache[path] = contents; + if (path == m_path) { + m_contents = contents; + m_result = File; + m_waitCondition.wakeOne(); + } +} + +void QQmlPreviewFileLoader::directory(const QString &path, const QStringList &entries) +{ + QMutexLocker locker(&m_mutex); + m_blacklist.whitelist(path); + m_directoryCache[path] = entries; + if (path == m_path) { + m_entries = entries; + m_result = Directory; + m_waitCondition.wakeOne(); + } +} + +void QQmlPreviewFileLoader::error(const QString &path) +{ + QMutexLocker locker(&m_mutex); + m_blacklist.blacklist(path); + if (path == m_path) { + m_result = Fallback; + m_waitCondition.wakeOne(); + } +} + +void QQmlPreviewFileLoader::clearCache() +{ + QMutexLocker locker(&m_mutex); + m_fileCache.clear(); + m_directoryCache.clear(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/shared/qqmlconfigurabledebugservice.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h index 85ff9b182f..0c55c48c4a 100644 --- a/src/plugins/qmltooling/shared/qqmlconfigurabledebugservice.h +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h @@ -1,9 +1,9 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QML preview debug service. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -37,9 +37,8 @@ ** ****************************************************************************/ - -#ifndef QQMLCONFIGURABLEDEBUGSEVICE_H -#define QQMLCONFIGURABLEDEBUGSEVICE_H +#ifndef QQMLPREVIEWFILELOADER_H +#define QQMLPREVIEWFILELOADER_H // // W A R N I N G @@ -52,61 +51,66 @@ // We mean it. // -#include <private/qqmldebugservice_p.h> -#include <private/qqmldebugconnector_p.h> +#include "qqmlpreviewblacklist.h" + +#include <QtCore/qobject.h> +#include <QtCore/qthread.h> #include <QtCore/qmutex.h> +#include <QtCore/qwaitcondition.h> +#include <QtCore/qvector.h> +#include <QtCore/qurl.h> +#include <QtCore/qpointer.h> +#include <QtCore/qset.h> QT_BEGIN_NAMESPACE -template <class Base> -class QQmlConfigurableDebugService : public Base +class QQmlPreviewServiceImpl; +class QQmlPreviewFileLoader : public QObject { -protected: - QQmlConfigurableDebugService(float version, QObject *parent = 0) : - Base(version, parent), m_configMutex(QMutex::Recursive) - { - init(); - } - - void stopWaiting() - { - QMutexLocker lock(&m_configMutex); - m_waitingForConfiguration = false; - for (QJSEngine *engine : qAsConst(m_waitingEngines)) - emit Base::attachedToEngine(engine); - m_waitingEngines.clear(); - } - - void init() - { - QMutexLocker lock(&m_configMutex); - // If we're not enabled or not blocking, don't wait for configuration - m_waitingForConfiguration = (Base::state() == QQmlDebugService::Enabled && - QQmlDebugConnector::instance()->blockingMode()); - } - - void stateChanged(QQmlDebugService::State newState) Q_DECL_OVERRIDE - { - if (newState != QQmlDebugService::Enabled) - stopWaiting(); - else - init(); - } - - void engineAboutToBeAdded(QJSEngine *engine) Q_DECL_OVERRIDE - { - QMutexLocker lock(&m_configMutex); - if (m_waitingForConfiguration) - m_waitingEngines.append(engine); - else - emit Base::attachedToEngine(engine); - } - - QMutex m_configMutex; - QList<QJSEngine *> m_waitingEngines; - bool m_waitingForConfiguration; + Q_OBJECT +public: + enum Result { + File, + Directory, + Fallback, + Unknown + }; + + QQmlPreviewFileLoader(QQmlPreviewServiceImpl *service); + ~QQmlPreviewFileLoader(); + + Result load(const QString &file); + QByteArray contents(); + QStringList entries(); + + void whitelist(const QUrl &url); + bool isBlacklisted(const QString &file); + +signals: + void request(const QString &file); + +private: + QMutex m_mutex; + QWaitCondition m_waitCondition; + + QThread m_thread; + QPointer<QQmlPreviewServiceImpl> m_service; + + QString m_path; + QByteArray m_contents; + QStringList m_entries; + Result m_result; + + QQmlPreviewBlacklist m_blacklist; + QHash<QString, QByteArray> m_fileCache; + QHash<QString, QStringList> m_directoryCache; + + void file(const QString &file, const QByteArray &contents); + void directory(const QString &file, const QStringList &entries); + void error(const QString &file); + void clearCache(); }; QT_END_NAMESPACE -#endif // QQMLCONFIGURABLEDEBUGSEVICE_H +#endif // QQMLPREVIEWFILELOADER_H diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp new file mode 100644 index 0000000000..5bd96af582 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp @@ -0,0 +1,472 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QML preview debug service. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpreviewhandler.h" + +#include <QtCore/qtimer.h> +#include <QtCore/qsettings.h> +#include <QtCore/qlibraryinfo.h> +#include <QtCore/qtranslator.h> + +#include <QtGui/qwindow.h> +#include <QtGui/qguiapplication.h> +#include <QtQuick/qquickwindow.h> +#include <QtQuick/qquickitem.h> +#include <QtQml/qqmlcomponent.h> + +#include <private/qquickpixmapcache_p.h> +#include <private/qquickview_p.h> +#include <private/qhighdpiscaling_p.h> + +QT_BEGIN_NAMESPACE + +struct QuitLockDisabler +{ + const bool quitLockEnabled; + + QuitLockDisabler() : quitLockEnabled(QCoreApplication::isQuitLockEnabled()) + { + QCoreApplication::setQuitLockEnabled(false); + } + + ~QuitLockDisabler() + { + QCoreApplication::setQuitLockEnabled(quitLockEnabled); + } +}; + +QQmlPreviewHandler::QQmlPreviewHandler(QObject *parent) : QObject(parent) +{ + m_dummyItem.reset(new QQuickItem); + + // TODO: Is there a better way to determine this? We want to keep the window alive when possible + // as otherwise it will reappear in a different place when (re)loading a file. However, + // the file we load might create another window, in which case the eglfs plugin (and + // others?) will do a qFatal as it only supports a single window. + const QString platformName = QGuiApplication::platformName(); + m_supportsMultipleWindows = (platformName == QStringLiteral("windows") + || platformName == QStringLiteral("cocoa") + || platformName == QStringLiteral("xcb") + || platformName == QStringLiteral("wayland")); + + QCoreApplication::instance()->installEventFilter(this); + + m_fpsTimer.setInterval(1000); + connect(&m_fpsTimer, &QTimer::timeout, this, &QQmlPreviewHandler::fpsTimerHit); +} + +QQmlPreviewHandler::~QQmlPreviewHandler() +{ + removeTranslators(); + clear(); +} + +static void closeAllWindows() +{ + const QWindowList windows = QGuiApplication::allWindows(); + for (QWindow *window : windows) + window->close(); +} + +bool QQmlPreviewHandler::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::Show) { + if (QWindow *window = qobject_cast<QQuickWindow*>(obj)) { + m_lastPosition.initLastSavedWindowPosition(window); + } + } + if (m_currentWindow && (event->type() == QEvent::Move || event->type() == QEvent::Resize) && + qobject_cast<QQuickWindow*>(obj) == m_currentWindow) { + // we always start with factor 1 so calculate and save the origin as it would be not scaled + m_lastPosition.setPosition(m_currentWindow->framePosition() * + QHighDpiScaling::factor(m_currentWindow)); + } + + return QObject::eventFilter(obj, event); +} + +void QQmlPreviewHandler::addEngine(QQmlEngine *qmlEngine) +{ + m_engines.append(qmlEngine); +} + +void QQmlPreviewHandler::removeEngine(QQmlEngine *qmlEngine) +{ + const bool found = m_engines.removeOne(qmlEngine); + Q_ASSERT(found); + for (QObject *obj : m_createdObjects) + if (obj && QtQml::qmlEngine(obj) == qmlEngine) + delete obj; + m_createdObjects.removeAll(nullptr); +} + +void QQmlPreviewHandler::loadUrl(const QUrl &url) +{ + QSharedPointer<QuitLockDisabler> disabler(new QuitLockDisabler); + + clear(); + m_component.reset(nullptr); + QQuickPixmap::purgeCache(); + + const int numEngines = m_engines.count(); + if (numEngines > 1) { + emit error(QString::fromLatin1("%1 QML engines available. We cannot decide which one " + "should load the component.").arg(numEngines)); + return; + } else if (numEngines == 0) { + emit error(QLatin1String("No QML engines found.")); + return; + } + m_lastPosition.loadWindowPositionSettings(url); + + QQmlEngine *engine = m_engines.front(); + engine->clearComponentCache(); + m_component.reset(new QQmlComponent(engine, url, this)); + + auto onStatusChanged = [disabler, this](QQmlComponent::Status status) { + switch (status) { + case QQmlComponent::Null: + case QQmlComponent::Loading: + return true; // try again later + case QQmlComponent::Ready: + tryCreateObject(); + break; + case QQmlComponent::Error: + emit error(m_component->errorString()); + break; + default: + Q_UNREACHABLE(); + break; + } + + disconnect(m_component.data(), &QQmlComponent::statusChanged, this, nullptr); + return false; // we're done + }; + + if (onStatusChanged(m_component->status())) + connect(m_component.data(), &QQmlComponent::statusChanged, this, onStatusChanged); +} + +void QQmlPreviewHandler::rerun() +{ + if (m_component.isNull() || !m_component->isReady()) + emit error(QLatin1String("Component is not ready.")); + + QuitLockDisabler disabler; + Q_UNUSED(disabler); + clear(); + tryCreateObject(); +} + +void QQmlPreviewHandler::zoom(qreal newFactor) +{ + if (!m_currentWindow) + return; + if (qFuzzyIsNull(newFactor)) { + emit error(QString::fromLatin1("Zooming with factor: %1 will result in nothing " \ + "so it will be ignored.").arg(newFactor)); + return; + } + QString errorMessage; + bool resetZoom = false; + + if (newFactor < 0) { + resetZoom = true; + newFactor = 1.0; + } + + // On single-window devices we allow any scale factor as the window will adapt to the screen. + if (m_supportsMultipleWindows) { + const QSize newAvailableScreenSize = QQmlPreviewPosition::currentScreenSize(m_currentWindow) + * QHighDpiScaling::factor(m_currentWindow) / newFactor; + if (m_currentWindow->size().width() > newAvailableScreenSize.width()) { + errorMessage = QString::fromLatin1( + "Zooming with factor: " + "%1 will result in a too wide preview.").arg(newFactor); + } + if (m_currentWindow->size().height() > newAvailableScreenSize.height()) { + errorMessage = QString::fromLatin1( + "Zooming with factor: " + "%1 will result in a too heigh preview.").arg(newFactor); + } + } + + if (errorMessage.isEmpty()) { + const QPoint newToOriginMappedPosition = m_currentWindow->position() * + QHighDpiScaling::factor(m_currentWindow) / newFactor; + m_currentWindow->destroy(); + QHighDpiScaling::setScreenFactor(m_currentWindow->screen(), newFactor); + if (resetZoom) + QHighDpiScaling::updateHighDpiScaling(); + m_currentWindow->setPosition(newToOriginMappedPosition); + m_currentWindow->show(); + } else { + emit error(errorMessage); + } +} + +void QQmlPreviewHandler::removeTranslators() +{ + if (!m_qtTranslator.isNull()) { + QCoreApplication::removeTranslator(m_qtTranslator.get()); + m_qtTranslator.reset(); + } + + if (m_qmlTranslator.isNull()) { + QCoreApplication::removeTranslator(m_qmlTranslator.get()); + m_qmlTranslator.reset(); + } +} + +void QQmlPreviewHandler::language(const QUrl &context, const QLocale &locale) +{ + removeTranslators(); + + m_qtTranslator.reset(new QTranslator(this)); + if (m_qtTranslator->load(locale, QLatin1String("qt"), QLatin1String("_"), + QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { + QCoreApplication::installTranslator(m_qtTranslator.get()); + } + + m_qmlTranslator.reset(new QTranslator(this)); + if (m_qmlTranslator->load(locale, QLatin1String("qml"), QLatin1String("_"), + context.toLocalFile() + QLatin1String("/i18n"))) { + QCoreApplication::installTranslator(m_qmlTranslator.get()); + } + + for (QQmlEngine *engine : qAsConst(m_engines)) + engine->retranslate(); +} + +void QQmlPreviewHandler::clear() +{ + qDeleteAll(m_createdObjects); + m_createdObjects.clear(); + setCurrentWindow(nullptr); +} + +Qt::WindowFlags fixFlags(Qt::WindowFlags flags) +{ + // If only the type flag is given, some other window flags are automatically assumed. When we + // add a flag, we need to make those explicit. + switch (flags) { + case Qt::Window: + return flags | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint + | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint; + case Qt::Dialog: + case Qt::Tool: + return flags | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint; + default: + return flags; + } +} + +void QQmlPreviewHandler::showObject(QObject *object) +{ + if (QWindow *window = qobject_cast<QWindow *>(object)) { + setCurrentWindow(qobject_cast<QQuickWindow *>(window)); + for (QWindow *otherWindow : QGuiApplication::allWindows()) { + if (QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(otherWindow)) { + if (quickWindow == m_currentWindow) + continue; + quickWindow->setVisible(false); + quickWindow->setFlags(quickWindow->flags() & ~Qt::WindowStaysOnTopHint); + } + } + } else if (QQuickItem *item = qobject_cast<QQuickItem *>(object)) { + setCurrentWindow(nullptr); + for (QWindow *window : QGuiApplication::allWindows()) { + if (QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(window)) { + if (m_currentWindow != nullptr) { + emit error(QLatin1String("Multiple QQuickWindows available. We cannot " + "decide which one to use.")); + return; + } + setCurrentWindow(quickWindow); + } else { + window->setVisible(false); + window->setFlag(Qt::WindowStaysOnTopHint, false); + } + } + + if (m_currentWindow == nullptr) { + setCurrentWindow(new QQuickWindow); + m_createdObjects.append(m_currentWindow.data()); + } + + for (QQuickItem *oldItem : m_currentWindow->contentItem()->childItems()) + oldItem->setParentItem(m_dummyItem.data()); + + // Special case for QQuickView, as that keeps a "root" pointer around, and uses it to + // automatically resize the window or the item. + if (QQuickView *view = qobject_cast<QQuickView *>(m_currentWindow)) + QQuickViewPrivate::get(view)->setRootObject(item); + else + item->setParentItem(m_currentWindow->contentItem()); + + m_currentWindow->resize(item->size().toSize()); + } else { + emit error(QLatin1String("Created object is neither a QWindow nor a QQuickItem.")); + } + + if (m_currentWindow) { + m_lastPosition.initLastSavedWindowPosition(m_currentWindow); + m_currentWindow->setFlags(fixFlags(m_currentWindow->flags()) | Qt::WindowStaysOnTopHint); + m_currentWindow->setVisible(true); + } +} + +void QQmlPreviewHandler::setCurrentWindow(QQuickWindow *window) +{ + if (window == m_currentWindow.data()) + return; + + if (m_currentWindow) { + disconnect(m_currentWindow.data(), &QQuickWindow::beforeSynchronizing, + this, &QQmlPreviewHandler::beforeSynchronizing); + disconnect(m_currentWindow.data(), &QQuickWindow::afterSynchronizing, + this, &QQmlPreviewHandler::afterSynchronizing); + disconnect(m_currentWindow.data(), &QQuickWindow::beforeRendering, + this, &QQmlPreviewHandler::beforeRendering); + disconnect(m_currentWindow.data(), &QQuickWindow::frameSwapped, + this, &QQmlPreviewHandler::frameSwapped); + m_fpsTimer.stop(); + m_rendering = FrameTime(); + m_synchronizing = FrameTime(); + } + + m_currentWindow = window; + + if (m_currentWindow) { + connect(m_currentWindow.data(), &QQuickWindow::beforeSynchronizing, + this, &QQmlPreviewHandler::beforeSynchronizing, Qt::DirectConnection); + connect(m_currentWindow.data(), &QQuickWindow::afterSynchronizing, + this, &QQmlPreviewHandler::afterSynchronizing, Qt::DirectConnection); + connect(m_currentWindow.data(), &QQuickWindow::beforeRendering, + this, &QQmlPreviewHandler::beforeRendering, Qt::DirectConnection); + connect(m_currentWindow.data(), &QQuickWindow::frameSwapped, + this, &QQmlPreviewHandler::frameSwapped, Qt::DirectConnection); + m_fpsTimer.start(); + } +} + +void QQmlPreviewHandler::beforeSynchronizing() +{ + m_synchronizing.beginFrame(); +} + +void QQmlPreviewHandler::afterSynchronizing() +{ + + if (m_rendering.elapsed >= 0) + m_rendering.endFrame(); + m_synchronizing.recordFrame(); + m_synchronizing.endFrame(); +} + +void QQmlPreviewHandler::beforeRendering() +{ + m_rendering.beginFrame(); +} + +void QQmlPreviewHandler::frameSwapped() +{ + m_rendering.recordFrame(); +} + +void QQmlPreviewHandler::FrameTime::beginFrame() +{ + timer.start(); +} + +void QQmlPreviewHandler::FrameTime::recordFrame() +{ + elapsed = timer.elapsed(); +} + +void QQmlPreviewHandler::FrameTime::endFrame() +{ + if (elapsed < min) + min = static_cast<quint16>(qMax(0ll, elapsed)); + if (elapsed > max) + max = static_cast<quint16>(qMin(qint64(std::numeric_limits<quint16>::max()), elapsed)); + total = static_cast<quint16>(qBound(0ll, qint64(std::numeric_limits<quint16>::max()), + elapsed + total)); + ++number; + elapsed = -1; +} + +void QQmlPreviewHandler::FrameTime::reset() +{ + min = std::numeric_limits<quint16>::max(); + max = 0; + total = 0; + number = 0; +} + +void QQmlPreviewHandler::fpsTimerHit() +{ + const FpsInfo info = { + m_synchronizing.number, + m_synchronizing.min, + m_synchronizing.max, + m_synchronizing.total, + + m_rendering.number, + m_rendering.min, + m_rendering.max, + m_rendering.total + }; + + emit fps(info); + + m_rendering.reset(); + m_synchronizing.reset(); +} + +void QQmlPreviewHandler::tryCreateObject() +{ + if (!m_supportsMultipleWindows) + closeAllWindows(); + QObject *object = m_component->create(); + m_createdObjects.append(object); + showObject(object); +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h new file mode 100644 index 0000000000..21ea672580 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QML preview debug service. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPREVIEWHANDLER_H +#define QQMLPREVIEWHANDLER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qqmlpreviewposition.h" + +#include <QtCore/qobject.h> +#include <QtCore/qvector.h> +#include <QtCore/qrect.h> +#include <QtCore/qpointer.h> +#include <QtCore/qelapsedtimer.h> +#include <QtQml/qqmlengine.h> + +QT_BEGIN_NAMESPACE + +class QQmlEngine; +class QQuickItem; +class QQmlPreviewUrlInterceptor; +class QQuickWindow; +class QTranslator; + +class QQmlPreviewHandler : public QObject +{ + Q_OBJECT +public: + explicit QQmlPreviewHandler(QObject *parent = nullptr); + ~QQmlPreviewHandler(); + + void addEngine(QQmlEngine *engine); + void removeEngine(QQmlEngine *engine); + + void loadUrl(const QUrl &url); + void rerun(); + void zoom(qreal newFactor); + void language(const QUrl &context, const QLocale &locale); + + void clear(); + + struct FpsInfo { + quint16 numSyncs; + quint16 minSync; + quint16 maxSync; + quint16 totalSync; + + quint16 numRenders; + quint16 minRender; + quint16 maxRender; + quint16 totalRender; + }; + +signals: + void error(const QString &message); + void fps(const FpsInfo &info); + +protected: + bool eventFilter(QObject *obj, QEvent *event); +private: + void tryCreateObject(); + void showObject(QObject *object); + void setCurrentWindow(QQuickWindow *window); + + void beforeSynchronizing(); + void afterSynchronizing(); + void beforeRendering(); + void frameSwapped(); + + void fpsTimerHit(); + void removeTranslators(); + + QScopedPointer<QQuickItem> m_dummyItem; + QList<QQmlEngine *> m_engines; + QVector<QPointer<QObject>> m_createdObjects; + QScopedPointer<QQmlComponent> m_component; + QPointer<QQuickWindow> m_currentWindow; + bool m_supportsMultipleWindows; + QQmlPreviewPosition m_lastPosition; + + QTimer m_fpsTimer; + + struct FrameTime { + void beginFrame(); + void recordFrame(); + void endFrame(); + void reset(); + + QElapsedTimer timer; + qint64 elapsed = -1; + quint16 min = std::numeric_limits<quint16>::max(); + quint16 max = 0; + quint16 total = 0; + quint16 number = 0; + }; + + FrameTime m_rendering; + FrameTime m_synchronizing; + + QScopedPointer<QTranslator> m_qtTranslator; + QScopedPointer<QTranslator> m_qmlTranslator; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlPreviewHandler::FpsInfo) + +#endif // QQMLPREVIEWHANDLER_H diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp new file mode 100644 index 0000000000..3edcbac0a9 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QML preview debug service. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpreviewposition.h" + +#include <QtGui/qwindow.h> +#include <QtGui/qscreen.h> +#include <QtGui/qguiapplication.h> + +QT_BEGIN_NAMESPACE + +static const QSize availableScreenSize(const QPoint &point) +{ + if (const QScreen *screen = QGuiApplication::screenAt(point)) + return screen->availableGeometry().size(); + return QSize(); +} + +QQmlPreviewPosition::QQmlPreviewPosition() + : m_settings("QtProject", "QtQmlPreview") +{ + m_savePositionTimer.setSingleShot(true); + m_savePositionTimer.setInterval(500); + QObject::connect(&m_savePositionTimer, &QTimer::timeout, [this]() { + saveWindowPosition(); + }); +} + +void QQmlPreviewPosition::setPosition(const QPoint &point) +{ + m_hasPosition = true; + m_lastWindowPosition = point; + m_savePositionTimer.start(); +} + +void QQmlPreviewPosition::saveWindowPosition() +{ + if (m_hasPosition) { + if (!m_settingsKey.isNull()) + m_settings.setValue(m_settingsKey, m_lastWindowPosition); + + m_settings.setValue(QLatin1String("global_lastpostion"), m_lastWindowPosition); + } +} + +void QQmlPreviewPosition::loadWindowPositionSettings(const QUrl &url) +{ + m_settingsKey = url.toString(QUrl::PreferLocalFile) + QLatin1String("_lastpostion"); + + if (m_settings.contains(m_settingsKey)) { + m_hasPosition = true; + m_lastWindowPosition = m_settings.value(m_settingsKey).toPoint(); + } +} + +void QQmlPreviewPosition::initLastSavedWindowPosition(QWindow *window) +{ + if (m_positionedWindows.contains(window)) + return; + if (!m_hasPosition) { + // in case there was nothing saved, we do not want to set anything + if (!m_settings.contains(QLatin1String("global_lastpostion"))) + return; + m_lastWindowPosition = m_settings.value(QLatin1String("global_lastpostion")).toPoint(); + } + if (QGuiApplication::screenAt(m_lastWindowPosition)) + window->setFramePosition(m_lastWindowPosition); + + m_positionedWindows.append(window); +} + +const QSize QQmlPreviewPosition::currentScreenSize(QWindow *window) +{ + return availableScreenSize(window->position()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/shared/qqmldebugserverconnection.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h index 3fac15acb2..3d4ca9dc67 100644 --- a/src/plugins/qmltooling/shared/qqmldebugserverconnection.h +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h @@ -1,9 +1,9 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QML preview debug service. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -37,11 +37,8 @@ ** ****************************************************************************/ -#ifndef QQMLDEBUGSERVERCONNECTION_H -#define QQMLDEBUGSERVERCONNECTION_H - -#include <private/qtqmlglobal_p.h> -#include <QtCore/qobject.h> +#ifndef QQMLPREVIEWPOSITION_H +#define QQMLPREVIEWPOSITION_H // // W A R N I N G @@ -54,35 +51,37 @@ // We mean it. // +#include <QtCore/qvector.h> +#include <QtCore/qpoint.h> +#include <QtCore/qurl.h> +#include <QtCore/qtimer.h> +#include <QtCore/qsettings.h> + QT_BEGIN_NAMESPACE +class QWindow; -class QQmlDebugServer; -class QQmlDebugServerConnection : public QObject +class QQmlPreviewPosition { - Q_OBJECT public: - QQmlDebugServerConnection(QObject *parent = 0) : QObject(parent) {} + QQmlPreviewPosition(); - virtual void setServer(QQmlDebugServer *server) = 0; - virtual bool setPortRange(int portFrom, int portTo, bool block, const QString &hostaddress) = 0; - virtual bool setFileName(const QString &fileName, bool block) = 0; - virtual bool isConnected() const = 0; - virtual void disconnect() = 0; - virtual void waitForConnection() = 0; - virtual void flush() = 0; -}; + void setPosition(const QPoint &point); + void saveWindowPosition(); + void loadWindowPositionSettings(const QUrl &url); + void initLastSavedWindowPosition(QWindow *window); + static const QSize currentScreenSize(QWindow *window); -class QQmlDebugServerConnectionFactory : public QObject -{ - Q_OBJECT -public: - virtual QQmlDebugServerConnection *create(const QString &key) = 0; +private: + bool m_hasPosition = false; + QPoint m_lastWindowPosition; + QSettings m_settings; + QString m_settingsKey; + QTimer m_savePositionTimer; + QVector<QWindow *> m_positionedWindows; }; -#define QQmlDebugServerConnectionFactory_iid "org.qt-project.Qt.QQmlDebugServerConnectionFactory" -Q_DECLARE_INTERFACE(QQmlDebugServerConnectionFactory, QQmlDebugServerConnectionFactory_iid) QT_END_NAMESPACE -#endif // QQMLDEBUGSERVERCONNECTION_H +#endif // QQMLPREVIEWPOSITION_H diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp new file mode 100644 index 0000000000..2e2224df47 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QML preview debug service. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpreviewservice.h" + +#include <QtCore/qpointer.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcomponent.h> +#include <QtQuick/qquickwindow.h> +#include <QtQuick/qquickitem.h> +#include <QtGui/qguiapplication.h> + +#include <private/qquickpixmapcache_p.h> +#include <private/qqmldebugconnector_p.h> +#include <private/qversionedpacket_p.h> + +QT_BEGIN_NAMESPACE + +const QString QQmlPreviewServiceImpl::s_key = QStringLiteral("QmlPreview"); +using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>; + +QQmlPreviewServiceImpl::QQmlPreviewServiceImpl(QObject *parent) : + QQmlDebugService(s_key, 1.0f, parent) +{ + m_loader.reset(new QQmlPreviewFileLoader(this)); + connect(this, &QQmlPreviewServiceImpl::load, + m_loader.data(), &QQmlPreviewFileLoader::whitelist, Qt::DirectConnection); + connect(this, &QQmlPreviewServiceImpl::load, &m_handler, &QQmlPreviewHandler::loadUrl); + connect(this, &QQmlPreviewServiceImpl::rerun, &m_handler, &QQmlPreviewHandler::rerun); + connect(this, &QQmlPreviewServiceImpl::zoom, &m_handler, &QQmlPreviewHandler::zoom); + connect(this, &QQmlPreviewServiceImpl::language, &m_handler, &QQmlPreviewHandler::language); + connect(&m_handler, &QQmlPreviewHandler::error, this, &QQmlPreviewServiceImpl::forwardError, + Qt::DirectConnection); + connect(&m_handler, &QQmlPreviewHandler::fps, this, &QQmlPreviewServiceImpl::forwardFps, + Qt::DirectConnection); +} + +QQmlPreviewServiceImpl::~QQmlPreviewServiceImpl() +{ +} + +void QQmlPreviewServiceImpl::messageReceived(const QByteArray &data) +{ + QQmlDebugPacket packet(data); + qint8 command; + + packet >> command; + switch (command) { + case File: { + QString path; + QByteArray contents; + packet >> path >> contents; + emit file(path, contents); + + // Replace the whole scene with the first file successfully loaded over the debug + // connection. This is an OK approximation of the root component, and if the client wants + // something specific, it will send an explicit Load anyway. + if (m_currentUrl.isEmpty() && path.endsWith(".qml")) { + if (path.startsWith(':')) + m_currentUrl = QUrl("qrc" + path); + else + m_currentUrl = QUrl::fromLocalFile(path); + emit load(m_currentUrl); + } + break; + } + case Directory: { + QString path; + QStringList entries; + packet >> path >> entries; + emit directory(path, entries); + break; + } + case Load: { + QUrl url; + packet >> url; + if (url.isEmpty()) + url = m_currentUrl; + else + m_currentUrl = url; + emit load(url); + break; + } + case Error: { + QString file; + packet >> file; + emit error(file); + break; + } + case Rerun: + emit rerun(); + break; + case ClearCache: + emit clearCache(); + break; + case Zoom: { + float factor; + packet >> factor; + emit zoom(static_cast<qreal>(factor)); + break; + } + case Language: { + QUrl context; + QString locale; + packet >> context >> locale; + emit language(context.isEmpty() ? m_currentUrl : context, + locale.isEmpty() ? QLocale() : QLocale(locale)); + break; + } + default: + forwardError(QString::fromLatin1("Invalid command: %1").arg(command)); + break; + } +} + +void QQmlPreviewServiceImpl::engineAboutToBeAdded(QJSEngine *engine) +{ + if (QQmlEngine *qmlEngine = qobject_cast<QQmlEngine *>(engine)) + m_handler.addEngine(qmlEngine); + emit attachedToEngine(engine); +} + +void QQmlPreviewServiceImpl::engineAboutToBeRemoved(QJSEngine *engine) +{ + if (QQmlEngine *qmlEngine = qobject_cast<QQmlEngine *>(engine)) + m_handler.removeEngine(qmlEngine); + emit detachedFromEngine(engine); +} + +void QQmlPreviewServiceImpl::stateChanged(QQmlDebugService::State state) +{ + m_fileEngine.reset(state == Enabled ? new QQmlPreviewFileEngineHandler(m_loader.data()) + : nullptr); +} + +void QQmlPreviewServiceImpl::forwardRequest(const QString &file) +{ + QQmlDebugPacket packet; + packet << static_cast<qint8>(Request) << file; + emit messageToClient(name(), packet.data()); +} + +void QQmlPreviewServiceImpl::forwardError(const QString &error) +{ + QQmlDebugPacket packet; + packet << static_cast<qint8>(Error) << error; + emit messageToClient(name(), packet.data()); +} + +void QQmlPreviewServiceImpl::forwardFps(const QQmlPreviewHandler::FpsInfo &frames) +{ + QQmlDebugPacket packet; + packet << static_cast<qint8>(Fps) + << frames.numSyncs << frames.minSync << frames.maxSync << frames.totalSync + << frames.numRenders << frames.minRender << frames.maxRender << frames.totalRender; + emit messageToClient(name(), packet.data()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h new file mode 100644 index 0000000000..7bdc87ec59 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QML preview debug service. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPREVIEWSERVICE_H +#define QQMLPREVIEWSERVICE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qqmlpreviewhandler.h" +#include "qqmlpreviewfileengine.h" +#include <private/qqmldebugserviceinterfaces_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlPreviewFileEngineHandler; +class QQmlPreviewHandler; +class QQmlPreviewServiceImpl : public QQmlDebugService +{ + Q_OBJECT + +public: + enum Command { + File, + Load, + Request, + Error, + Rerun, + Directory, + ClearCache, + Zoom, + Fps, + Language + }; + + static const QString s_key; + + QQmlPreviewServiceImpl(QObject *parent = nullptr); + virtual ~QQmlPreviewServiceImpl(); + + void messageReceived(const QByteArray &message) override; + void engineAboutToBeAdded(QJSEngine *engine) override; + void engineAboutToBeRemoved(QJSEngine *engine) override; + void stateChanged(State state) override; + + void forwardRequest(const QString &file); + void forwardError(const QString &error); + void forwardFps(const QQmlPreviewHandler::FpsInfo &frames); + +signals: + void error(const QString &file); + void file(const QString &file, const QByteArray &contents); + void directory(const QString &file, const QStringList &entries); + void load(const QUrl &url); + void rerun(); + void clearCache(); + void zoom(qreal factor); + void language(const QUrl &context, const QLocale &locale); + +private: + QScopedPointer<QQmlPreviewFileEngineHandler> m_fileEngine; + QScopedPointer<QQmlPreviewFileLoader> m_loader; + QQmlPreviewHandler m_handler; + QUrl m_currentUrl; +}; + +QT_END_NAMESPACE + +#endif // QQMLPREVIEWSERVICE_H diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.json b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.json new file mode 100644 index 0000000000..d7e1ef1f10 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.json @@ -0,0 +1,3 @@ +{ + "Keys" : [ "QmlPreview" ] +} diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp new file mode 100644 index 0000000000..f0aa3226c8 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QML preview debug service. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpreviewservicefactory.h" +#include "qqmlpreviewservice.h" + +QT_BEGIN_NAMESPACE + +QQmlDebugService *QQmlPreviewServiceFactory::create(const QString &key) +{ + return key == QQmlPreviewServiceImpl::s_key ? new QQmlPreviewServiceImpl(this) : nullptr; +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/shared/qqmldebugpacket.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.h index f1c21e0a2b..0ceadf24f5 100644 --- a/src/plugins/qmltooling/shared/qqmldebugpacket.h +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.h @@ -1,9 +1,9 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QML preview debug service. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -37,12 +37,8 @@ ** ****************************************************************************/ -#ifndef QQMLDEBUGPACKET_P_H -#define QQMLDEBUGPACKET_P_H - -#include <QtCore/qbuffer.h> -#include <QtQml/private/qqmldebugconnector_p.h> -#include <QtPacketProtocol/private/qpacket_p.h> +#ifndef QQMLPREVIEWSERVCIEFACTORY_H +#define QQMLPREVIEWSERVCIEFACTORY_H // // W A R N I N G @@ -55,16 +51,19 @@ // We mean it. // +#include <private/qqmldebugservicefactory_p.h> + QT_BEGIN_NAMESPACE -// QPacket with a fixed data stream version, centrally set by QQmlDebugServer -class QQmlDebugPacket : public QPacket +class QQmlPreviewServiceFactory : public QQmlDebugServiceFactory { + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlDebugServiceFactory_iid FILE "qqmlpreviewservice.json") + public: - QQmlDebugPacket() : QPacket(QQmlDebugConnector::dataStreamVersion()) {} - QQmlDebugPacket(const QByteArray &ba) : QPacket(QQmlDebugConnector::dataStreamVersion(), ba) {} + QQmlDebugService *create(const QString &key) override; }; QT_END_NAMESPACE -#endif // QQMLDEBUGPACKET_P_H +#endif // QQMLPREVIEWSERVCIEFACTORY_H diff --git a/src/plugins/qmltooling/qmldbg_profiler/qmldbg_profiler.pro b/src/plugins/qmltooling/qmldbg_profiler/qmldbg_profiler.pro index 4629a7b81e..ac874b079e 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qmldbg_profiler.pro +++ b/src/plugins/qmltooling/qmldbg_profiler/qmldbg_profiler.pro @@ -9,17 +9,12 @@ SOURCES += \ $$PWD/qv4profileradapter.cpp HEADERS += \ - $$PWD/../shared/qqmlconfigurabledebugservice.h \ - $$PWD/../shared/qqmldebugpacket.h \ $$PWD/qqmlenginecontrolservice.h \ $$PWD/qqmlprofileradapter.h \ $$PWD/qqmlprofilerservice.h \ $$PWD/qqmlprofilerservicefactory.h \ $$PWD/qv4profileradapter.h -INCLUDEPATH += $$PWD \ - $$PWD/../shared - OTHER_FILES += \ $$PWD/qqmlprofilerservice.json diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp index 9918a95116..f76add448f 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.cpp @@ -38,11 +38,14 @@ ****************************************************************************/ #include "qqmlenginecontrolservice.h" -#include "qqmldebugpacket.h" +#include <private/qqmldebugconnector_p.h> +#include <private/qversionedpacket_p.h> #include <QJSEngine> QT_BEGIN_NAMESPACE +using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>; + QQmlEngineControlServiceImpl::QQmlEngineControlServiceImpl(QObject *parent) : QQmlEngineControlService(1, parent) { @@ -53,8 +56,8 @@ void QQmlEngineControlServiceImpl::messageReceived(const QByteArray &message) { QMutexLocker lock(&dataMutex); QQmlDebugPacket d(message); - int command; - int engineId; + qint32 command; + qint32 engineId; d >> command >> engineId; QJSEngine *engine = qobject_cast<QJSEngine *>(objectForId(engineId)); if (command == StartWaitingEngine && startingEngines.contains(engine)) { @@ -112,10 +115,11 @@ void QQmlEngineControlServiceImpl::engineRemoved(QJSEngine *engine) } } -void QQmlEngineControlServiceImpl::sendMessage(QQmlEngineControlServiceImpl::MessageType type, QJSEngine *engine) +void QQmlEngineControlServiceImpl::sendMessage(QQmlEngineControlServiceImpl::MessageType type, + QJSEngine *engine) { QQmlDebugPacket d; - d << int(type) << idForObject(engine); + d << static_cast<qint32>(type) << idForObject(engine); emit messageToClient(name(), d.data()); } diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.h index 6392944519..3c5daa0f4f 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlenginecontrolservice.h @@ -81,15 +81,15 @@ protected: QList<QJSEngine *> stoppingEngines; bool blockingMode; - void messageReceived(const QByteArray &) Q_DECL_OVERRIDE; - void engineAboutToBeAdded(QJSEngine *) Q_DECL_OVERRIDE; - void engineAboutToBeRemoved(QJSEngine *) Q_DECL_OVERRIDE; - void engineAdded(QJSEngine *) Q_DECL_OVERRIDE; - void engineRemoved(QJSEngine *) Q_DECL_OVERRIDE; + void messageReceived(const QByteArray &) override; + void engineAboutToBeAdded(QJSEngine *) override; + void engineAboutToBeRemoved(QJSEngine *) override; + void engineAdded(QJSEngine *) override; + void engineRemoved(QJSEngine *) override; void sendMessage(MessageType type, QJSEngine *engine); - void stateChanged(State) Q_DECL_OVERRIDE; + void stateChanged(State) override; }; QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp index 510c745d4e..a688e98b3f 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp @@ -38,38 +38,49 @@ ****************************************************************************/ #include "qqmlprofileradapter.h" -#include "qqmldebugpacket.h" +#include "qqmlprofilerservice.h" #include <private/qqmldebugserviceinterfaces_p.h> QT_BEGIN_NAMESPACE -QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine) : - next(0) +QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine) { - setService(service); engine->profiler = new QQmlProfiler; + init(service, engine->profiler); +} + +QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlTypeLoader *loader) +{ + QQmlProfiler *profiler = new QQmlProfiler; + loader->setProfiler(profiler); + init(service, profiler); +} + +void QQmlProfilerAdapter::init(QQmlProfilerService *service, QQmlProfiler *profiler) +{ + next = 0; + setService(service); connect(this, &QQmlProfilerAdapter::profilingEnabled, - engine->profiler, &QQmlProfiler::startProfiling); + profiler, &QQmlProfiler::startProfiling); connect(this, &QQmlAbstractProfilerAdapter::profilingEnabledWhileWaiting, - engine->profiler, &QQmlProfiler::startProfiling, Qt::DirectConnection); + profiler, &QQmlProfiler::startProfiling, Qt::DirectConnection); connect(this, &QQmlAbstractProfilerAdapter::profilingDisabled, - engine->profiler, &QQmlProfiler::stopProfiling); + profiler, &QQmlProfiler::stopProfiling); connect(this, &QQmlAbstractProfilerAdapter::profilingDisabledWhileWaiting, - engine->profiler, &QQmlProfiler::stopProfiling, Qt::DirectConnection); + profiler, &QQmlProfiler::stopProfiling, Qt::DirectConnection); connect(this, &QQmlAbstractProfilerAdapter::dataRequested, - engine->profiler, &QQmlProfiler::reportData); + profiler, &QQmlProfiler::reportData); connect(this, &QQmlAbstractProfilerAdapter::referenceTimeKnown, - engine->profiler, &QQmlProfiler::setTimer); - connect(engine->profiler, &QQmlProfiler::dataReady, + profiler, &QQmlProfiler::setTimer); + connect(profiler, &QQmlProfiler::dataReady, this, &QQmlProfilerAdapter::receiveData); } // convert to QByteArrays that can be sent to the debug client static void qQmlProfilerDataToByteArrays(const QQmlProfilerData &d, QQmlProfiler::LocationHash &locations, - QList<QByteArray> &messages, - bool trackLocations) + QList<QByteArray> &messages) { QQmlDebugPacket ds; Q_ASSERT_X((d.messageType & (1 << 31)) == 0, Q_FUNC_INFO, @@ -84,7 +95,7 @@ static void qQmlProfilerDataToByteArrays(const QQmlProfilerData &d, if (decodedMessageType == QQmlProfilerDefinitions::RangeEnd || decodedMessageType == QQmlProfilerDefinitions::RangeStart) { ds << d.time << decodedMessageType << static_cast<quint32>(d.detailType); - if (trackLocations && d.locationId != 0) + if (d.locationId != 0) ds << static_cast<qint64>(d.locationId); } else { auto i = locations.find(d.locationId); @@ -95,8 +106,7 @@ static void qQmlProfilerDataToByteArrays(const QQmlProfilerData &d, << 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); + ds << static_cast<qint64>(d.locationId); messages.append(ds.squeezedData()); ds.clear(); ds << d.time << int(QQmlProfilerDefinitions::RangeData) @@ -104,10 +114,8 @@ static void qQmlProfilerDataToByteArrays(const QQmlProfilerData &d, << (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. - } + 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; @@ -118,14 +126,13 @@ static void qQmlProfilerDataToByteArrays(const QQmlProfilerData &d, } } -qint64 QQmlProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages, - bool trackLocations) +qint64 QQmlProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages) { 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, trackLocations); + qQmlProfilerDataToByteArrays(nextData, locations, messages); ++next; } diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h index 1fee5c389f..12544a19c2 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h @@ -60,13 +60,14 @@ class QQmlProfilerAdapter : public QQmlAbstractProfilerAdapter { Q_OBJECT public: QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine); - qint64 sendMessages(qint64 until, QList<QByteArray> &messages, - bool trackLocations) Q_DECL_OVERRIDE; + QQmlProfilerAdapter(QQmlProfilerService *service, QQmlTypeLoader *loader); + qint64 sendMessages(qint64 until, QList<QByteArray> &messages) override; void receiveData(const QVector<QQmlProfilerData> &new_data, const QQmlProfiler::LocationHash &locations); private: + void init(QQmlProfilerService *service, QQmlProfiler *profiler); QVector<QQmlProfilerData> data; QQmlProfiler::LocationHash locations; int next; diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp index edeb364f60..462401a093 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp @@ -41,7 +41,6 @@ #include "qv4profileradapter.h" #include "qqmlprofileradapter.h" #include "qqmlprofilerservicefactory.h" -#include "qqmldebugpacket.h" #include <private/qjsengine_p.h> #include <private/qqmldebugpluginmanager_p.h> @@ -59,7 +58,7 @@ Q_QML_DEBUG_PLUGIN_LOADER(QQmlAbstractProfilerAdapter) QQmlProfilerServiceImpl::QQmlProfilerServiceImpl(QObject *parent) : QQmlConfigurableDebugService<QQmlProfilerService>(1, parent), - m_waitingForStop(false), m_useMessageTypes(false) + m_waitingForStop(false), m_globalEnabled(false), m_globalFeatures(0) { m_timer.start(); QQmlAbstractProfilerAdapter *quickAdapter = @@ -119,11 +118,14 @@ void QQmlProfilerServiceImpl::engineAboutToBeAdded(QJSEngine *engine) QMutexLocker lock(&m_configMutex); if (QQmlEngine *qmlEngine = qobject_cast<QQmlEngine *>(engine)) { - QQmlProfilerAdapter *qmlAdapter = - new QQmlProfilerAdapter(this, QQmlEnginePrivate::get(qmlEngine)); + QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(qmlEngine); + QQmlProfilerAdapter *qmlAdapter = new QQmlProfilerAdapter(this, enginePrivate); addEngineProfiler(qmlAdapter, engine); + QQmlProfilerAdapter *compileAdapter + = new QQmlProfilerAdapter(this, &(enginePrivate->typeLoader)); + addEngineProfiler(compileAdapter, engine); } - QV4ProfilerAdapter *v4Adapter = new QV4ProfilerAdapter(this, QV8Engine::getV4(engine->handle())); + QV4ProfilerAdapter *v4Adapter = new QV4ProfilerAdapter(this, engine->handle()); addEngineProfiler(v4Adapter, engine); QQmlConfigurableDebugService<QQmlProfilerService>::engineAboutToBeAdded(engine); } @@ -134,6 +136,10 @@ void QQmlProfilerServiceImpl::engineAdded(QJSEngine *engine) "QML profilers have to be added from the engine thread"); QMutexLocker lock(&m_configMutex); + + if (m_globalEnabled) + startProfiling(engine, m_globalFeatures); + const auto range = qAsConst(m_engineProfilers).equal_range(engine); for (auto it = range.first; it != range.second; ++it) (*it)->stopWaiting(); @@ -237,9 +243,9 @@ void QQmlProfilerServiceImpl::startProfiling(QJSEngine *engine, quint64 features QQmlDebugPacket d; - d << m_timer.nsecsElapsed() << (int)Event << (int)StartTrace; + d << m_timer.nsecsElapsed() << static_cast<qint32>(Event) << static_cast<qint32>(StartTrace); bool startedAny = false; - if (engine != 0) { + if (engine != nullptr) { const auto range = qAsConst(m_engineProfilers).equal_range(engine); for (auto it = range.first; it != range.second; ++it) { QQmlAbstractProfilerAdapter *profiler = *it; @@ -251,6 +257,9 @@ void QQmlProfilerServiceImpl::startProfiling(QJSEngine *engine, quint64 features if (startedAny) d << idForObject(engine); } else { + m_globalEnabled = true; + m_globalFeatures = features; + QSet<QJSEngine *> engines; for (QMultiHash<QJSEngine *, QQmlAbstractProfilerAdapter *>::iterator i(m_engineProfilers.begin()); i != m_engineProfilers.end(); ++i) { @@ -271,9 +280,8 @@ void QQmlProfilerServiceImpl::startProfiling(QJSEngine *engine, quint64 features } emit startFlushTimer(); + emit messageToClient(name(), d.data()); } - - emit messageToClient(name(), d.data()); } /*! @@ -289,14 +297,18 @@ void QQmlProfilerServiceImpl::stopProfiling(QJSEngine *engine) QList<QQmlAbstractProfilerAdapter *> stopping; QList<QQmlAbstractProfilerAdapter *> reporting; + if (engine == nullptr) + m_globalEnabled = false; + bool stillRunning = false; for (QMultiHash<QJSEngine *, QQmlAbstractProfilerAdapter *>::iterator i(m_engineProfilers.begin()); i != m_engineProfilers.end(); ++i) { if (i.value()->isRunning()) { - if (engine == 0 || i.key() == engine) { - m_startTimes.insert(-1, i.value()); + m_startTimes.insert(-1, i.value()); + if (engine == nullptr || i.key() == engine) { stopping << i.value(); } else { + reporting << i.value(); stillRunning = true; } } @@ -320,7 +332,7 @@ void QQmlProfilerServiceImpl::stopProfiling(QJSEngine *engine) m_waitingForStop = true; for (QQmlAbstractProfilerAdapter *profiler : qAsConst(reporting)) - profiler->reportData(m_useMessageTypes); + profiler->reportData(); for (QQmlAbstractProfilerAdapter *profiler : qAsConst(stopping)) profiler->stopProfiling(); @@ -335,7 +347,8 @@ void QQmlProfilerServiceImpl::sendMessages() QQmlDebugPacket traceEnd; if (m_waitingForStop) { - traceEnd << m_timer.nsecsElapsed() << (int)Event << (int)EndTrace; + traceEnd << m_timer.nsecsElapsed() << static_cast<qint32>(Event) + << static_cast<qint32>(EndTrace); QSet<QJSEngine *> seen; for (QQmlAbstractProfilerAdapter *profiler : qAsConst(m_startTimes)) { @@ -354,8 +367,7 @@ 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_useMessageTypes); + m_startTimes.begin().key(), messages); if (next != -1) m_startTimes.insert(next, first); @@ -365,25 +377,32 @@ void QQmlProfilerServiceImpl::sendMessages() } } + bool stillRunning = false; + for (const QQmlAbstractProfilerAdapter *profiler : qAsConst(m_engineProfilers)) { + if (profiler->isRunning()) { + stillRunning = true; + break; + } + } + if (m_waitingForStop) { - //indicate completion + // EndTrace can be sent multiple times, as it's engine specific. messages << traceEnd.data(); - QQmlDebugPacket ds; - ds << (qint64)-1 << (int)Complete; - messages << ds.data(); - m_waitingForStop = false; + if (!stillRunning) { + // Complete is only sent once, when no engines are running anymore. + QQmlDebugPacket ds; + ds << static_cast<qint64>(-1) << static_cast<qint32>(Complete); + messages << ds.data(); + m_waitingForStop = false; + } } emit messagesToClient(name(), messages); // Restart flushing if any profilers are still running - for (const QQmlAbstractProfilerAdapter *profiler : qAsConst(m_engineProfilers)) { - if (profiler->isRunning()) { - emit startFlushTimer(); - break; - } - } + if (stillRunning) + emit startFlushTimer(); } void QQmlProfilerServiceImpl::stateAboutToBeChanged(QQmlDebugService::State newState) @@ -411,7 +430,7 @@ void QQmlProfilerServiceImpl::messageReceived(const QByteArray &message) int engineId = -1; quint64 features = std::numeric_limits<quint64>::max(); bool enabled; - uint flushInterval = 0; + quint32 flushInterval = 0; stream >> enabled; if (!stream.atEnd()) stream >> engineId; @@ -419,7 +438,9 @@ void QQmlProfilerServiceImpl::messageReceived(const QByteArray &message) stream >> features; if (!stream.atEnd()) { stream >> flushInterval; - m_flushTimer.setInterval(flushInterval); + m_flushTimer.setInterval( + static_cast<int>(qMin(flushInterval, + static_cast<quint32>(std::numeric_limits<int>::max())))); auto timerStart = static_cast<void(QTimer::*)()>(&QTimer::start); if (flushInterval > 0) { connect(&m_flushTimer, &QTimer::timeout, this, &QQmlProfilerServiceImpl::flush); @@ -432,13 +453,15 @@ void QQmlProfilerServiceImpl::messageReceived(const QByteArray &message) &m_flushTimer, &QTimer::stop); } } + + bool useMessageTypes = false; if (!stream.atEnd()) - stream >> m_useMessageTypes; + stream >> useMessageTypes; // If engineId == -1 objectForId() and then the cast will return 0. - if (enabled) + if (enabled && useMessageTypes) // If the client doesn't support message types don't profile. startProfiling(qobject_cast<QJSEngine *>(objectForId(engineId)), features); - else + else if (!enabled) // On stopProfiling the client doesn't repeat useMessageTypes. stopProfiling(qobject_cast<QJSEngine *>(objectForId(engineId))); stopWaiting(); @@ -464,7 +487,7 @@ void QQmlProfilerServiceImpl::flush() } for (QQmlAbstractProfilerAdapter *profiler : qAsConst(reporting)) - profiler->reportData(m_useMessageTypes); + profiler->reportData(); } QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h index bbfc32b681..3791ab29ae 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h @@ -51,11 +51,13 @@ // We mean it. // -#include "qqmlconfigurabledebugservice.h" +#include <private/qqmlconfigurabledebugservice_p.h> #include <private/qqmldebugserviceinterfaces_p.h> #include <private/qqmlprofilerdefinitions_p.h> #include <private/qqmlabstractprofileradapter_p.h> #include <private/qqmlboundsignal_p.h> +#include <private/qqmldebugconnector_p.h> +#include <private/qversionedpacket_p.h> #include <QtCore/qelapsedtimer.h> #include <QtCore/qmetaobject.h> @@ -70,6 +72,7 @@ QT_BEGIN_NAMESPACE class QUrl; +using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>; class QQmlProfilerServiceImpl : public QQmlConfigurableDebugService<QQmlProfilerService>, @@ -78,30 +81,30 @@ class QQmlProfilerServiceImpl : Q_OBJECT public: - void engineAboutToBeAdded(QJSEngine *engine) Q_DECL_OVERRIDE; - void engineAboutToBeRemoved(QJSEngine *engine) Q_DECL_OVERRIDE; - void engineAdded(QJSEngine *engine) Q_DECL_OVERRIDE; - void engineRemoved(QJSEngine *engine) Q_DECL_OVERRIDE; + void engineAboutToBeAdded(QJSEngine *engine) override; + void engineAboutToBeRemoved(QJSEngine *engine) override; + void engineAdded(QJSEngine *engine) override; + void engineRemoved(QJSEngine *engine) override; - void addGlobalProfiler(QQmlAbstractProfilerAdapter *profiler) Q_DECL_OVERRIDE; - void removeGlobalProfiler(QQmlAbstractProfilerAdapter *profiler) Q_DECL_OVERRIDE; + void addGlobalProfiler(QQmlAbstractProfilerAdapter *profiler) override; + void removeGlobalProfiler(QQmlAbstractProfilerAdapter *profiler) override; void startProfiling(QJSEngine *engine, - quint64 features = std::numeric_limits<quint64>::max()) Q_DECL_OVERRIDE; - void stopProfiling(QJSEngine *engine) Q_DECL_OVERRIDE; + quint64 features = std::numeric_limits<quint64>::max()) override; + void stopProfiling(QJSEngine *engine) override; QQmlProfilerServiceImpl(QObject *parent = 0); - ~QQmlProfilerServiceImpl() Q_DECL_OVERRIDE; + ~QQmlProfilerServiceImpl() override; - void dataReady(QQmlAbstractProfilerAdapter *profiler) Q_DECL_OVERRIDE; + void dataReady(QQmlAbstractProfilerAdapter *profiler) override; signals: void startFlushTimer(); void stopFlushTimer(); protected: - virtual void stateAboutToBeChanged(State state) Q_DECL_OVERRIDE; - virtual void messageReceived(const QByteArray &) Q_DECL_OVERRIDE; + void stateAboutToBeChanged(State state) override; + void messageReceived(const QByteArray &) override; private: friend class QQmlProfilerServiceFactory; @@ -114,7 +117,9 @@ private: QElapsedTimer m_timer; QTimer m_flushTimer; bool m_waitingForStop; - bool m_useMessageTypes; + + bool m_globalEnabled; + quint64 m_globalFeatures; QList<QQmlAbstractProfilerAdapter *> m_globalProfilers; QMultiHash<QJSEngine *, QQmlAbstractProfilerAdapter *> m_engineProfilers; diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.cpp index 0cd3e0b4ab..81a1a35d18 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservicefactory.cpp @@ -51,7 +51,7 @@ QQmlDebugService *QQmlProfilerServiceFactory::create(const QString &key) if (key == QQmlEngineControlServiceImpl::s_key) return new QQmlEngineControlServiceImpl(this); - return 0; + return nullptr; } QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp index f1ac8ef998..12c36f3dd6 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp @@ -87,14 +87,17 @@ qint64 QV4ProfilerAdapter::appendMemoryEvents(qint64 until, QList<QByteArray> &m qint64 QV4ProfilerAdapter::finalizeMessages(qint64 until, QList<QByteArray> &messages, qint64 callNext, QQmlDebugPacket &d) { + qint64 memoryNext = -1; + if (callNext == -1) { m_functionLocations.clear(); m_functionCallData.clear(); m_functionCallPos = 0; + memoryNext = appendMemoryEvents(until, messages, d); + } else { + memoryNext = appendMemoryEvents(qMin(callNext, until), messages, d); } - qint64 memoryNext = appendMemoryEvents(until, messages, d); - if (memoryNext == -1) { m_memoryData.clear(); m_memoryPos = 0; @@ -104,8 +107,7 @@ qint64 QV4ProfilerAdapter::finalizeMessages(qint64 until, QList<QByteArray> &mes return callNext == -1 ? memoryNext : qMin(callNext, memoryNext); } -qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages, - bool trackLocations) +qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages) { QQmlDebugPacket d; @@ -134,24 +136,17 @@ qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &message appendMemoryEvents(props.start, messages, d); auto location = m_functionLocations.find(props.id); - d << props.start << int(RangeStart) << int(Javascript); - if (trackLocations) - d << static_cast<qint64>(props.id); + d << props.start << int(RangeStart) << int(Javascript) << static_cast<qint64>(props.id); if (location != m_functionLocations.end()) { messages.push_back(d.squeezedData()); d.clear(); d << props.start << int(RangeLocation) << int(Javascript) << location->file << location->line - << location->column; - if (trackLocations) - d << static_cast<qint64>(props.id); + << location->column << static_cast<qint64>(props.id); messages.push_back(d.squeezedData()); d.clear(); - d << props.start << int(RangeData) << int(Javascript) << location->name; - - if (trackLocations) { - d << static_cast<qint64>(props.id); - m_functionLocations.erase(location); - } + d << props.start << int(RangeData) << int(Javascript) << location->name + << static_cast<qint64>(props.id); + m_functionLocations.erase(location); } messages.push_back(d.squeezedData()); d.clear(); diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h index 5d5b83f7ca..c4ca38d9b0 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h @@ -51,9 +51,10 @@ // We mean it. // +#include "qqmlprofilerservice.h" + #include <private/qv4profiling_p.h> #include <private/qqmlabstractprofileradapter_p.h> -#include "qqmldebugpacket.h" #include <QStack> #include <QList> @@ -67,8 +68,7 @@ class QV4ProfilerAdapter : public QQmlAbstractProfilerAdapter { public: QV4ProfilerAdapter(QQmlProfilerService *service, QV4::ExecutionEngine *engine); - virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages, - bool trackLocations) override; + virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages) override; void receiveData(const QV4::Profiling::FunctionLocationHash &, const QVector<QV4::Profiling::FunctionCallProperties> &, diff --git a/src/plugins/qmltooling/qmldbg_quickprofiler/qmldbg_quickprofiler.pro b/src/plugins/qmltooling/qmldbg_quickprofiler/qmldbg_quickprofiler.pro index 6ca0a184ca..f165917041 100644 --- a/src/plugins/qmltooling/qmldbg_quickprofiler/qmldbg_quickprofiler.pro +++ b/src/plugins/qmltooling/qmldbg_quickprofiler/qmldbg_quickprofiler.pro @@ -5,8 +5,6 @@ PLUGIN_TYPE = qmltooling PLUGIN_CLASS_NAME = QQuickProfilerAdapterFactory load(qt_plugin) -INCLUDEPATH += $$PWD/../shared - SOURCES += \ $$PWD/qquickprofileradapter.cpp \ $$PWD/qquickprofileradapterfactory.cpp @@ -14,7 +12,6 @@ SOURCES += \ HEADERS += \ $$PWD/qquickprofileradapter.h \ $$PWD/qquickprofileradapterfactory.h \ - $$PWD/../shared/qqmldebugpacket.h OTHER_FILES += \ qquickprofileradapter.json diff --git a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp index 35beb0ee0d..79a1c82411 100644 --- a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp @@ -38,13 +38,17 @@ ****************************************************************************/ #include "qquickprofileradapter.h" -#include "qqmldebugpacket.h" + #include <QCoreApplication> +#include <private/qqmldebugconnector_p.h> +#include <private/qversionedpacket_p.h> #include <private/qqmldebugserviceinterfaces_p.h> #include <private/qquickprofiler_p.h> QT_BEGIN_NAMESPACE +using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>; + QQuickProfilerAdapter::QQuickProfilerAdapter(QObject *parent) : QQmlAbstractProfilerAdapter(parent), next(0) { @@ -148,10 +152,8 @@ static void qQuickProfilerDataToByteArrays(const QQuickProfilerData &data, } } -qint64 QQuickProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages, - bool trackLocations) +qint64 QQuickProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages) { - 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 1ad020afd6..1f3467c1d0 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, bool trackLocations) override; + qint64 sendMessages(qint64 until, QList<QByteArray> &messages) override; void receiveData(const QVector<QQuickProfilerData> &new_data); private: diff --git a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapterfactory.cpp b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapterfactory.cpp index f38307b1f7..66addee2e8 100644 --- a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapterfactory.cpp +++ b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapterfactory.cpp @@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE QQmlAbstractProfilerAdapter *QQuickProfilerAdapterFactory::create(const QString &key) { if (key != QLatin1String("QQuickProfilerAdapter")) - return 0; + return nullptr; return new QQuickProfilerAdapter(this); } diff --git a/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro b/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro index fffdb4c888..d7d24a4d39 100644 --- a/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro +++ b/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro @@ -5,13 +5,7 @@ SOURCES += \ $$PWD/qqmldebugserver.cpp HEADERS += \ - $$PWD/qqmldebugserverfactory.h \ - $$PWD/../shared/qqmldebugserver.h \ - $$PWD/../shared/qqmldebugserverconnection.h \ - $$PWD/../shared/qqmldebugpacket.h - -INCLUDEPATH += $$PWD \ - $$PWD/../shared + $$PWD/qqmldebugserverfactory.h OTHER_FILES += \ qqmldebugserver.json diff --git a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp index 0a7421842a..8293e88038 100644 --- a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp +++ b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp @@ -37,17 +37,17 @@ ** ****************************************************************************/ -#include "qqmldebugserver.h" #include "qqmldebugserverfactory.h" -#include "qqmldebugserverconnection.h" -#include "qqmldebugpacket.h" +#include <private/qqmldebugserver_p.h> +#include <private/qqmldebugserverconnection_p.h> #include <private/qqmldebugservice_p.h> #include <private/qjsengine_p.h> #include <private/qqmlglobal_p.h> #include <private/qqmldebugpluginmanager_p.h> #include <private/qqmldebugserviceinterfaces_p.h> #include <private/qpacketprotocol_p.h> +#include <private/qversionedpacket_p.h> #include <QtCore/QAtomicInt> #include <QtCore/QDir> @@ -83,12 +83,13 @@ QT_BEGIN_NAMESPACE Q_QML_DEBUG_PLUGIN_LOADER(QQmlDebugServerConnection) const int protocolVersion = 1; +using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>; class QQmlDebugServerImpl; class QQmlDebugServerThread : public QThread { public: - QQmlDebugServerThread() : m_server(0), m_portFrom(-1), m_portTo(-1) {} + QQmlDebugServerThread() : m_server(nullptr), m_portFrom(-1), m_portTo(-1) {} void setServer(QQmlDebugServerImpl *server) { @@ -131,19 +132,19 @@ class QQmlDebugServerImpl : public QQmlDebugServer public: QQmlDebugServerImpl(); - bool blockingMode() const Q_DECL_OVERRIDE; + bool blockingMode() const override; - QQmlDebugService *service(const QString &name) const Q_DECL_OVERRIDE; + QQmlDebugService *service(const QString &name) const override; - void addEngine(QJSEngine *engine) Q_DECL_OVERRIDE; - void removeEngine(QJSEngine *engine) Q_DECL_OVERRIDE; - bool hasEngine(QJSEngine *engine) const Q_DECL_OVERRIDE; + void addEngine(QJSEngine *engine) override; + void removeEngine(QJSEngine *engine) override; + bool hasEngine(QJSEngine *engine) const override; - bool addService(const QString &name, QQmlDebugService *service) Q_DECL_OVERRIDE; - bool removeService(const QString &name) Q_DECL_OVERRIDE; + bool addService(const QString &name, QQmlDebugService *service) override; + bool removeService(const QString &name) override; - bool open(const QVariantHash &configuration) Q_DECL_OVERRIDE; - void setDevice(QIODevice *socket) Q_DECL_OVERRIDE; + bool open(const QVariantHash &configuration) override; + void setDevice(QIODevice *socket) override; void parseArguments(); @@ -176,14 +177,13 @@ private: void changeServiceState(const QString &serviceName, QQmlDebugService::State state); void removeThread(); void receiveMessage(); - void invalidPacket(); + void protocolError(); QQmlDebugServerConnection *m_connection; QHash<QString, QQmlDebugService *> m_plugins; QStringList m_clientPlugins; bool m_gotHello; bool m_blockingMode; - bool m_clientSupportsMultiPackets; QHash<QJSEngine *, EngineCondition> m_engineConditions; @@ -228,7 +228,7 @@ void QQmlDebugServerImpl::cleanup() void QQmlDebugServerThread::run() { - Q_ASSERT_X(m_server != 0, Q_FUNC_INFO, "There should always be a debug server available here."); + Q_ASSERT_X(m_server != nullptr, Q_FUNC_INFO, "There should always be a debug server available here."); QQmlDebugServerConnection *connection = loadQQmlDebugServerConnection(m_pluginName); if (connection) { { @@ -274,10 +274,9 @@ static void cleanupOnShutdown() } QQmlDebugServerImpl::QQmlDebugServerImpl() : - m_connection(0), + m_connection(nullptr), m_gotHello(false), - m_blockingMode(false), - m_clientSupportsMultiPackets(false) + m_blockingMode(false) { static bool postRoutineAdded = false; if (!postRoutineAdded) { @@ -463,10 +462,9 @@ void QQmlDebugServerImpl::receiveMessage() s_dataStreamVersion = QDataStream::Qt_DefaultCompiledVersion; } + bool clientSupportsMultiPackets = false; if (!in.atEnd()) - in >> m_clientSupportsMultiPackets; - else - m_clientSupportsMultiPackets = false; + in >> clientSupportsMultiPackets; // Send the hello answer immediately, since it needs to arrive before // the plugins below start sending messages. @@ -474,13 +472,15 @@ void QQmlDebugServerImpl::receiveMessage() QQmlDebugPacket out; QStringList pluginNames; QList<float> pluginVersions; - const int count = m_plugins.count(); - pluginNames.reserve(count); - pluginVersions.reserve(count); - for (QHash<QString, QQmlDebugService *>::ConstIterator i = m_plugins.constBegin(); - i != m_plugins.constEnd(); ++i) { - pluginNames << i.key(); - pluginVersions << i.value()->version(); + if (clientSupportsMultiPackets) { // otherwise, disable all plugins + const int count = m_plugins.count(); + pluginNames.reserve(count); + pluginVersions.reserve(count); + for (QHash<QString, QQmlDebugService *>::ConstIterator i = m_plugins.constBegin(); + i != m_plugins.constEnd(); ++i) { + pluginNames << i.key(); + pluginVersions << i.value()->version(); + } } out << QString(QStringLiteral("QDeclarativeDebugClient")) << 0 << protocolVersion @@ -522,7 +522,7 @@ void QQmlDebugServerImpl::receiveMessage() } else { qWarning("QML Debugger: Invalid control message %d.", op); - invalidPacket(); + protocolError(); return; } @@ -570,7 +570,7 @@ void QQmlDebugServerImpl::removeThread() QThread *parentThread = m_thread.thread(); delete m_connection; - m_connection = 0; + m_connection = nullptr; // Move it back to the parent thread so that we can potentially restart it on a new thread. moveToThread(parentThread); @@ -700,16 +700,11 @@ void QQmlDebugServerImpl::sendMessage(const QString &name, const QByteArray &mes void QQmlDebugServerImpl::sendMessages(const QString &name, const QList<QByteArray> &messages) { if (canSendMessage(name)) { - if (m_clientSupportsMultiPackets) { - QQmlDebugPacket out; - out << name; - for (const QByteArray &message : messages) - out << message; - m_protocol->send(out.data()); - } else { - for (const QByteArray &message : messages) - doSendMessage(name, message); - } + QQmlDebugPacket out; + out << name; + for (const QByteArray &message : messages) + out << message; + m_protocol->send(out.data()); m_connection->flush(); } } @@ -742,29 +737,28 @@ void QQmlDebugServerImpl::setDevice(QIODevice *socket) m_protocol = new QPacketProtocol(socket, this); QObject::connect(m_protocol, &QPacketProtocol::readyRead, this, &QQmlDebugServerImpl::receiveMessage); - QObject::connect(m_protocol, &QPacketProtocol::invalidPacket, - this, &QQmlDebugServerImpl::invalidPacket); + QObject::connect(m_protocol, &QPacketProtocol::error, + this, &QQmlDebugServerImpl::protocolError); if (blockingMode()) m_protocol->waitForReadyRead(-1); } -void QQmlDebugServerImpl::invalidPacket() +void QQmlDebugServerImpl::protocolError() { - qWarning("QML Debugger: Received a corrupted packet! Giving up ..."); + qWarning("QML Debugger: A protocol error has occurred! Giving up ..."); m_connection->disconnect(); // protocol might still be processing packages at this point m_protocol->deleteLater(); - m_protocol = 0; + m_protocol = nullptr; } QQmlDebugConnector *QQmlDebugServerFactory::create(const QString &key) { // Cannot parent it to this because it gets moved to another thread - return (key == QLatin1String("QQmlDebugServer") ? new QQmlDebugServerImpl : 0); + return (key == QLatin1String("QQmlDebugServer") ? new QQmlDebugServerImpl : nullptr); } QT_END_NAMESPACE #include "qqmldebugserver.moc" -#include "moc_qqmldebugserver.cpp" diff --git a/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro b/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro index 1face1813e..a0e9d06fd5 100644 --- a/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro +++ b/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro @@ -5,12 +5,7 @@ SOURCES += \ $$PWD/qtcpserverconnection.cpp HEADERS += \ - $$PWD/qtcpserverconnectionfactory.h \ - $$PWD/../shared/qqmldebugserver.h \ - $$PWD/../shared/qqmldebugserverconnection.h - -INCLUDEPATH += $$PWD \ - $$PWD/../shared + $$PWD/qtcpserverconnectionfactory.h OTHER_FILES += \ $$PWD/qtcpserverconnection.json diff --git a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp index af4f5292ba..42442b07e7 100644 --- a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp +++ b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp @@ -38,7 +38,8 @@ ****************************************************************************/ #include "qtcpserverconnectionfactory.h" -#include "qqmldebugserver.h" + +#include <private/qqmldebugserver_p.h> #include <QtCore/qplugin.h> #include <QtNetwork/qtcpserver.h> @@ -53,7 +54,7 @@ class QTcpServerConnection : public QQmlDebugServerConnection public: QTcpServerConnection(); - ~QTcpServerConnection(); + ~QTcpServerConnection() override; void setServer(QQmlDebugServer *server) override; bool setPortRange(int portFrom, int portTo, bool block, const QString &hostaddress) override; @@ -69,24 +70,16 @@ private: void newConnection(); bool listen(); - int m_portFrom; - int m_portTo; - bool m_block; + int m_portFrom = 0; + int m_portTo = 0; + bool m_block = false; QString m_hostaddress; - QTcpSocket *m_socket; - QTcpServer *m_tcpServer; - QQmlDebugServer *m_debugServer; + QTcpSocket *m_socket = nullptr; + QTcpServer *m_tcpServer = nullptr; + QQmlDebugServer *m_debugServer = nullptr; }; -QTcpServerConnection::QTcpServerConnection() : - m_portFrom(0), - m_portTo(0), - m_block(false), - m_socket(0), - m_tcpServer(0), - m_debugServer(0) -{ -} +QTcpServerConnection::QTcpServerConnection() {} QTcpServerConnection::~QTcpServerConnection() { @@ -115,7 +108,7 @@ void QTcpServerConnection::disconnect() } m_socket->deleteLater(); - m_socket = 0; + m_socket = nullptr; } bool QTcpServerConnection::setPortRange(int portFrom, int portTo, bool block, @@ -198,7 +191,7 @@ void QTcpServerConnection::newConnection() QQmlDebugServerConnection *QTcpServerConnectionFactory::create(const QString &key) { - return (key == QLatin1String("QTcpServerConnection") ? new QTcpServerConnection : 0); + return (key == QLatin1String("QTcpServerConnection") ? new QTcpServerConnection : nullptr); } QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnectionfactory.h b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnectionfactory.h index d3b0e00584..d1282c9d47 100644 --- a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnectionfactory.h +++ b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnectionfactory.h @@ -40,7 +40,7 @@ #ifndef QTCPSERVERCONNECTIONFACTORY_H #define QTCPSERVERCONNECTIONFACTORY_H -#include "qqmldebugserverconnection.h" +#include <private/qqmldebugserverconnection_p.h> QT_BEGIN_NAMESPACE diff --git a/src/plugins/qmltooling/qmltooling.pro b/src/plugins/qmltooling/qmltooling.pro index 27c51b53c8..30097be77b 100644 --- a/src/plugins/qmltooling/qmltooling.pro +++ b/src/plugins/qmltooling/qmltooling.pro @@ -6,37 +6,39 @@ SUBDIRS += \ packetprotocol # Connectors -SUBDIRS += \ - qmldbg_native \ - qmldbg_server +SUBDIRS += qmldbg_native +qtConfig(thread): SUBDIRS += qmldbg_server qmldbg_native.depends = packetprotocol qmldbg_server.depends = packetprotocol qtConfig(qml-network) { + qtConfig(localserver): SUBDIRS += qmldbg_local + SUBDIRS += \ - qmldbg_local \ qmldbg_tcp } -qtConfig(qml-interpreter) { - # Services - SUBDIRS += \ - qmldbg_debugger \ - qmldbg_profiler \ - qmldbg_messages \ - qmldbg_nativedebugger - - qmldbg_debugger.depends = packetprotocol - qmldbg_profiler.depends = packetprotocol - qmldbg_messages.depends = packetprotocol - qmldbg_nativedebugger.depends = packetprotocol -} +# Services +SUBDIRS += \ + qmldbg_messages \ + qmldbg_profiler \ + qmldbg_debugger \ + qmldbg_nativedebugger + +qmldbg_messages.depends = packetprotocol +qmldbg_profiler.depends = packetprotocol +qmldbg_debugger.depends = packetprotocol +qmldbg_nativedebugger.depends = packetprotocol qtHaveModule(quick) { SUBDIRS += \ qmldbg_inspector \ qmldbg_quickprofiler + + qtConfig(qml-network): SUBDIRS += qmldbg_preview + qmldbg_inspector.depends = packetprotocol qmldbg_quickprofiler.depends = packetprotocol + qmldbg_preview.depends = packetprotocol } diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp index 3351486bc6..312e8c19cd 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp @@ -460,8 +460,8 @@ QSGD3D12Material::UpdateResults QSGD3D12SmoothTextureMaterial::updatePipeline(co QSGD3D12TextMaterial::QSGD3D12TextMaterial(StyleType styleType, QSGD3D12RenderContext *rc, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat) : m_styleType(styleType), - m_font(font), - m_rc(rc) + m_rc(rc), + m_font(font) { setFlag(Blending, true); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp index 908f1221ab..75bde2c66b 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp @@ -2077,7 +2077,7 @@ void QSGD3D12EnginePrivate::queueDraw(const QSGD3D12Engine::DrawParams ¶ms) ? buffers[indexBufIdx].d[currentPFrameIndex].buffer.Get() : nullptr; if (!skip && params.mode != tframeData.drawingMode) { - D3D_PRIMITIVE_TOPOLOGY topology; + D3D_PRIMITIVE_TOPOLOGY topology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; switch (params.mode) { case QSGGeometry::DrawPoints: topology = D3D_PRIMITIVE_TOPOLOGY_POINTLIST; @@ -3046,7 +3046,7 @@ void QSGD3D12EnginePrivate::useRenderTargetAsTexture(uint id) } tframeData.activeTextures[tframeData.activeTextureCount++] = - TransientFrameData::ActiveTexture::ActiveTexture(TransientFrameData::ActiveTexture::TypeRenderTarget, id); + TransientFrameData::ActiveTexture(TransientFrameData::ActiveTexture::TypeRenderTarget, id); } QImage QSGD3D12EnginePrivate::executeAndWaitReadbackRenderTarget(uint id) diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h index 1048ed63e7..a95cbb1cbb 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h @@ -105,6 +105,7 @@ public: struct DeviceLossObserver { virtual void deviceLost() = 0; + virtual ~DeviceLossObserver() = default; }; void registerDeviceLossObserver(DeviceLossObserver *observer); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp index c0f111ee83..c38c616ae6 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp @@ -69,10 +69,10 @@ public: QSGD3D12Renderer::QSGD3D12Renderer(QSGRenderContext *context) : QSGRenderer(context), - m_renderList(16), m_vboData(1024), m_iboData(256), - m_cboData(4096) + m_cboData(4096), + m_renderList(16) { setNodeUpdater(new DummyUpdater); } @@ -447,10 +447,10 @@ void QSGD3D12Renderer::renderElements() struct RenderNodeState : public QSGRenderNode::RenderState { const QMatrix4x4 *projectionMatrix() const override { return m_projectionMatrix; } - QRect scissorRect() const { return m_scissorRect; } - bool scissorEnabled() const { return m_scissorEnabled; } - int stencilValue() const { return m_stencilValue; } - bool stencilEnabled() const { return m_stencilEnabled; } + QRect scissorRect() const override { return m_scissorRect; } + bool scissorEnabled() const override { return m_scissorEnabled; } + int stencilValue() const override { return m_stencilValue; } + bool stencilEnabled() const override { return m_stencilEnabled; } const QRegion *clipRegion() const override { return nullptr; } const QMatrix4x4 *m_projectionMatrix; diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp index 60b76deb2e..0d501f48c0 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp @@ -145,9 +145,6 @@ void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window) rc->invalidate(); - if (m_windows.isEmpty()) - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - delete rc; delete engine; @@ -461,6 +458,7 @@ void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) data.rc->initialize(nullptr); wd->syncSceneGraph(); + data.rc->endSync(); if (profileFrames) syncTime = renderTimer.nsecsElapsed(); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp index 62771eb8f9..b4fb721a8b 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp @@ -171,7 +171,7 @@ void QSGD3D12ShaderLinker::linkTextureSubRects() // texture bind point. for (Constant &c : constants) { if (c.specialType == QSGShaderEffectNode::VariableData::SubRect) { - if (c.value.type() == QMetaType::QByteArray) { + if (c.value.type() == QVariant::ByteArray) { const QByteArray name = c.value.toByteArray(); if (!textureNameMap.contains(name)) qWarning("ShaderEffect: qt_SubRect_%s refers to unknown source texture", qPrintable(name)); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp index 11cc257103..120a84566f 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp @@ -410,6 +410,7 @@ bool QSGD3D12RenderThread::event(QEvent *e) QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window); rc->initialize(nullptr); wd->syncSceneGraph(); + rc->endSync(); wd->renderSceneGraph(wme->window->size()); *wme->image = engine->executeAndWaitReadbackRenderTarget(); } @@ -545,6 +546,7 @@ void QSGD3D12RenderThread::sync(bool inExpose) rc->initialize(nullptr); wd->syncSceneGraph(); + rc->endSync(); if (!hadRenderer && wd->renderer) { if (Q_UNLIKELY(debug_loop())) diff --git a/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp b/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp index 0bd51cbf46..d728686248 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp @@ -176,6 +176,12 @@ void QSGOpenVGInternalRectangleNode::setGradientStops(const QGradientStops &stop m_fillDirty = true; } +void QSGOpenVGInternalRectangleNode::setGradientVertical(bool vertical) +{ + m_vertical = vertical; + m_fillDirty = true; +} + void QSGOpenVGInternalRectangleNode::setRadius(qreal radius) { m_radius = radius; @@ -242,13 +248,13 @@ void QSGOpenVGInternalRectangleNode::render() } else { // Linear Gradient vgSetParameteri(m_rectanglePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_LINEAR_GRADIENT); - const VGfloat verticalLinearGradient[] = { - 0.0f, + const VGfloat linearGradient[] = { 0.0f, 0.0f, - static_cast<VGfloat>(m_rect.height()) + m_vertical ? 0.0f : static_cast<VGfloat>(m_rect.width()), + m_vertical ? static_cast<VGfloat>(m_rect.height()) : 0.0f }; - vgSetParameterfv(m_rectanglePaint, VG_PAINT_LINEAR_GRADIENT, 4, verticalLinearGradient); + vgSetParameterfv(m_rectanglePaint, VG_PAINT_LINEAR_GRADIENT, 4, linearGradient); vgSetParameteri(m_rectanglePaint, VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_PAD); vgSetParameteri(m_rectanglePaint, VG_PAINT_COLOR_RAMP_PREMULTIPLIED, false); diff --git a/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.h b/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.h index e8d25c94f8..86d2c3318c 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.h +++ b/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.h @@ -59,6 +59,7 @@ public: void setPenColor(const QColor &color) override; void setPenWidth(qreal width) override; void setGradientStops(const QGradientStops &stops) override; + void setGradientVertical(bool vertical) override; void setRadius(qreal radius) override; void setAligned(bool aligned) override; void update() override; @@ -85,6 +86,7 @@ private: qreal m_penWidth = 0.0; qreal m_radius = 0.0; bool m_aligned = false; + bool m_vertical = true; QGradientStops m_gradientStops; VGPath m_rectanglePath; diff --git a/src/plugins/scenegraph/openvg/qsgopenvgpublicnodes.cpp b/src/plugins/scenegraph/openvg/qsgopenvgpublicnodes.cpp index e45b79706b..b5f6b39c60 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgpublicnodes.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvgpublicnodes.cpp @@ -174,7 +174,7 @@ void QSGOpenVGRectangleNode::render() } -QSGOpenVGImageNode::QSGOpenVGImageNode() +QSGOpenVGImageNode::QSGOpenVGImageNode() : m_texture(nullptr), m_owns(false) { // Set Dummy material and geometry to avoid asserts setMaterial((QSGMaterial*)1); @@ -184,9 +184,8 @@ QSGOpenVGImageNode::QSGOpenVGImageNode() QSGOpenVGImageNode::~QSGOpenVGImageNode() { - if (m_owns) { - m_texture->deleteLater(); - } + if (m_owns) + delete m_texture; } void QSGOpenVGImageNode::setRect(const QRectF &rect) @@ -212,6 +211,8 @@ QRectF QSGOpenVGImageNode::sourceRect() const void QSGOpenVGImageNode::setTexture(QSGTexture *texture) { + if (m_owns) + delete m_texture; m_texture = texture; markDirty(DirtyMaterial); } @@ -321,7 +322,7 @@ void QSGOpenVGImageNode::render() } -QSGOpenVGNinePatchNode::QSGOpenVGNinePatchNode() +QSGOpenVGNinePatchNode::QSGOpenVGNinePatchNode() : m_texture(nullptr) { // Set Dummy material and geometry to avoid asserts setMaterial((QSGMaterial*)1); @@ -329,8 +330,14 @@ QSGOpenVGNinePatchNode::QSGOpenVGNinePatchNode() } +QSGOpenVGNinePatchNode::~QSGOpenVGNinePatchNode() +{ + delete m_texture; +} + void QSGOpenVGNinePatchNode::setTexture(QSGTexture *texture) { + delete m_texture; m_texture = texture; markDirty(DirtyMaterial); } diff --git a/src/plugins/scenegraph/openvg/qsgopenvgpublicnodes.h b/src/plugins/scenegraph/openvg/qsgopenvgpublicnodes.h index 8e12c27824..e1cd3063a1 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgpublicnodes.h +++ b/src/plugins/scenegraph/openvg/qsgopenvgpublicnodes.h @@ -118,6 +118,7 @@ class QSGOpenVGNinePatchNode : public QSGNinePatchNode, public QSGOpenVGRenderab { public: QSGOpenVGNinePatchNode(); + ~QSGOpenVGNinePatchNode(); void setTexture(QSGTexture *texture) override; void setBounds(const QRectF &bounds) override; diff --git a/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp b/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp index f7aa704095..c41dfd7400 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp @@ -43,6 +43,7 @@ #include <QtCore/QCoreApplication> #include <QtCore/QElapsedTimer> +#include <private/qquickanimatorcontroller_p.h> #include <private/qquickwindow_p.h> #include <private/qquickprofiler_p.h> @@ -91,10 +92,11 @@ void QSGOpenVGRenderLoop::windowDestroyed(QQuickWindow *window) rc->invalidate(); delete vg; vg = nullptr; - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); } else if (vg && window == vg->window()) { vg->doneCurrent(); } + + delete d->animationController; } void QSGOpenVGRenderLoop::exposureChanged(QQuickWindow *window) @@ -205,6 +207,7 @@ void QSGOpenVGRenderLoop::renderWindow(QQuickWindow *window) emit window->afterAnimating(); cd->syncSceneGraph(); + rc->endSync(); if (profileFrames) syncTime = renderTimer.nsecsElapsed(); |