aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@theqtcompany.com>2015-10-22 13:07:11 +0200
committerUlf Hermann <ulf.hermann@theqtcompany.com>2015-10-22 13:07:11 +0200
commitb103f6a6b9cc0ddf3df2788816a6fd98369b1b6d (patch)
treefc90bc070f0f50a2d8768db8fa1e16611d158f18 /src/plugins
parent4867a49618e0d6a476e0549aeca5134b2e3c5892 (diff)
parent8ee3673e439b4499a8a4a4280637ee0270c4a54a (diff)
Merge remote-tracking branch 'origin/5.6' into origin/dev
Conflicts: src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h src/qml/debugger/qqmldebugserviceinterfaces.cpp src/qml/jsruntime/qv4debugging_p.h Change-Id: I82a4ce1bcd4579181df886558f55ad2b328d1682
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro2
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservice.json2
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp804
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.h92
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h14
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp46
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h13
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp59
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h6
-rw-r--r--src/plugins/qmltooling/qmldbg_native/qmldbg_native.pro12
-rw-r--r--src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.cpp384
-rw-r--r--src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.json3
-rw-r--r--src/plugins/qmltooling/qmltooling.pro1
15 files changed, 1389 insertions, 57 deletions
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro b/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro
index d860328dc8..8d1a54e9e4 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro
+++ b/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro
@@ -9,6 +9,7 @@ SOURCES += \
$$PWD/qdebugmessageservice.cpp \
$$PWD/qqmldebuggerservicefactory.cpp \
$$PWD/qqmlenginedebugservice.cpp \
+ $$PWD/qqmlnativedebugservice.cpp \
$$PWD/qqmlwatcher.cpp \
$$PWD/qv4debugservice.cpp \
$$PWD/qv4debuggeragent.cpp \
@@ -19,6 +20,7 @@ HEADERS += \
$$PWD/qdebugmessageservice.h \
$$PWD/qqmldebuggerservicefactory.h \
$$PWD/qqmlenginedebugservice.h \
+ $$PWD/qqmlnativedebugservice.h \
$$PWD/qqmlwatcher.h \
$$PWD/qv4debugservice.h \
$$PWD/qv4debuggeragent.h \
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservice.json b/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservice.json
index b1e90364d5..967a725903 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservice.json
+++ b/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservice.json
@@ -1,3 +1,3 @@
{
- "Keys": [ "DebugMessages", "QmlDebugger", "V8Debugger" ]
+ "Keys": [ "DebugMessages", "QmlDebugger", "V8Debugger", "NativeQmlDebugger" ]
}
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp
index ac3b435350..4690da04d1 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qqmldebuggerservicefactory.cpp
@@ -35,6 +35,7 @@
#include "qqmlenginedebugservice.h"
#include "qdebugmessageservice.h"
#include "qv4debugservice.h"
+#include "qqmlnativedebugservice.h"
#include <private/qqmldebugserviceinterfaces_p.h>
QT_BEGIN_NAMESPACE
@@ -50,6 +51,9 @@ QQmlDebugService *QQmlDebuggerServiceFactory::create(const QString &key)
if (key == QV4DebugServiceImpl::s_key)
return new QV4DebugServiceImpl(this);
+ if (key == QQmlNativeDebugServiceImpl::s_key)
+ return new QQmlNativeDebugServiceImpl(this);
+
return 0;
}
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp
new file mode 100644
index 0000000000..f5cc78e77f
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp
@@ -0,0 +1,804 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlnativedebugservice.h"
+
+#include <private/qqmldebugconnector_p.h>
+#include <private/qv4debugging_p.h>
+#include <private/qv8engine_p.h>
+#include <private/qv4engine_p.h>
+#include <private/qv4debugging_p.h>
+#include <private/qv4script_p.h>
+#include <private/qv4string_p.h>
+#include <private/qv4objectiterator_p.h>
+#include <private/qv4identifier_p.h>
+#include <private/qv4runtime_p.h>
+#include <private/qv4isel_moth_p.h>
+#include <private/qqmldebugserviceinterfaces_p.h>
+
+#include <qqmlengine.h>
+
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qjsonvalue.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qpointer.h>
+
+//#define TRACE_PROTOCOL(s) qDebug() << s
+#define TRACE_PROTOCOL(s)
+
+QT_BEGIN_NAMESPACE
+
+class BreakPoint
+{
+public:
+ BreakPoint() : id(-1), lineNumber(-1), enabled(false), ignoreCount(0), hitCount(0) {}
+ bool isValid() const { return lineNumber >= 0 && !fileName.isEmpty(); }
+
+ int id;
+ int lineNumber;
+ QString fileName;
+ bool enabled;
+ QString condition;
+ int ignoreCount;
+
+ int hitCount;
+};
+
+inline uint qHash(const BreakPoint &b, uint seed = 0) Q_DECL_NOTHROW
+{
+ return qHash(b.fileName, seed) ^ b.lineNumber;
+}
+
+inline bool operator==(const BreakPoint &a, const BreakPoint &b)
+{
+ return a.lineNumber == b.lineNumber && a.fileName == b.fileName
+ && a.enabled == b.enabled && a.condition == b.condition
+ && a.ignoreCount == b.ignoreCount;
+}
+
+static void setError(QJsonObject *response, const QString &msg)
+{
+ response->insert(QStringLiteral("type"), QStringLiteral("error"));
+ response->insert(QStringLiteral("msg"), msg);
+}
+
+class NativeDebugger;
+
+class Collector
+{
+public:
+ Collector(QV4::ExecutionEngine *engine)
+ : m_engine(engine), m_anonCount(0)
+ {}
+
+ void collect(QJsonArray *output, const QString &parentIName, const QString &name,
+ const QV4::Value &value);
+
+ bool isExpanded(const QString &iname) const { return m_expanded.contains(iname); }
+
+public:
+ QV4::ExecutionEngine *m_engine;
+ int m_anonCount;
+ QStringList m_expanded;
+};
+
+// Encapsulate Breakpoint handling
+// Could be made per-NativeDebugger (i.e. per execution engine, if needed)
+class BreakPointHandler
+{
+public:
+ BreakPointHandler() : m_haveBreakPoints(false), m_breakOnThrow(true), m_lastBreakpoint(1) {}
+
+ void handleSetBreakpoint(QJsonObject *response, const QJsonObject &arguments);
+ void handleRemoveBreakpoint(QJsonObject *response, const QJsonObject &arguments);
+
+ void removeBreakPoint(int id);
+ void enableBreakPoint(int id, bool onoff);
+
+ void setBreakOnThrow(bool onoff);
+ bool m_haveBreakPoints;
+ bool m_breakOnThrow;
+ int m_lastBreakpoint;
+ QVector<BreakPoint> m_breakPoints;
+};
+
+void BreakPointHandler::handleSetBreakpoint(QJsonObject *response, const QJsonObject &arguments)
+{
+ TRACE_PROTOCOL("SET BREAKPOINT" << arguments);
+ QString type = arguments.value(QStringLiteral("type")).toString();
+
+ QString fileName = arguments.value(QStringLiteral("file")).toString();
+ if (fileName.isEmpty()) {
+ setError(response, QStringLiteral("breakpoint has no file name"));
+ return;
+ }
+
+ int line = arguments.value(QStringLiteral("line")).toInt(-1);
+ if (line < 0) {
+ setError(response, QStringLiteral("breakpoint has an invalid line number"));
+ return;
+ }
+
+ BreakPoint bp;
+ bp.id = m_lastBreakpoint++;
+ bp.fileName = fileName.mid(fileName.lastIndexOf('/') + 1);
+ bp.lineNumber = line;
+ bp.enabled = arguments.value(QStringLiteral("enabled")).toBool(true);
+ bp.condition = arguments.value(QStringLiteral("condition")).toString();
+ bp.ignoreCount = arguments.value(QStringLiteral("ignorecount")).toInt();
+ m_breakPoints.append(bp);
+
+ m_haveBreakPoints = true;
+
+ response->insert(QStringLiteral("type"), type);
+ response->insert(QStringLiteral("breakpoint"), bp.id);
+}
+
+void BreakPointHandler::handleRemoveBreakpoint(QJsonObject *response, const QJsonObject &arguments)
+{
+ int id = arguments.value(QStringLiteral("id")).toInt();
+ removeBreakPoint(id);
+ response->insert(QStringLiteral("id"), id);
+}
+
+class NativeDebugger : public QV4::Debugging::Debugger
+{
+public:
+ NativeDebugger(QQmlNativeDebugServiceImpl *service, QV4::ExecutionEngine *engine);
+
+ void signalEmitted(const QString &signal);
+
+ QV4::ExecutionEngine *engine() const { return m_engine; }
+
+ bool pauseAtNextOpportunity() const Q_DECL_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 handleCommand(QJsonObject *response, const QString &cmd, const QJsonObject &arguments);
+
+private:
+ void handleBacktrace(QJsonObject *response, const QJsonObject &arguments);
+ void handleVariables(QJsonObject *response, const QJsonObject &arguments);
+ void handleExpressions(QJsonObject *response, const QJsonObject &arguments);
+
+ void handleDebuggerDeleted(QObject *debugger);
+
+ QV4::ReturnedValue evaluateExpression(QV4::Scope &scope, const QString &expression);
+ bool checkCondition(const QString &expression);
+
+ QStringList breakOnSignals;
+
+ enum Speed {
+ NotStepping = 0,
+ StepOut,
+ StepOver,
+ StepIn,
+ };
+
+ void pauseAndWait();
+ void pause();
+ void handleContinue(QJsonObject *reponse, Speed speed);
+
+ QV4::Function *getFunction() const;
+
+ bool reallyHitTheBreakPoint(const QV4::Function *function, int lineNumber);
+
+ QV4::ExecutionEngine *m_engine;
+ QQmlNativeDebugServiceImpl *m_service;
+ QV4::PersistentValue m_currentContext;
+ Speed m_stepping;
+ bool m_pauseRequested;
+ bool m_runningJob;
+
+ QV4::PersistentValue m_returnedValue;
+};
+
+bool NativeDebugger::checkCondition(const QString &expression)
+{
+ QV4::Scope scope(m_engine);
+ QV4::ReturnedValue result = evaluateExpression(scope, expression);
+ QV4::ScopedValue val(scope, result);
+ return val->booleanValue();
+}
+
+QV4::ReturnedValue NativeDebugger::evaluateExpression(QV4::Scope &scope, const QString &expression)
+{
+ m_runningJob = true;
+
+ QV4::ExecutionContextSaver saver(scope);
+
+ QV4::ExecutionContext *ctx = m_engine->currentContext;
+ m_engine->pushContext(ctx);
+
+ QV4::Script script(ctx, expression);
+ script.strictMode = ctx->d()->strictMode;
+ // 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 (!m_engine->hasException)
+ result = script.run();
+
+ m_runningJob = false;
+ return result->asReturnedValue();
+}
+
+NativeDebugger::NativeDebugger(QQmlNativeDebugServiceImpl *service, QV4::ExecutionEngine *engine)
+ : m_returnedValue(engine, QV4::Primitive::undefinedValue())
+{
+ m_stepping = NotStepping;
+ m_pauseRequested = false;
+ m_runningJob = false;
+ m_service = service;
+ m_engine = engine;
+ TRACE_PROTOCOL("Creating native debugger");
+}
+
+void NativeDebugger::signalEmitted(const QString &signal)
+{
+ //This function is only called by QQmlBoundSignal
+ //only if there is a slot connected to the signal. Hence, there
+ //is no need for additional check.
+
+ //Parse just the name and remove the class info
+ //Normalize to Lower case.
+ QString signalName = signal.left(signal.indexOf(QLatin1Char('('))).toLower();
+
+ foreach (const QString &signal, breakOnSignals) {
+ if (signal == signalName) {
+ // TODO: pause debugger
+ break;
+ }
+ }
+}
+
+void NativeDebugger::handleCommand(QJsonObject *response, const QString &cmd,
+ const QJsonObject &arguments)
+{
+ if (cmd == QStringLiteral("backtrace"))
+ handleBacktrace(response, arguments);
+ else if (cmd == QStringLiteral("variables"))
+ handleVariables(response, arguments);
+ else if (cmd == QStringLiteral("expressions"))
+ handleExpressions(response, arguments);
+ else if (cmd == QStringLiteral("stepin"))
+ handleContinue(response, StepIn);
+ else if (cmd == QStringLiteral("stepout"))
+ handleContinue(response, StepOut);
+ else if (cmd == QStringLiteral("stepover"))
+ handleContinue(response, StepOver);
+ else if (cmd == QStringLiteral("continue"))
+ handleContinue(response, NotStepping);
+}
+
+static QString encodeContext(QV4::ExecutionContext *executionContext)
+{
+ QByteArray ba;
+ QDataStream ds(&ba, QIODevice::WriteOnly);
+ ds << quintptr(executionContext);
+ return QString::fromLatin1(ba.toHex());
+}
+
+static void decodeContext(const QString &context, QV4::ExecutionContext **executionContext)
+{
+ quintptr rawContext;
+ QDataStream ds(QByteArray::fromHex(context.toLatin1()));
+ ds >> rawContext;
+ *executionContext = reinterpret_cast<QV4::ExecutionContext *>(rawContext);
+}
+
+void NativeDebugger::handleBacktrace(QJsonObject *response, const QJsonObject &arguments)
+{
+ int limit = arguments.value(QStringLiteral("limit")).toInt(0);
+
+ QJsonArray frameArray;
+ QV4::ExecutionContext *executionContext = m_engine->currentContext;
+ for (int i = 0; i < limit && executionContext; ++i) {
+ QV4::Heap::FunctionObject *heapFunctionObject = executionContext->getFunctionObject();
+ if (heapFunctionObject) {
+
+ QJsonObject frame;
+ frame[QStringLiteral("language")] = QStringLiteral("js");
+ frame[QStringLiteral("context")] = encodeContext(executionContext);
+
+ if (QV4::Function *function = heapFunctionObject->function) {
+ if (QV4::Heap::String *functionName = function->name())
+ frame[QStringLiteral("function")] = functionName->toQString();
+ frame[QStringLiteral("file")] = function->sourceFile();
+ }
+ int line = executionContext->d()->lineNumber;
+ frame[QStringLiteral("line")] = (line < 0 ? -line : line);
+
+ frameArray.push_back(frame);
+ }
+
+ executionContext = m_engine->parentContext(executionContext);
+ }
+
+ response->insert(QStringLiteral("frames"), frameArray);
+}
+
+void Collector::collect(QJsonArray *out, const QString &parentIName, const QString &name,
+ const QV4::Value &value)
+{
+ QJsonObject dict;
+ QV4::Scope scope(m_engine);
+
+ QString nonEmptyName = name.isEmpty() ? QString::fromLatin1("@%1").arg(m_anonCount++) : name;
+ QString iname = parentIName + QLatin1Char('.') + nonEmptyName;
+ dict.insert(QStringLiteral("iname"), iname);
+ dict.insert(QStringLiteral("name"), nonEmptyName);
+
+ QV4::ScopedValue typeString(scope, QV4::Runtime::typeofValue(m_engine, value));
+ dict.insert(QStringLiteral("type"), typeString->toQStringNoThrow());
+
+ switch (value.type()) {
+ case QV4::Value::Empty_Type:
+ dict.insert(QStringLiteral("valueencoded"), QStringLiteral("empty"));
+ dict.insert(QStringLiteral("haschild"), false);
+ break;
+ case QV4::Value::Undefined_Type:
+ dict.insert(QStringLiteral("valueencoded"), QStringLiteral("undefined"));
+ dict.insert(QStringLiteral("haschild"), false);
+ break;
+ case QV4::Value::Null_Type:
+ dict.insert(QStringLiteral("type"), QStringLiteral("object"));
+ dict.insert(QStringLiteral("valueencoded"), QStringLiteral("null"));
+ dict.insert(QStringLiteral("haschild"), false);
+ break;
+ case QV4::Value::Boolean_Type:
+ dict.insert(QStringLiteral("value"), value.booleanValue());
+ dict.insert(QStringLiteral("haschild"), false);
+ break;
+ case QV4::Value::Managed_Type:
+ if (const QV4::String *string = value.as<QV4::String>()) {
+ dict.insert(QStringLiteral("value"), string->toQStringNoThrow());
+ dict.insert(QStringLiteral("haschild"), false);
+ dict.insert(QStringLiteral("valueencoded"), QStringLiteral("utf16"));
+ dict.insert(QStringLiteral("quoted"), true);
+ } else if (const QV4::ArrayObject *array = value.as<QV4::ArrayObject>()) {
+ // The size of an array is number of its numerical properties.
+ // We don't consider free form object properties here.
+ const uint n = array->getLength();
+ dict.insert(QStringLiteral("value"), qint64(n));
+ dict.insert(QStringLiteral("valueencoded"), QStringLiteral("itemcount"));
+ dict.insert(QStringLiteral("haschild"), qint64(n));
+ if (isExpanded(iname)) {
+ QJsonArray children;
+ for (uint i = 0; i < n; ++i) {
+ QV4::ReturnedValue v = array->getIndexed(i);
+ QV4::ScopedValue sval(scope, v);
+ collect(&children, iname, QString::number(i), *sval);
+ }
+ dict.insert(QStringLiteral("children"), children);
+ }
+ } else if (const QV4::Object *object = value.as<QV4::Object>()) {
+ QJsonArray children;
+ bool expanded = isExpanded(iname);
+ qint64 numProperties = 0;
+ QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
+ QV4::ScopedProperty p(scope);
+ QV4::ScopedString name(scope);
+ while (true) {
+ QV4::PropertyAttributes attrs;
+ uint index;
+ it.next(name.getRef(), &index, p, &attrs);
+ if (attrs.isEmpty())
+ break;
+ if (name.getPointer()) {
+ ++numProperties;
+ if (expanded) {
+ if (name.getPointer()) {
+ QV4::Value v = p.property->value;
+ collect(&children, iname, name->toQStringNoThrow(), v);
+ }
+ }
+ }
+ }
+ dict.insert(QStringLiteral("value"), numProperties);
+ dict.insert(QStringLiteral("valueencoded"), QStringLiteral("itemcount"));
+ dict.insert(QStringLiteral("haschild"), numProperties > 0);
+ if (expanded)
+ dict.insert(QStringLiteral("children"), children);
+ }
+ break;
+ case QV4::Value::Integer_Type:
+ dict.insert(QStringLiteral("value"), value.integerValue());
+ dict.insert(QStringLiteral("haschild"), false);
+ break;
+ default: // double
+ dict.insert(QStringLiteral("value"), value.doubleValue());
+ dict.insert(QStringLiteral("haschild"), false);
+ }
+
+ out->append(dict);
+}
+
+void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &arguments)
+{
+ TRACE_PROTOCOL("Build variables");
+ QV4::ExecutionContext *executionContext = 0;
+ decodeContext(arguments.value(QStringLiteral("context")).toString(), &executionContext);
+ if (!executionContext) {
+ setError(response, QStringLiteral("No execution context passed"));
+ return;
+ }
+ TRACE_PROTOCOL("Context: " << executionContext);
+
+ QV4::ExecutionEngine *engine = executionContext->d()->engine;
+ if (!engine) {
+ setError(response, QStringLiteral("No execution engine passed"));
+ return;
+ }
+ TRACE_PROTOCOL("Engine: " << engine);
+
+ Collector collector(engine);
+ QJsonArray expanded = arguments.value(QStringLiteral("expanded")).toArray();
+ foreach (const QJsonValue &ex, expanded)
+ collector.m_expanded.append(ex.toString());
+ TRACE_PROTOCOL("Expanded: " << collector.m_expanded);
+
+ QJsonArray output;
+ QV4::Scope scope(engine);
+
+ if (QV4::CallContext *callContext = executionContext->asCallContext()) {
+ QV4::Value thisObject = callContext->thisObject();
+ collector.collect(&output, QString(), QStringLiteral("this"), thisObject);
+ QV4::Identifier *const *variables = callContext->variables();
+ QV4::Identifier *const *formals = callContext->formals();
+ for (unsigned i = 0, ei = callContext->variableCount(); i != ei; ++i) {
+ QString qName;
+ if (QV4::Identifier *name = variables[i])
+ qName = name->string;
+ QV4::Value val = callContext->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);
+ }
+ }
+
+ response->insert(QStringLiteral("variables"), output);
+}
+
+void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject &arguments)
+{
+ TRACE_PROTOCOL("Evaluate expressions");
+ QV4::ExecutionContext *executionContext = 0;
+ decodeContext(arguments.value(QStringLiteral("context")).toString(), &executionContext);
+ if (!executionContext) {
+ setError(response, QStringLiteral("No execution context passed"));
+ return;
+ }
+ TRACE_PROTOCOL("Context: " << executionContext);
+
+ QV4::ExecutionEngine *engine = executionContext->d()->engine;
+ if (!engine) {
+ setError(response, QStringLiteral("No execution engine passed"));
+ return;
+ }
+ TRACE_PROTOCOL("Engines: " << engine << m_engine);
+
+ Collector collector(engine);
+ QJsonArray expanded = arguments.value(QStringLiteral("expanded")).toArray();
+ foreach (const QJsonValue &ex, expanded)
+ collector.m_expanded.append(ex.toString());
+ TRACE_PROTOCOL("Expanded: " << collector.m_expanded);
+
+ QJsonArray output;
+ QV4::Scope scope(engine);
+
+ QJsonArray expressions = arguments.value(QStringLiteral("expressions")).toArray();
+ foreach (const QJsonValue &expr, expressions) {
+ QString expression = expr.toObject().value(QStringLiteral("expression")).toString();
+ QString name = expr.toObject().value(QStringLiteral("name")).toString();
+ TRACE_PROTOCOL("Evaluate expression: " << expression);
+ m_runningJob = true;
+
+ QV4::ReturnedValue eval = evaluateExpression(scope, expression);
+ QV4::ScopedValue result(scope, eval);
+
+ m_runningJob = false;
+ if (result->isUndefined()) {
+ QJsonObject dict;
+ dict[QStringLiteral("name")] = name;
+ dict[QStringLiteral("valueencoded")] = QStringLiteral("undefined");
+ output.append(dict);
+ } else if (result.ptr && result.ptr->_val) {
+ collector.collect(&output, QString(), name, *result);
+ } else {
+ QJsonObject dict;
+ dict[QStringLiteral("name")] = name;
+ dict[QStringLiteral("valueencoded")] = QStringLiteral("notaccessible");
+ output.append(dict);
+ }
+ TRACE_PROTOCOL("EXCEPTION: " << engine->hasException);
+ engine->hasException = false;
+ }
+
+ response->insert(QStringLiteral("expressions"), output);
+}
+
+void BreakPointHandler::removeBreakPoint(int id)
+{
+ for (int i = 0; i != m_breakPoints.size(); ++i) {
+ if (m_breakPoints.at(i).id == id) {
+ m_breakPoints.remove(i);
+ m_haveBreakPoints = !m_breakPoints.isEmpty();
+ return;
+ }
+ }
+}
+
+void BreakPointHandler::enableBreakPoint(int id, bool enabled)
+{
+ m_breakPoints[id].enabled = enabled;
+}
+
+void NativeDebugger::pause()
+{
+ m_pauseRequested = true;
+}
+
+void NativeDebugger::handleContinue(QJsonObject *response, Speed speed)
+{
+ Q_UNUSED(response);
+
+ if (!m_returnedValue.isUndefined())
+ m_returnedValue.set(m_engine, QV4::Encode::undefined());
+
+ m_currentContext.set(m_engine, *m_engine->currentContext);
+ m_stepping = speed;
+}
+
+void NativeDebugger::maybeBreakAtInstruction()
+{
+ if (m_runningJob) // do not re-enter when we're doing a job for the debugger.
+ return;
+
+ if (m_stepping == StepOver) {
+ if (m_currentContext.asManaged()->d() == m_engine->current)
+ pauseAndWait();
+ return;
+ }
+
+ if (m_stepping == StepIn) {
+ pauseAndWait();
+ return;
+ }
+
+ if (m_pauseRequested) { // Serve debugging requests from the agent
+ m_pauseRequested = false;
+ pauseAndWait();
+ return;
+ }
+
+ if (m_service->m_breakHandler->m_haveBreakPoints) {
+ if (QV4::Function *function = getFunction()) {
+ const int lineNumber = m_engine->current->lineNumber;
+ if (reallyHitTheBreakPoint(function, lineNumber))
+ pauseAndWait();
+ }
+ }
+}
+
+void NativeDebugger::enteringFunction()
+{
+ if (m_runningJob)
+ return;
+
+ if (m_stepping == StepIn) {
+ m_currentContext.set(m_engine, *m_engine->currentContext);
+ }
+}
+
+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));
+ m_stepping = StepOver;
+ m_returnedValue.set(m_engine, retVal);
+ }
+}
+
+void NativeDebugger::aboutToThrow()
+{
+ if (!m_service->m_breakHandler->m_breakOnThrow)
+ return;
+
+ if (m_runningJob) // do not re-enter when we're doing a job for the debugger.
+ return;
+
+ QJsonObject event;
+ // TODO: complete this!
+ event.insert(QStringLiteral("event"), QStringLiteral("exception"));
+ m_service->emitAsynchronousMessageToClient(event);
+}
+
+QV4::Function *NativeDebugger::getFunction() const
+{
+ QV4::Scope scope(m_engine);
+ QV4::ExecutionContext *context = m_engine->currentContext;
+ QV4::ScopedFunctionObject function(scope, context->getFunctionObject());
+ if (function)
+ return function->function();
+ else
+ return context->d()->engine->globalCode;
+}
+
+void NativeDebugger::pauseAndWait()
+{
+ QJsonObject event;
+
+ event.insert(QStringLiteral("event"), QStringLiteral("break"));
+ event.insert(QStringLiteral("language"), QStringLiteral("js"));
+ if (QV4::ExecutionContext *executionContext = m_engine->currentContext) {
+ QV4::Heap::FunctionObject *heapFunctionObject = executionContext->getFunctionObject();
+ if (heapFunctionObject) {
+ if (QV4::Function *function = heapFunctionObject->function)
+ event.insert(QStringLiteral("file"), function->sourceFile());
+ int line = executionContext->d()->lineNumber;
+ event.insert(QStringLiteral("line"), (line < 0 ? -line : line));
+ }
+ }
+
+ m_service->emitAsynchronousMessageToClient(event);
+}
+
+bool NativeDebugger::reallyHitTheBreakPoint(const QV4::Function *function, int lineNumber)
+{
+ 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 QString base = fileName.mid(fileName.lastIndexOf('/') + 1);
+ if (bp.fileName.endsWith(base)) {
+ if (bp.condition.isEmpty() || checkCondition(bp.condition)) {
+ BreakPoint &mbp = m_service->m_breakHandler->m_breakPoints[i];
+ ++mbp.hitCount;
+ if (mbp.hitCount > mbp.ignoreCount)
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+QQmlNativeDebugServiceImpl::QQmlNativeDebugServiceImpl(QObject *parent)
+ : QQmlNativeDebugService(1.0, parent)
+{
+ m_breakHandler = new BreakPointHandler;
+}
+
+QQmlNativeDebugServiceImpl::~QQmlNativeDebugServiceImpl()
+{
+ delete m_breakHandler;
+}
+
+void QQmlNativeDebugServiceImpl::engineAboutToBeAdded(QQmlEngine *engine)
+{
+ TRACE_PROTOCOL("Adding engine" << engine);
+ if (engine) {
+ QV4::ExecutionEngine *ee = QV8Engine::getV4(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));
+ }
+ }
+ QQmlDebugService::engineAboutToBeAdded(engine);
+}
+
+void QQmlNativeDebugServiceImpl::engineAboutToBeRemoved(QQmlEngine *engine)
+{
+ TRACE_PROTOCOL("Removing engine" << engine);
+ if (engine) {
+ QV4::ExecutionEngine *executionEngine = QV8Engine::getV4(engine->handle());
+ foreach (NativeDebugger *debugger, m_debuggers) {
+ if (debugger->engine() == executionEngine)
+ m_debuggers.removeAll(debugger);
+ }
+ }
+ QQmlDebugService::engineAboutToBeRemoved(engine);
+}
+
+void QQmlNativeDebugServiceImpl::stateAboutToBeChanged(QQmlDebugService::State state)
+{
+ if (state == Enabled) {
+ foreach (NativeDebugger *debugger, m_debuggers) {
+ QV4::ExecutionEngine *engine = debugger->engine();
+ if (!engine->debugger)
+ engine->setDebugger(debugger);
+ }
+ }
+ QQmlDebugService::stateAboutToBeChanged(state);
+}
+
+void QQmlNativeDebugServiceImpl::messageReceived(const QByteArray &message)
+{
+ TRACE_PROTOCOL("Native message received: " << message);
+ QJsonObject request = QJsonDocument::fromJson(message).object();
+ QJsonObject response;
+ QJsonObject arguments = request.value(QStringLiteral("arguments")).toObject();
+ QString cmd = request.value(QStringLiteral("command")).toString();
+
+ if (cmd == QStringLiteral("setbreakpoint")) {
+ m_breakHandler->handleSetBreakpoint(&response, arguments);
+ } else if (cmd == QStringLiteral("removebreakpoint")) {
+ m_breakHandler->handleRemoveBreakpoint(&response, arguments);
+ } else if (cmd == QStringLiteral("echo")) {
+ response.insert(QStringLiteral("result"), arguments);
+ } else {
+ foreach (NativeDebugger *debugger, m_debuggers)
+ if (debugger)
+ debugger->handleCommand(&response, cmd, arguments);
+ }
+ QJsonDocument doc;
+ doc.setObject(response);
+ QByteArray ba = doc.toJson(QJsonDocument::Compact);
+ TRACE_PROTOCOL("Sending synchronous response:" << ba.constData() << endl);
+ emit messageToClient(s_key, ba);
+}
+
+void QQmlNativeDebugServiceImpl::emitAsynchronousMessageToClient(const QJsonObject &message)
+{
+ QJsonDocument doc;
+ doc.setObject(message);
+ QByteArray ba = doc.toJson(QJsonDocument::Compact);
+ TRACE_PROTOCOL("Sending asynchronous message:" << ba.constData() << endl);
+ emit messageToClient(s_key, ba);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.h
new file mode 100644
index 0000000000..9d0780a203
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQML_NATIVE_DEBUG_SERVICE_H
+#define QQML_NATIVE_DEBUG_SERVICE_H
+
+#include <private/qqmldebugconnector_p.h>
+#include <private/qv4debugging_p.h>
+#include <private/qv8engine_p.h>
+#include <private/qv4engine_p.h>
+#include <private/qv4debugging_p.h>
+#include <private/qv4script_p.h>
+#include <private/qv4string_p.h>
+#include <private/qv4objectiterator_p.h>
+#include <private/qv4identifier_p.h>
+#include <private/qv4runtime_p.h>
+#include <private/qqmldebugserviceinterfaces_p.h>
+
+#include <QtCore/qjsonarray.h>
+
+#include <qqmlengine.h>
+
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonValue>
+#include <QVector>
+#include <QPointer>
+
+QT_BEGIN_NAMESPACE
+
+class NativeDebugger;
+class BreakPointHandler;
+class QQmlDebuggerServiceFactory;
+
+class QQmlNativeDebugServiceImpl : public QQmlNativeDebugService
+{
+public:
+ QQmlNativeDebugServiceImpl(QObject *parent);
+
+ ~QQmlNativeDebugServiceImpl();
+
+ void engineAboutToBeAdded(QQmlEngine *engine);
+ void engineAboutToBeRemoved(QQmlEngine *engine);
+
+ void stateAboutToBeChanged(State state);
+
+ void messageReceived(const QByteArray &message);
+
+ void emitAsynchronousMessageToClient(const QJsonObject &message);
+
+private:
+ friend class QQmlDebuggerServiceFactory;
+ friend class NativeDebugger;
+
+ QList<QPointer<NativeDebugger> > m_debuggers;
+ BreakPointHandler *m_breakHandler;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQML_NATIVE_DEBUG_SERVICE_H
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
index b13c469893..377f0845d0 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
@@ -208,13 +208,13 @@ QV4DataCollector::Ref QV4DataCollector::addScriptRef(const QString &scriptName)
return ref;
}
-void QV4DataCollector::collectScope(QJsonObject *dict, QV4::Debugging::Debugger *debugger,
+void QV4DataCollector::collectScope(QJsonObject *dict, QV4::Debugging::V4Debugger *debugger,
int frameNr, int scopeNr)
{
QStringList names;
Refs refs;
- if (debugger->state() == QV4::Debugging::Debugger::Paused) {
+ if (debugger->state() == QV4::Debugging::V4Debugger::Paused) {
RefHolder holder(this, &refs);
ArgumentCollectJob argumentsJob(m_engine, this, &names, frameNr, scopeNr);
debugger->runInEngine(&argumentsJob);
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h
index 39d621e95e..0ea40f896c 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h
@@ -60,7 +60,7 @@ public:
Ref addFunctionRef(const QString &functionName);
Ref addScriptRef(const QString &scriptName);
- void collectScope(QJsonObject *dict, QV4::Debugging::Debugger *debugger, int frameNr,
+ void collectScope(QJsonObject *dict, QV4::Debugging::V4Debugger *debugger, int frameNr,
int scopeNr);
QV4::ExecutionEngine *engine() const { return m_engine; }
@@ -101,7 +101,7 @@ private:
QV4DataCollector::Refs *m_previousRefs;
};
-class ExpressionEvalJob: public QV4::Debugging::Debugger::JavaScriptJob
+class ExpressionEvalJob: public QV4::Debugging::V4Debugger::JavaScriptJob
{
QV4DataCollector *collector;
QString exception;
@@ -113,7 +113,7 @@ public:
const QString &exceptionMessage() const;
};
-class GatherSourcesJob: public QV4::Debugging::Debugger::Job
+class GatherSourcesJob: public QV4::Debugging::V4Debugger::Job
{
QV4::ExecutionEngine *engine;
QStringList sources;
@@ -124,7 +124,7 @@ public:
const QStringList &result() const;
};
-class ArgumentCollectJob: public QV4::Debugging::Debugger::Job
+class ArgumentCollectJob: public QV4::Debugging::V4Debugger::Job
{
QV4::ExecutionEngine *engine;
QV4DataCollector *collector;
@@ -138,7 +138,7 @@ public:
void run();
};
-class LocalCollectJob: public QV4::Debugging::Debugger::Job
+class LocalCollectJob: public QV4::Debugging::V4Debugger::Job
{
QV4::ExecutionEngine *engine;
QV4DataCollector *collector;
@@ -152,7 +152,7 @@ public:
void run();
};
-class ThisCollectJob: public QV4::Debugging::Debugger::Job
+class ThisCollectJob: public QV4::Debugging::V4Debugger::Job
{
QV4::ExecutionEngine *engine;
QV4DataCollector *collector;
@@ -166,7 +166,7 @@ public:
bool myRun();
};
-class ExceptionCollectJob: public QV4::Debugging::Debugger::Job
+class ExceptionCollectJob: public QV4::Debugging::V4Debugger::Job
{
QV4::ExecutionEngine *engine;
QV4DataCollector *collector;
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
index 270a6958a9..dcb40dd548 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
@@ -44,7 +44,7 @@ QV4DebuggerAgent::QV4DebuggerAgent(QV4DebugServiceImpl *debugService)
: m_breakOnThrow(false), m_debugService(debugService)
{}
-QV4::Debugging::Debugger *QV4DebuggerAgent::firstDebugger() const
+QV4::Debugging::V4Debugger *QV4DebuggerAgent::firstDebugger() const
{
// Currently only 1 single engine is supported, so:
if (m_debuggers.isEmpty())
@@ -56,13 +56,13 @@ QV4::Debugging::Debugger *QV4DebuggerAgent::firstDebugger() const
bool QV4DebuggerAgent::isRunning() const
{
// Currently only 1 single engine is supported, so:
- if (QV4::Debugging::Debugger *debugger = firstDebugger())
- return debugger->state() == QV4::Debugging::Debugger::Running;
+ if (QV4::Debugging::V4Debugger *debugger = firstDebugger())
+ return debugger->state() == QV4::Debugging::V4Debugger::Running;
else
return false;
}
-void QV4DebuggerAgent::debuggerPaused(QV4::Debugging::Debugger *debugger,
+void QV4DebuggerAgent::debuggerPaused(QV4::Debugging::V4Debugger *debugger,
QV4::Debugging::PauseReason reason)
{
Q_UNUSED(reason);
@@ -105,7 +105,7 @@ void QV4DebuggerAgent::debuggerPaused(QV4::Debugging::Debugger *debugger,
m_debugService->send(event);
}
-void QV4DebuggerAgent::addDebugger(QV4::Debugging::Debugger *debugger)
+void QV4DebuggerAgent::addDebugger(QV4::Debugging::V4Debugger *debugger)
{
Q_ASSERT(!m_debuggers.contains(debugger));
m_debuggers << debugger;
@@ -118,49 +118,55 @@ void QV4DebuggerAgent::addDebugger(QV4::Debugging::Debugger *debugger)
connect(debugger, SIGNAL(destroyed(QObject*)),
this, SLOT(handleDebuggerDeleted(QObject*)));
- connect(debugger, SIGNAL(debuggerPaused(QV4::Debugging::Debugger*,QV4::Debugging::PauseReason)),
- this, SLOT(debuggerPaused(QV4::Debugging::Debugger*,QV4::Debugging::PauseReason)),
+ connect(debugger,
+ SIGNAL(debuggerPaused(QV4::Debugging::V4Debugger*,QV4::Debugging::PauseReason)),
+ this, SLOT(debuggerPaused(QV4::Debugging::V4Debugger*,QV4::Debugging::PauseReason)),
Qt::QueuedConnection);
}
-void QV4DebuggerAgent::removeDebugger(QV4::Debugging::Debugger *debugger)
+void QV4DebuggerAgent::removeDebugger(QV4::Debugging::V4Debugger *debugger)
{
m_debuggers.removeAll(debugger);
disconnect(debugger, SIGNAL(destroyed(QObject*)),
this, SLOT(handleDebuggerDeleted(QObject*)));
disconnect(debugger,
- SIGNAL(debuggerPaused(QV4::Debugging::Debugger*,QV4::Debugging::PauseReason)),
+ SIGNAL(debuggerPaused(QV4::Debugging::V4Debugger*,QV4::Debugging::PauseReason)),
this,
- SLOT(debuggerPaused(QV4::Debugging::Debugger*,QV4::Debugging::PauseReason)));
+ SLOT(debuggerPaused(QV4::Debugging::V4Debugger*,QV4::Debugging::PauseReason)));
+}
+
+const QList<QV4::Debugging::V4Debugger *> &QV4DebuggerAgent::debuggers()
+{
+ return m_debuggers;
}
void QV4DebuggerAgent::handleDebuggerDeleted(QObject *debugger)
{
- m_debuggers.removeAll(static_cast<QV4::Debugging::Debugger *>(debugger));
+ m_debuggers.removeAll(static_cast<QV4::Debugging::V4Debugger *>(debugger));
}
-void QV4DebuggerAgent::pause(QV4::Debugging::Debugger *debugger) const
+void QV4DebuggerAgent::pause(QV4::Debugging::V4Debugger *debugger) const
{
debugger->pause();
}
void QV4DebuggerAgent::pauseAll() const
{
- foreach (QV4::Debugging::Debugger *debugger, m_debuggers)
+ foreach (QV4::Debugging::V4Debugger *debugger, m_debuggers)
pause(debugger);
}
void QV4DebuggerAgent::resumeAll() const
{
- foreach (QV4::Debugging::Debugger *debugger, m_debuggers)
- if (debugger->state() == QV4::Debugging::Debugger::Paused)
- debugger->resume(QV4::Debugging::Debugger::FullThrottle);
+ foreach (QV4::Debugging::V4Debugger *debugger, m_debuggers)
+ if (debugger->state() == QV4::Debugging::V4Debugger::Paused)
+ debugger->resume(QV4::Debugging::V4Debugger::FullThrottle);
}
int QV4DebuggerAgent::addBreakPoint(const QString &fileName, int lineNumber, bool enabled, const QString &condition)
{
if (enabled)
- foreach (QV4::Debugging::Debugger *debugger, m_debuggers)
+ foreach (QV4::Debugging::V4Debugger *debugger, m_debuggers)
debugger->addBreakPoint(fileName, lineNumber, condition);
int id = m_breakPoints.size();
@@ -177,7 +183,7 @@ void QV4DebuggerAgent::removeBreakPoint(int id)
m_breakPoints.remove(id);
if (breakPoint.enabled)
- foreach (QV4::Debugging::Debugger *debugger, m_debuggers)
+ foreach (QV4::Debugging::V4Debugger *debugger, m_debuggers)
debugger->removeBreakPoint(breakPoint.fileName, breakPoint.lineNr);
}
@@ -195,7 +201,7 @@ void QV4DebuggerAgent::enableBreakPoint(int id, bool onoff)
return;
breakPoint.enabled = onoff;
- foreach (QV4::Debugging::Debugger *debugger, m_debuggers) {
+ foreach (QV4::Debugging::V4Debugger *debugger, m_debuggers) {
if (onoff)
debugger->addBreakPoint(breakPoint.fileName, breakPoint.lineNr, breakPoint.condition);
else
@@ -218,7 +224,7 @@ void QV4DebuggerAgent::setBreakOnThrow(bool onoff)
{
if (onoff != m_breakOnThrow) {
m_breakOnThrow = onoff;
- foreach (QV4::Debugging::Debugger *debugger, m_debuggers)
+ foreach (QV4::Debugging::V4Debugger *debugger, m_debuggers)
debugger->setBreakOnThrow(onoff);
}
}
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h
index 64cf0e3d60..9f77a17b45 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h
@@ -46,13 +46,14 @@ class QV4DebuggerAgent : public QObject
public:
QV4DebuggerAgent(QV4DebugServiceImpl *m_debugService);
- QV4::Debugging::Debugger *firstDebugger() const;
+ QV4::Debugging::V4Debugger *firstDebugger() const;
bool isRunning() const;
- void addDebugger(QV4::Debugging::Debugger *debugger);
- void removeDebugger(QV4::Debugging::Debugger *debugger);
+ void addDebugger(QV4::Debugging::V4Debugger *debugger);
+ void removeDebugger(QV4::Debugging::V4Debugger *debugger);
+ const QList<QV4::Debugging::V4Debugger *> &debuggers();
- void pause(QV4::Debugging::Debugger *debugger) const;
+ void pause(QV4::Debugging::V4Debugger *debugger) const;
void pauseAll() const;
void resumeAll() const;
int addBreakPoint(const QString &fileName, int lineNumber, bool enabled = true, const QString &condition = QString());
@@ -65,11 +66,11 @@ public:
void setBreakOnThrow(bool onoff);
public slots:
- void debuggerPaused(QV4::Debugging::Debugger *debugger, QV4::Debugging::PauseReason reason);
+ void debuggerPaused(QV4::Debugging::V4Debugger *debugger, QV4::Debugging::PauseReason reason);
void handleDebuggerDeleted(QObject *debugger);
private:
- QList<QV4::Debugging::Debugger *> m_debuggers;
+ QList<QV4::Debugging::V4Debugger *> m_debuggers;
struct BreakPoint {
QString fileName;
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
index 14333e01f6..f742502e2a 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
@@ -34,6 +34,7 @@
#include "qv4debugservice.h"
#include "qqmlengine.h"
#include <private/qv4engine_p.h>
+#include <private/qv4isel_moth_p.h>
#include <private/qv4function_p.h>
#include <private/qqmldebugconnector_p.h>
#include <private/qpacket_p.h>
@@ -269,7 +270,7 @@ public:
int toFrame = arguments.value(QStringLiteral("toFrame")).toInt(fromFrame + 10);
// no idea what the bottom property is for, so we'll ignore it.
- QV4::Debugging::Debugger *debugger = debugService->debuggerAgent.firstDebugger();
+ QV4::Debugging::V4Debugger *debugger = debugService->debuggerAgent.firstDebugger();
QJsonArray frameArray;
QVector<QV4::StackFrame> frames = debugger->stackTrace(toFrame);
@@ -306,7 +307,7 @@ public:
const int frameNr = arguments.value(QStringLiteral("number")).toInt(
debugService->selectedFrame());
- QV4::Debugging::Debugger *debugger = debugService->debuggerAgent.firstDebugger();
+ QV4::Debugging::V4Debugger *debugger = debugService->debuggerAgent.firstDebugger();
QVector<QV4::StackFrame> frames = debugger->stackTrace(frameNr + 1);
if (frameNr < 0 || frameNr >= frames.size()) {
createErrorResponse(QStringLiteral("frame command has invalid frame number"));
@@ -339,7 +340,7 @@ public:
debugService->selectedFrame());
const int scopeNr = arguments.value(QStringLiteral("number")).toInt(0);
- QV4::Debugging::Debugger *debugger = debugService->debuggerAgent.firstDebugger();
+ QV4::Debugging::V4Debugger *debugger = debugService->debuggerAgent.firstDebugger();
QVector<QV4::StackFrame> frames = debugger->stackTrace(frameNr + 1);
if (frameNr < 0 || frameNr >= frames.size()) {
createErrorResponse(QStringLiteral("scope command has invalid frame number"));
@@ -397,10 +398,10 @@ public:
// decypher the payload:
QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject();
- QV4::Debugging::Debugger *debugger = debugService->debuggerAgent.firstDebugger();
+ QV4::Debugging::V4Debugger *debugger = debugService->debuggerAgent.firstDebugger();
if (arguments.empty()) {
- debugger->resume(QV4::Debugging::Debugger::FullThrottle);
+ debugger->resume(QV4::Debugging::V4Debugger::FullThrottle);
} else {
QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject();
QString stepAction = arguments.value(QStringLiteral("stepaction")).toString();
@@ -409,11 +410,11 @@ public:
qWarning() << "Step count other than 1 is not supported.";
if (stepAction == QStringLiteral("in")) {
- debugger->resume(QV4::Debugging::Debugger::StepIn);
+ debugger->resume(QV4::Debugging::V4Debugger::StepIn);
} else if (stepAction == QStringLiteral("out")) {
- debugger->resume(QV4::Debugging::Debugger::StepOut);
+ debugger->resume(QV4::Debugging::V4Debugger::StepOut);
} else if (stepAction == QStringLiteral("next")) {
- debugger->resume(QV4::Debugging::Debugger::StepOver);
+ debugger->resume(QV4::Debugging::V4Debugger::StepOver);
} else {
createErrorResponse(QStringLiteral("continue command has invalid stepaction"));
return;
@@ -505,7 +506,7 @@ public:
}
// do it:
- QV4::Debugging::Debugger *debugger = debugService->debuggerAgent.firstDebugger();
+ QV4::Debugging::V4Debugger *debugger = debugService->debuggerAgent.firstDebugger();
GatherSourcesJob job(debugger->engine());
debugger->runInEngine(&job);
@@ -560,8 +561,8 @@ public:
virtual void handleRequest()
{
- QV4::Debugging::Debugger *debugger = debugService->debuggerAgent.firstDebugger();
- if (debugger->state() == QV4::Debugging::Debugger::Paused) {
+ QV4::Debugging::V4Debugger *debugger = debugService->debuggerAgent.firstDebugger();
+ if (debugger->state() == QV4::Debugging::V4Debugger::Paused) {
QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject();
QString expression = arguments.value(QStringLiteral("expression")).toString();
const int frame = arguments.value(QStringLiteral("frame")).toInt(0);
@@ -632,8 +633,11 @@ void QV4DebugServiceImpl::engineAboutToBeAdded(QQmlEngine *engine)
QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle());
if (QQmlDebugConnector *server = QQmlDebugConnector::instance()) {
if (ee) {
- ee->enableDebugger();
- debuggerAgent.addDebugger(ee->debugger);
+ ee->iselFactory.reset(new QV4::Moth::ISelFactory);
+ QV4::Debugging::V4Debugger *debugger = new QV4::Debugging::V4Debugger(ee);
+ if (state() == Enabled)
+ ee->setDebugger(debugger);
+ debuggerAgent.addDebugger(debugger);
debuggerAgent.moveToThread(server->thread());
}
}
@@ -646,12 +650,29 @@ void QV4DebugServiceImpl::engineAboutToBeRemoved(QQmlEngine *engine)
QMutexLocker lock(&m_configMutex);
if (engine){
const QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle());
- if (ee)
- debuggerAgent.removeDebugger(ee->debugger);
+ if (ee) {
+ QV4::Debugging::V4Debugger *debugger
+ = qobject_cast<QV4::Debugging::V4Debugger *>(ee->debugger);
+ if (debugger)
+ debuggerAgent.removeDebugger(debugger);
+ }
}
QQmlConfigurableDebugService<QV4DebugService>::engineAboutToBeRemoved(engine);
}
+void QV4DebugServiceImpl::stateAboutToBeChanged(State state)
+{
+ QMutexLocker lock(&m_configMutex);
+ if (state == Enabled) {
+ foreach (QV4::Debugging::V4Debugger *debugger, debuggerAgent.debuggers()) {
+ QV4::ExecutionEngine *ee = debugger->engine();
+ if (!ee->debugger)
+ ee->setDebugger(debugger);
+ }
+ }
+ QQmlConfigurableDebugService<QV4DebugService>::stateAboutToBeChanged(state);
+}
+
void QV4DebugServiceImpl::signalEmitted(const QString &signal)
{
//This function is only called by QQmlBoundSignal
@@ -766,7 +787,7 @@ void QV4DebugServiceImpl::clearHandles(QV4::ExecutionEngine *engine)
}
QJsonObject QV4DebugServiceImpl::buildFrame(const QV4::StackFrame &stackFrame, int frameNr,
- QV4::Debugging::Debugger *debugger)
+ QV4::Debugging::V4Debugger *debugger)
{
QV4DataCollector::Ref ref;
@@ -784,7 +805,7 @@ QJsonObject QV4DebugServiceImpl::buildFrame(const QV4::StackFrame &stackFrame, i
frame[QLatin1String("column")] = stackFrame.column;
QJsonArray scopes;
- if (debugger->state() == QV4::Debugging::Debugger::Paused) {
+ if (debugger->state() == QV4::Debugging::V4Debugger::Paused) {
RefHolder holder(theCollector.data(), &collectedRefs);
bool foundThis = false;
ThisCollectJob job(debugger->engine(), theCollector.data(), frameNr, &foundThis);
@@ -834,7 +855,7 @@ int QV4DebugServiceImpl::encodeScopeType(QV4::Heap::ExecutionContext::ContextTyp
}
QJsonObject QV4DebugServiceImpl::buildScope(int frameNr, int scopeNr,
- QV4::Debugging::Debugger *debugger)
+ QV4::Debugging::V4Debugger *debugger)
{
QJsonObject scope;
@@ -842,7 +863,7 @@ QJsonObject QV4DebugServiceImpl::buildScope(int frameNr, int scopeNr,
RefHolder holder(theCollector.data(), &collectedRefs);
theCollector->collectScope(&object, debugger, frameNr, scopeNr);
- if (debugger->state() == QV4::Debugging::Debugger::Paused) {
+ if (debugger->state() == QV4::Debugging::V4Debugger::Paused) {
QVector<QV4::Heap::ExecutionContext::ContextType> scopeTypes =
QV4DataCollector::getScopeTypes(debugger->engine(), frameNr);
scope[QLatin1String("type")] = encodeScopeType(scopeTypes[scopeNr]);
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
index dbf41ac363..ea4a695fed 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
@@ -73,16 +73,18 @@ public:
void engineAboutToBeAdded(QQmlEngine *engine);
void engineAboutToBeRemoved(QQmlEngine *engine);
+ void stateAboutToBeChanged(State state);
+
void signalEmitted(const QString &signal);
void send(QJsonObject v8Payload);
- QJsonObject buildScope(int frameNr, int scopeNr, QV4::Debugging::Debugger *debugger);
+ QJsonObject buildScope(int frameNr, int scopeNr, QV4::Debugging::V4Debugger *debugger);
QJsonArray buildRefs();
QJsonValue lookup(QV4DataCollector::Ref refId);
QJsonValue toRef(QV4DataCollector::Ref ref);
QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr,
- QV4::Debugging::Debugger *debugger);
+ QV4::Debugging::V4Debugger *debugger);
int selectedFrame() const;
void selectFrame(int frameNr);
diff --git a/src/plugins/qmltooling/qmldbg_native/qmldbg_native.pro b/src/plugins/qmltooling/qmldbg_native/qmldbg_native.pro
new file mode 100644
index 0000000000..7dc16b8c44
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_native/qmldbg_native.pro
@@ -0,0 +1,12 @@
+TARGET = qmldbg_native
+QT += qml-private core-private
+
+PLUGIN_TYPE = qmltooling
+PLUGIN_CLASS_NAME = QQmlNativeDebugConnectorFactory
+load(qt_plugin)
+
+SOURCES += \
+ $$PWD/qqmlnativedebugconnector.cpp
+
+OTHER_FILES += \
+ $$PWD/qqmlnativedebugconnector.json
diff --git a/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.cpp b/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.cpp
new file mode 100644
index 0000000000..018b10d3e7
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.cpp
@@ -0,0 +1,384 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qqmldebugconnector_p.h>
+#include <private/qhooks_p.h>
+#include <private/qpacket_p.h>
+
+#include <qqmlengine.h>
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qjsonvalue.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qvector.h>
+
+//#define TRACE_PROTOCOL(s) qDebug() << s
+#define TRACE_PROTOCOL(s)
+
+QT_USE_NAMESPACE
+
+static bool expectSyncronousResponse = false;
+Q_GLOBAL_STATIC(QByteArray, responseBuffer)
+
+extern "C" {
+
+Q_DECL_EXPORT const char *qt_qmlDebugMessageBuffer;
+Q_DECL_EXPORT int qt_qmlDebugMessageLength;
+Q_DECL_EXPORT bool qt_qmlDebugConnectionBlocker;
+
+// In blocking mode, this will busy wait until the debugger sets block to false.
+Q_DECL_EXPORT void qt_qmlDebugConnectorOpen();
+
+// First thing, set the debug stream version. Please use this function as we might move the version
+// member to some other place.
+Q_DECL_EXPORT void qt_qmlDebugSetStreamVersion(int version)
+{
+ QPacket::setDataStreamVersion(version);
+}
+
+
+// Break in this one to process output from an asynchronous message/
+Q_DECL_EXPORT void qt_qmlDebugMessageAvailable()
+{
+}
+
+
+// Break in this one to get notified about construction and destruction of
+// interesting objects, such as QmlEngines.
+Q_DECL_EXPORT void qt_qmlDebugObjectAvailable()
+{
+}
+
+Q_DECL_EXPORT void qt_qmlDebugClearBuffer()
+{
+ responseBuffer->clear();
+}
+
+// Send a message to a service.
+Q_DECL_EXPORT bool qt_qmlDebugSendDataToService(const char *serviceName, const char *hexData)
+{
+ QByteArray msg = QByteArray::fromHex(hexData);
+
+ QQmlDebugConnector *instance = QQmlDebugConnector::instance();
+ if (!instance)
+ return false;
+
+ QQmlDebugService *recipient = instance->service(serviceName);
+ if (!recipient)
+ return false;
+
+ TRACE_PROTOCOL("Recipient: " << recipient << " got message: " << msg);
+ expectSyncronousResponse = true;
+ recipient->messageReceived(msg);
+ expectSyncronousResponse = false;
+
+ return true;
+}
+
+// Enable a service.
+Q_DECL_EXPORT bool qt_qmlDebugEnableService(const char *data)
+{
+ QQmlDebugConnector *instance = QQmlDebugConnector::instance();
+ if (!instance)
+ return false;
+
+ QString name = QString::fromLatin1(data);
+ QQmlDebugService *service = instance->service(name);
+ if (!service || service->state() == QQmlDebugService::Enabled)
+ return false;
+
+ service->stateAboutToBeChanged(QQmlDebugService::Enabled);
+ service->setState(QQmlDebugService::Enabled);
+ service->stateChanged(QQmlDebugService::Enabled);
+ return true;
+}
+
+Q_DECL_EXPORT bool qt_qmlDebugDisableService(const char *data)
+{
+ QQmlDebugConnector *instance = QQmlDebugConnector::instance();
+ if (!instance)
+ return false;
+
+ QString name = QString::fromLatin1(data);
+ QQmlDebugService *service = instance->service(name);
+ if (!service || service->state() == QQmlDebugService::Unavailable)
+ return false;
+
+ service->stateAboutToBeChanged(QQmlDebugService::Unavailable);
+ service->setState(QQmlDebugService::Unavailable);
+ service->stateChanged(QQmlDebugService::Unavailable);
+ return true;
+}
+
+quintptr qt_qmlDebugTestHooks[] = {
+ quintptr(1), // Internal Version
+ quintptr(6), // Number of entries following
+ quintptr(&qt_qmlDebugMessageBuffer),
+ quintptr(&qt_qmlDebugMessageLength),
+ quintptr(&qt_qmlDebugSendDataToService),
+ quintptr(&qt_qmlDebugEnableService),
+ quintptr(&qt_qmlDebugDisableService),
+ quintptr(&qt_qmlDebugObjectAvailable)
+};
+
+// In blocking mode, this will busy wait until the debugger sets block to false.
+Q_DECL_EXPORT void qt_qmlDebugConnectorOpen()
+{
+ TRACE_PROTOCOL("Opening native debug connector");
+
+ // FIXME: Use a dedicated hook. Startup is a safe workaround, though,
+ // as we are already beyond its only use.
+ qtHookData[QHooks::Startup] = quintptr(&qt_qmlDebugTestHooks);
+
+ while (qt_qmlDebugConnectionBlocker)
+ ;
+
+ TRACE_PROTOCOL("Opened native debug connector");
+}
+
+} // extern "C"
+
+QT_BEGIN_NAMESPACE
+
+class QQmlNativeDebugConnector : public QQmlDebugConnector
+{
+ Q_OBJECT
+
+public:
+ QQmlNativeDebugConnector();
+ ~QQmlNativeDebugConnector();
+
+ bool blockingMode() const;
+ QQmlDebugService *service(const QString &name) const;
+ void addEngine(QQmlEngine *engine);
+ void removeEngine(QQmlEngine *engine);
+ bool addService(const QString &name, QQmlDebugService *service);
+ bool removeService(const QString &name);
+ bool open(const QVariantHash &configuration);
+
+private slots:
+ void sendMessage(const QString &name, const QByteArray &message);
+ void sendMessages(const QString &name, const QList<QByteArray> &messages);
+
+private:
+ void announceObjectAvailability(const QString &objectType, QObject *object, bool available);
+
+ QVector<QQmlDebugService *> m_services;
+ bool m_blockingMode;
+};
+
+QQmlNativeDebugConnector::QQmlNativeDebugConnector()
+ : m_blockingMode(false)
+{
+ const QString args = commandLineArguments();
+ const QStringList lstjsDebugArguments = args.split(QLatin1Char(','));
+ QStringList services;
+ QStringList::const_iterator argsItEnd = lstjsDebugArguments.cend();
+ QStringList::const_iterator argsIt = lstjsDebugArguments.cbegin();
+ for (; argsIt != argsItEnd; ++argsIt) {
+ const QString strArgument = *argsIt;
+ if (strArgument == QLatin1String("block")) {
+ m_blockingMode = true;
+ } else if (strArgument == QLatin1String("native")) {
+ // Ignore. This is used to signal that this connector
+ // should be loaded and that has already happened.
+ } else if (strArgument.startsWith(QLatin1String("services:"))) {
+ services.append(strArgument.mid(9));
+ } else if (!services.isEmpty()) {
+ services.append(strArgument);
+ } else {
+ qWarning("QML Debugger: Invalid argument \"%s\" detected. Ignoring the same.",
+ qUtf8Printable(strArgument));
+ }
+ }
+ setServices(services);
+}
+
+QQmlNativeDebugConnector::~QQmlNativeDebugConnector()
+{
+ foreach (QQmlDebugService *service, m_services) {
+ service->stateAboutToBeChanged(QQmlDebugService::NotConnected);
+ service->setState(QQmlDebugService::NotConnected);
+ service->stateChanged(QQmlDebugService::NotConnected);
+ }
+}
+
+bool QQmlNativeDebugConnector::blockingMode() const
+{
+ return m_blockingMode;
+}
+
+QQmlDebugService *QQmlNativeDebugConnector::service(const QString &name) const
+{
+ for (QVector<QQmlDebugService *>::ConstIterator i = m_services.begin(); i != m_services.end();
+ ++i) {
+ if ((*i)->name() == name)
+ return *i;
+ }
+ return 0;
+}
+
+void QQmlNativeDebugConnector::addEngine(QQmlEngine *engine)
+{
+ TRACE_PROTOCOL("Add engine to connector:" << engine);
+ foreach (QQmlDebugService *service, m_services)
+ service->engineAboutToBeAdded(engine);
+
+ announceObjectAvailability(QLatin1String("qmlengine"), engine, true);
+
+ foreach (QQmlDebugService *service, m_services)
+ service->engineAdded(engine);
+}
+
+void QQmlNativeDebugConnector::removeEngine(QQmlEngine *engine)
+{
+ TRACE_PROTOCOL("Remove engine from connector:" << engine);
+ foreach (QQmlDebugService *service, m_services)
+ service->engineAboutToBeRemoved(engine);
+
+ announceObjectAvailability(QLatin1String("qmlengine"), engine, false);
+
+ foreach (QQmlDebugService *service, m_services)
+ service->engineRemoved(engine);
+}
+
+void QQmlNativeDebugConnector::announceObjectAvailability(const QString &objectType,
+ QObject *object, bool available)
+{
+ QJsonObject ob;
+ ob.insert(QLatin1String("objecttype"), objectType);
+ ob.insert(QLatin1String("object"), QString::number(quintptr(object)));
+ ob.insert(QLatin1String("available"), available);
+ QJsonDocument doc;
+ doc.setObject(ob);
+
+ QByteArray ba = doc.toJson(QJsonDocument::Compact);
+ qt_qmlDebugMessageBuffer = ba.constData();
+ qt_qmlDebugMessageLength = ba.size();
+ TRACE_PROTOCOL("Reporting engine availabilty");
+ qt_qmlDebugObjectAvailable(); // Trigger native breakpoint.
+}
+
+bool QQmlNativeDebugConnector::addService(const QString &name, QQmlDebugService *service)
+{
+ TRACE_PROTOCOL("Add service to connector: " << qPrintable(name) << service);
+ for (QVector<QQmlDebugService *>::ConstIterator i = m_services.begin(); i != m_services.end();
+ ++i) {
+ if ((*i)->name() == name)
+ return false;
+ }
+
+ connect(service, &QQmlDebugService::messageToClient,
+ this, &QQmlNativeDebugConnector::sendMessage);
+ connect(service, &QQmlDebugService::messagesToClient,
+ this, &QQmlNativeDebugConnector::sendMessages);
+
+ service->setState(QQmlDebugService::Unavailable);
+
+ m_services << service;
+ return true;
+}
+
+bool QQmlNativeDebugConnector::removeService(const QString &name)
+{
+ for (QVector<QQmlDebugService *>::Iterator i = m_services.begin(); i != m_services.end(); ++i) {
+ if ((*i)->name() == name) {
+ QQmlDebugService *service = *i;
+ m_services.erase(i);
+ service->setState(QQmlDebugService::NotConnected);
+
+ disconnect(service, &QQmlDebugService::messagesToClient,
+ this, &QQmlNativeDebugConnector::sendMessages);
+ disconnect(service, &QQmlDebugService::messageToClient,
+ this, &QQmlNativeDebugConnector::sendMessage);
+
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QQmlNativeDebugConnector::open(const QVariantHash &configuration)
+{
+ m_blockingMode = configuration.value(QStringLiteral("block"), m_blockingMode).toBool();
+ qt_qmlDebugConnectionBlocker = m_blockingMode;
+ qt_qmlDebugConnectorOpen();
+ return true;
+}
+
+void QQmlNativeDebugConnector::sendMessage(const QString &name, const QByteArray &message)
+{
+ (*responseBuffer) += name.toUtf8() + ' ' + QByteArray::number(message.size()) + ' ' + message;
+ qt_qmlDebugMessageBuffer = responseBuffer->constData();
+ qt_qmlDebugMessageLength = responseBuffer->size();
+ // Responses are allowed to accumulate, the buffer will be cleared by
+ // separate calls to qt_qmlDebugClearBuffer() once the synchronous
+ // function return ('if' branch below) or in the native breakpoint handler
+ // ('else' branch below).
+ if (expectSyncronousResponse) {
+ TRACE_PROTOCOL("Expected synchronous response in " << message);
+ // Do not trigger the native breakpoint on qt_qmlDebugMessageFromService.
+ } else {
+ TRACE_PROTOCOL("Found asynchronous message in " << message);
+ // Trigger native breakpoint.
+ qt_qmlDebugMessageAvailable();
+ }
+}
+
+void QQmlNativeDebugConnector::sendMessages(const QString &name, const QList<QByteArray> &messages)
+{
+ for (int i = 0; i != messages.size(); ++i)
+ sendMessage(name, messages.at(i));
+}
+
+class QQmlNativeDebugConnectorFactory : public QQmlDebugConnectorFactory
+{
+ Q_OBJECT
+
+ Q_PLUGIN_METADATA(IID QQmlDebugConnectorFactory_iid FILE "qqmlnativedebugconnector.json")
+
+public:
+ QQmlNativeDebugConnectorFactory() {}
+
+ QQmlDebugConnector *create(const QString &key)
+ {
+ return key == QLatin1String("QQmlNativeDebugConnector") ? new QQmlNativeDebugConnector : 0;
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "qqmlnativedebugconnector.moc"
diff --git a/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.json b/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.json
new file mode 100644
index 0000000000..925e6a665c
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "QQmlNativeDebugConnector" ]
+}
diff --git a/src/plugins/qmltooling/qmltooling.pro b/src/plugins/qmltooling/qmltooling.pro
index 75da89f3e8..5b39747674 100644
--- a/src/plugins/qmltooling/qmltooling.pro
+++ b/src/plugins/qmltooling/qmltooling.pro
@@ -2,6 +2,7 @@ TEMPLATE = subdirs
# Connectors
SUBDIRS += \
+ qmldbg_native \
qmldbg_server \
qmldbg_local \
qmldbg_tcp