aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@theqtcompany.com>2015-07-22 18:50:34 +0200
committerUlf Hermann <ulf.hermann@theqtcompany.com>2015-08-10 10:05:14 +0000
commita2ccdd33888cb0eb4516089d3418dd01c8cca25c (patch)
tree400d8d1de2223812f9b8c0106e69c6ef9656c0a3
parentbf5db2bbc1da83a9930832e628e65ec64cd4b831 (diff)
Move DataCollector into debugger plugin
The data collector and all the jobs it uses to interact with the engine are only used from the debugger plugin. We can as well move them there. Change-Id: Ia48251f08b48c7e1e607b8ae2a3d1de29f80f742 Reviewed-by: Erik Verbruggen <erik.verbruggen@theqtcompany.com>
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro6
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp452
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h178
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp1
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp76
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h11
-rw-r--r--src/qml/jsruntime/qv4context_p.h2
-rw-r--r--src/qml/jsruntime/qv4debugging.cpp594
-rw-r--r--src/qml/jsruntime/qv4debugging_p.h88
-rw-r--r--tests/auto/qml/qv4debugger/qv4debugger.pro10
-rw-r--r--tests/auto/qml/qv4debugger/tst_qv4debugger.cpp38
11 files changed, 768 insertions, 688 deletions
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro b/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro
index dd0c9a174f..d860328dc8 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro
+++ b/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro
@@ -11,7 +11,8 @@ SOURCES += \
$$PWD/qqmlenginedebugservice.cpp \
$$PWD/qqmlwatcher.cpp \
$$PWD/qv4debugservice.cpp \
- $$PWD/qv4debuggeragent.cpp
+ $$PWD/qv4debuggeragent.cpp \
+ $$PWD/qv4datacollector.cpp
HEADERS += \
$$PWD/../shared/qqmlconfigurabledebugservice.h \
@@ -20,7 +21,8 @@ HEADERS += \
$$PWD/qqmlenginedebugservice.h \
$$PWD/qqmlwatcher.h \
$$PWD/qv4debugservice.h \
- $$PWD/qv4debuggeragent.h
+ $$PWD/qv4debuggeragent.h \
+ $$PWD/qv4datacollector.h
INCLUDEPATH += $$PWD \
$$PWD/../shared
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
new file mode 100644
index 0000000000..64ee5c3b96
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
@@ -0,0 +1,452 @@
+/****************************************************************************
+**
+** 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 "qv4datacollector.h"
+
+#include <private/qv4script_p.h>
+#include <private/qv4string_p.h>
+#include <private/qv4objectiterator_p.h>
+#include <private/qv4identifier_p.h>
+
+#include <QtCore/qjsonarray.h>
+
+QT_BEGIN_NAMESPACE
+
+QV4::Heap::CallContext *QV4DataCollector::findContext(QV4::Heap::ExecutionContext *ctxt, int frame)
+{
+ if (!ctxt)
+ return 0;
+
+ QV4::Scope scope(ctxt->engine);
+ QV4::ScopedContext ctx(scope, ctxt);
+ while (ctx) {
+ QV4::CallContext *cCtxt = ctx->asCallContext();
+ if (cCtxt && cCtxt->d()->function) {
+ if (frame < 1)
+ return cCtxt->d();
+ --frame;
+ }
+ ctx = ctx->d()->parent;
+ }
+
+ return 0;
+}
+
+QV4::Heap::CallContext *QV4DataCollector::findScope(QV4::Heap::ExecutionContext *ctxt, int scope)
+{
+ if (!ctxt)
+ return 0;
+
+ QV4::Scope s(ctxt->engine);
+ QV4::ScopedContext ctx(s, ctxt);
+ for (; scope > 0 && ctx; --scope)
+ ctx = ctx->d()->outer;
+
+ return (ctx && ctx->d()) ? ctx->asCallContext()->d() : 0;
+}
+
+QVector<QV4::Heap::ExecutionContext::ContextType> QV4DataCollector::getScopeTypes(
+ QV4::ExecutionEngine *engine, int frame)
+{
+ QVector<QV4::Heap::ExecutionContext::ContextType> types;
+
+ QV4::Scope scope(engine);
+ QV4::Scoped<QV4::CallContext> sctxt(scope, findContext(engine->currentContext(), frame));
+ if (!sctxt || sctxt->d()->type < QV4::Heap::ExecutionContext::Type_QmlContext)
+ return types;
+
+ QV4::ScopedContext it(scope, sctxt->d());
+ for (; it; it = it->d()->outer)
+ types.append(it->d()->type);
+
+ return types;
+}
+
+
+QV4DataCollector::QV4DataCollector(QV4::ExecutionEngine *engine)
+ : m_engine(engine), m_collectedRefs(Q_NULLPTR)
+{
+ values.set(engine, engine->newArrayObject());
+}
+
+QV4DataCollector::~QV4DataCollector()
+{
+}
+
+void QV4DataCollector::collect(const QV4::ScopedValue &value)
+{
+ if (m_collectedRefs)
+ m_collectedRefs->append(addRef(value));
+}
+
+QJsonObject QV4DataCollector::lookupRef(Ref ref)
+{
+ QJsonObject dict;
+ if (lookupSpecialRef(ref, &dict))
+ return dict;
+
+ dict.insert(QStringLiteral("handle"), qint64(ref));
+
+ QV4::Scope scope(engine());
+ QV4::ScopedValue value(scope, getValue(ref));
+ switch (value->type()) {
+ case QV4::Value::Empty_Type:
+ Q_ASSERT(!"empty Value encountered");
+ break;
+ case QV4::Value::Undefined_Type:
+ dict.insert(QStringLiteral("type"), QStringLiteral("undefined"));
+ break;
+ case QV4::Value::Null_Type:
+ dict.insert(QStringLiteral("type"), QStringLiteral("null"));
+ break;
+ case QV4::Value::Boolean_Type:
+ dict.insert(QStringLiteral("type"), QStringLiteral("boolean"));
+ dict.insert(QStringLiteral("value"), value->booleanValue() ? QStringLiteral("true")
+ : QStringLiteral("false"));
+ break;
+ case QV4::Value::Managed_Type:
+ if (QV4::String *s = value->as<QV4::String>()) {
+ dict.insert(QStringLiteral("type"), QStringLiteral("string"));
+ dict.insert(QStringLiteral("value"), s->toQString());
+ } else if (QV4::Object *o = value->as<QV4::Object>()) {
+ dict.insert(QStringLiteral("type"), QStringLiteral("object"));
+ dict.insert(QStringLiteral("properties"), collectProperties(o));
+ } else {
+ Q_UNREACHABLE();
+ }
+ break;
+ case QV4::Value::Integer_Type:
+ dict.insert(QStringLiteral("type"), QStringLiteral("number"));
+ dict.insert(QStringLiteral("value"), value->integerValue());
+ break;
+ default: // double
+ dict.insert(QStringLiteral("type"), QStringLiteral("number"));
+ dict.insert(QStringLiteral("value"), value->doubleValue());
+ break;
+ }
+
+ return dict;
+}
+
+QV4DataCollector::Ref QV4DataCollector::addFunctionRef(const QString &functionName)
+{
+ Ref ref = addRef(QV4::Primitive::emptyValue(), false);
+
+ QJsonObject dict;
+ dict.insert(QStringLiteral("handle"), qint64(ref));
+ dict.insert(QStringLiteral("type"), QStringLiteral("function"));
+ dict.insert(QStringLiteral("className"), QStringLiteral("Function"));
+ dict.insert(QStringLiteral("name"), functionName);
+ specialRefs.insert(ref, dict);
+
+ return ref;
+}
+
+QV4DataCollector::Ref QV4DataCollector::addScriptRef(const QString &scriptName)
+{
+ 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);
+ specialRefs.insert(ref, dict);
+
+ return ref;
+}
+
+void QV4DataCollector::collectScope(QJsonObject *dict, QV4::Debugging::Debugger *debugger,
+ int frameNr, int scopeNr)
+{
+ QStringList names;
+
+ Refs refs;
+ if (debugger->state() == QV4::Debugging::Debugger::Paused) {
+ RefHolder holder(this, &refs);
+ ArgumentCollectJob argumentsJob(m_engine, this, &names, frameNr, scopeNr);
+ debugger->runInEngine(&argumentsJob);
+ LocalCollectJob localsJob(m_engine, this, &names, frameNr, scopeNr);
+ debugger->runInEngine(&localsJob);
+ }
+
+ QV4::Scope scope(engine());
+ QV4::ScopedObject scopeObject(scope, engine()->newObject());
+
+ Q_ASSERT(names.size() == refs.size());
+ for (int i = 0, ei = refs.size(); i != ei; ++i)
+ scopeObject->put(engine(), names.at(i),
+ QV4::Value::fromReturnedValue(getValue(refs.at(i))));
+
+ Ref scopeObjectRef = addRef(scopeObject);
+ dict->insert(QStringLiteral("ref"), qint64(scopeObjectRef));
+ if (m_collectedRefs)
+ m_collectedRefs->append(scopeObjectRef);
+}
+
+QV4DataCollector::Ref QV4DataCollector::addRef(QV4::Value value, bool deduplicate)
+{
+ class ExceptionStateSaver
+ {
+ quint32 *hasExceptionLoc;
+ quint32 hadException;
+
+ public:
+ ExceptionStateSaver(QV4::ExecutionEngine *engine)
+ : hasExceptionLoc(&engine->hasException)
+ , hadException(false)
+ { std::swap(*hasExceptionLoc, hadException); }
+
+ ~ExceptionStateSaver()
+ { std::swap(*hasExceptionLoc, hadException); }
+ };
+
+ // if we wouldn't do this, the putIndexed won't work.
+ ExceptionStateSaver resetExceptionState(engine());
+ QV4::Scope scope(engine());
+ QV4::ScopedObject array(scope, values.value());
+ if (deduplicate) {
+ for (Ref i = 0; i < array->getLength(); ++i) {
+ if (array->getIndexed(i) == value.rawValue())
+ return i;
+ }
+ }
+ Ref ref = array->getLength();
+ array->putIndexed(ref, value);
+ Q_ASSERT(array->getLength() - 1 == ref);
+ return ref;
+}
+
+QV4::ReturnedValue QV4DataCollector::getValue(Ref ref)
+{
+ QV4::Scope scope(engine());
+ QV4::ScopedObject array(scope, values.value());
+ Q_ASSERT(ref < array->getLength());
+ return array->getIndexed(ref, Q_NULLPTR);
+}
+
+bool QV4DataCollector::lookupSpecialRef(Ref ref, QJsonObject *dict)
+{
+ SpecialRefs::const_iterator it = specialRefs.find(ref);
+ if (it == specialRefs.end())
+ return false;
+
+ *dict = it.value();
+ return true;
+}
+
+QJsonArray QV4DataCollector::collectProperties(QV4::Object *object)
+{
+ QJsonArray res;
+
+ QV4::Scope scope(engine());
+ QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
+ QV4::ScopedValue name(scope);
+ QV4::ScopedValue value(scope);
+ while (true) {
+ QV4::Value v;
+ name = it.nextPropertyNameAsString(&v);
+ if (name->isNull())
+ break;
+ QString key = name->toQStringNoThrow();
+ value = v;
+ res.append(collectAsJson(key, value));
+ }
+
+ return res;
+}
+
+QJsonObject QV4DataCollector::collectAsJson(const QString &name, const QV4::ScopedValue &value)
+{
+ QJsonObject dict;
+ if (!name.isNull())
+ dict.insert(QStringLiteral("name"), name);
+ Ref ref = addRef(value);
+ dict.insert(QStringLiteral("ref"), qint64(ref));
+ if (m_collectedRefs)
+ m_collectedRefs->append(ref);
+
+ // TODO: enable this when creator can handle it.
+ if (false) {
+ if (value->isManaged() && !value->isString()) {
+ QV4::Scope scope(engine());
+ QV4::ScopedObject obj(scope, value->as<QV4::Object>());
+ dict.insert(QStringLiteral("propertycount"), qint64(obj->getLength()));
+ }
+ }
+
+ return dict;
+}
+
+ExpressionEvalJob::ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr,
+ const QString &expression,
+ QV4DataCollector *collector)
+ : JavaScriptJob(engine, frameNr, expression)
+ , collector(collector)
+{
+}
+
+void ExpressionEvalJob::handleResult(QV4::ScopedValue &result)
+{
+ collector->collect(result);
+}
+
+GatherSourcesJob::GatherSourcesJob(QV4::ExecutionEngine *engine, int seq)
+ : engine(engine)
+ , seq(seq)
+{}
+
+void GatherSourcesJob::run()
+{
+ QStringList sources;
+
+ foreach (QV4::CompiledData::CompilationUnit *unit, engine->compilationUnits) {
+ QString fileName = unit->fileName();
+ if (!fileName.isEmpty())
+ sources.append(fileName);
+ }
+
+ QV4::Debugging::Debugger *debugger = engine->debugger;
+ emit debugger->sourcesCollected(debugger, sources, seq);
+}
+
+ArgumentCollectJob::ArgumentCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector,
+ QStringList *names, int frameNr, int scopeNr)
+ : engine(engine)
+ , collector(collector)
+ , names(names)
+ , frameNr(frameNr)
+ , scopeNr(scopeNr)
+{}
+
+void ArgumentCollectJob::run()
+{
+ if (frameNr < 0)
+ return;
+
+ QV4::Scope scope(engine);
+ QV4::Scoped<QV4::CallContext> ctxt(
+ scope, QV4DataCollector::findScope(
+ QV4DataCollector::findContext(engine->currentContext(), frameNr), scopeNr));
+ if (!ctxt)
+ return;
+
+ 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);
+ collector->collect(v);
+ }
+}
+
+LocalCollectJob::LocalCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector,
+ QStringList *names, int frameNr, int scopeNr)
+ : engine(engine)
+ , collector(collector)
+ , names(names)
+ , frameNr(frameNr)
+ , scopeNr(scopeNr)
+{}
+
+void LocalCollectJob::run()
+{
+ if (frameNr < 0)
+ return;
+
+ QV4::Scope scope(engine);
+ QV4::Scoped<QV4::CallContext> ctxt(
+ scope, QV4DataCollector::findScope(
+ QV4DataCollector::findContext(engine->currentContext(), frameNr), scopeNr));
+ if (!ctxt)
+ return;
+
+ QV4::ScopedValue v(scope);
+ 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];
+ collector->collect(v);
+ }
+}
+
+ThisCollectJob::ThisCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector,
+ int frameNr, bool *foundThis)
+ : engine(engine)
+ , collector(collector)
+ , frameNr(frameNr)
+ , foundThis(foundThis)
+{}
+
+void ThisCollectJob::run()
+{
+ *foundThis = myRun();
+}
+
+bool ThisCollectJob::myRun()
+{
+ QV4::Scope scope(engine);
+ QV4::ScopedContext ctxt(scope, QV4DataCollector::findContext(engine->currentContext(),
+ frameNr));
+ while (ctxt) {
+ if (QV4::CallContext *cCtxt = ctxt->asCallContext())
+ if (cCtxt->d()->activation)
+ break;
+ ctxt = ctxt->d()->outer;
+ }
+
+ if (!ctxt)
+ return false;
+
+ QV4::ScopedValue o(scope, ctxt->asCallContext()->d()->activation);
+ collector->collect(o);
+ return true;
+}
+
+ExceptionCollectJob::ExceptionCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector)
+ : engine(engine)
+ , collector(collector)
+{}
+
+void ExceptionCollectJob::run()
+{
+ QV4::Scope scope(engine);
+ QV4::ScopedValue v(scope, *engine->exceptionValue);
+ collector->collect(v);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h
new file mode 100644
index 0000000000..c91b77cb93
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** 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 QV4DATACOLLECTOR_H
+#define QV4DATACOLLECTOR_H
+
+#include <private/qv4engine_p.h>
+#include <private/qv4debugging_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QV4DataCollector
+{
+public:
+ typedef uint Ref;
+ typedef QVector<uint> Refs;
+
+ static QV4::Heap::CallContext *findContext(QV4::Heap::ExecutionContext *ctxt, int frame);
+ static QV4::Heap::CallContext *findScope(QV4::Heap::ExecutionContext *ctxt, int scope);
+ static QVector<QV4::Heap::ExecutionContext::ContextType> getScopeTypes(
+ QV4::ExecutionEngine *engine, int frame);
+
+ QV4DataCollector(QV4::ExecutionEngine *engine);
+ ~QV4DataCollector();
+
+ void collect(const QV4::ScopedValue &value);
+
+ QJsonObject lookupRef(Ref ref);
+
+ Ref addFunctionRef(const QString &functionName);
+ Ref addScriptRef(const QString &scriptName);
+
+ void collectScope(QJsonObject *dict, QV4::Debugging::Debugger *debugger, int frameNr,
+ int scopeNr);
+
+ QV4::ExecutionEngine *engine() const { return m_engine; }
+
+private:
+ friend class RefHolder;
+
+ Ref addRef(QV4::Value value, bool deduplicate = true);
+ QV4::ReturnedValue getValue(Ref ref);
+ bool lookupSpecialRef(Ref ref, QJsonObject *dict);
+
+ QJsonArray collectProperties(QV4::Object *object);
+ QJsonObject collectAsJson(const QString &name, const QV4::ScopedValue &value);
+ void collectArgumentsInContext();
+
+ QV4::ExecutionEngine *m_engine;
+ Refs *m_collectedRefs;
+ QV4::PersistentValue values;
+ typedef QHash<Ref, QJsonObject> SpecialRefs;
+ SpecialRefs specialRefs;
+};
+
+class RefHolder {
+public:
+ RefHolder(QV4DataCollector *collector, QV4DataCollector::Refs *target) :
+ m_collector(collector), m_previousRefs(collector->m_collectedRefs)
+ {
+ m_collector->m_collectedRefs = target;
+ }
+
+ ~RefHolder()
+ {
+ std::swap(m_collector->m_collectedRefs, m_previousRefs);
+ }
+
+private:
+ QV4DataCollector *m_collector;
+ QV4DataCollector::Refs *m_previousRefs;
+};
+
+class ExpressionEvalJob: public QV4::Debugging::Debugger::JavaScriptJob
+{
+ QV4DataCollector *collector;
+
+public:
+ ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr, const QString &expression,
+ QV4DataCollector *collector);
+ virtual void handleResult(QV4::ScopedValue &result);
+};
+
+class GatherSourcesJob: public QV4::Debugging::Debugger::Job
+{
+ QV4::ExecutionEngine *engine;
+ const int seq;
+
+public:
+ GatherSourcesJob(QV4::ExecutionEngine *engine, int seq);
+ void run();
+};
+
+class ArgumentCollectJob: public QV4::Debugging::Debugger::Job
+{
+ QV4::ExecutionEngine *engine;
+ QV4DataCollector *collector;
+ QStringList *names;
+ int frameNr;
+ int scopeNr;
+
+public:
+ ArgumentCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector,
+ QStringList *names, int frameNr, int scopeNr);
+ void run();
+};
+
+class LocalCollectJob: public QV4::Debugging::Debugger::Job
+{
+ QV4::ExecutionEngine *engine;
+ QV4DataCollector *collector;
+ QStringList *names;
+ int frameNr;
+ int scopeNr;
+
+public:
+ LocalCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector, QStringList *names,
+ int frameNr, int scopeNr);
+ void run();
+};
+
+class ThisCollectJob: public QV4::Debugging::Debugger::Job
+{
+ QV4::ExecutionEngine *engine;
+ QV4DataCollector *collector;
+ int frameNr;
+ bool *foundThis;
+
+public:
+ ThisCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector, int frameNr,
+ bool *foundThis);
+ void run();
+ bool myRun();
+};
+
+class ExceptionCollectJob: public QV4::Debugging::Debugger::Job
+{
+ QV4::ExecutionEngine *engine;
+ QV4DataCollector *collector;
+
+public:
+ ExceptionCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector);
+ void run();
+};
+
+QT_END_NAMESPACE
+
+#endif // QV4DATACOLLECTOR_H
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
index 15421f4f5d..7f22b16e45 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
@@ -33,6 +33,7 @@
#include "qv4debuggeragent.h"
#include "qv4debugservice.h"
+#include "qv4datacollector.h"
#include <QtCore/qjsonobject.h>
#include <QtCore/qjsonarray.h>
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
index 21bfe97808..6b68f9518e 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
@@ -506,7 +506,9 @@ public:
}
// do it:
- debugService->debuggerAgent.firstDebugger()->gatherSources(requestSequenceNr());
+ QV4::Debugging::Debugger *debugger = debugService->debuggerAgent.firstDebugger();
+ GatherSourcesJob job(debugger->engine(), requestSequenceNr());
+ debugger->runInEngine(&job);
// response will be send by
}
@@ -555,10 +557,13 @@ public:
QV4::Debugging::Debugger *debugger = debugService->debuggerAgent.firstDebugger();
Q_ASSERT(debugger->state() == QV4::Debugging::Debugger::Paused);
- QV4::Debugging::DataCollector *collector = debugService->collector();
- QV4::Debugging::DataCollector::Refs refs;
- QV4::Debugging::RefHolder holder(collector, &refs);
- debugger->evaluateExpression(frame, expression, collector);
+ QV4DataCollector *collector = debugService->collector();
+ QV4DataCollector::Refs refs;
+ RefHolder holder(collector, &refs);
+ Q_ASSERT(debugger->state() == QV4::Debugging::Debugger::Paused);
+
+ ExpressionEvalJob job(debugger->engine(), frame, expression, collector);
+ debugger->runInEngine(&job);
Q_ASSERT(refs.size() == 1);
@@ -762,13 +767,13 @@ void QV4DebugServiceImpl::send(QJsonObject v8Payload)
void QV4DebugServiceImpl::clearHandles(QV4::ExecutionEngine *engine)
{
- theCollector.reset(new QV4::Debugging::DataCollector(engine));
+ theCollector.reset(new QV4DataCollector(engine));
}
QJsonObject QV4DebugServiceImpl::buildFrame(const QV4::StackFrame &stackFrame, int frameNr,
QV4::Debugging::Debugger *debugger)
{
- QV4::Debugging::DataCollector::Ref ref;
+ QV4DataCollector::Ref ref;
QJsonObject frame;
frame[QLatin1String("index")] = frameNr;
@@ -783,24 +788,28 @@ QJsonObject QV4DebugServiceImpl::buildFrame(const QV4::StackFrame &stackFrame, i
if (stackFrame.column >= 0)
frame[QLatin1String("column")] = stackFrame.column;
- {
- QV4::Debugging::RefHolder holder(theCollector.data(), &collectedRefs);
- if (debugger->collectThisInContext(theCollector.data(), frameNr))
- frame[QLatin1String("receiver")] = toRef(collectedRefs.last());
- }
-
QJsonArray scopes;
- // Only type and index are used by Qt Creator, so we keep it easy:
- QVector<QV4::Heap::ExecutionContext::ContextType> scopeTypes = debugger->getScopeTypes(frameNr);
- for (int i = 0, ei = scopeTypes.count(); i != ei; ++i) {
- int type = encodeScopeType(scopeTypes[i]);
- if (type == -1)
- continue;
+ if (debugger->state() == QV4::Debugging::Debugger::Paused) {
+ RefHolder holder(theCollector.data(), &collectedRefs);
+ bool foundThis = false;
+ ThisCollectJob job(debugger->engine(), theCollector.data(), frameNr, &foundThis);
+ debugger->runInEngine(&job);
+ if (foundThis)
+ frame[QLatin1String("receiver")] = toRef(collectedRefs.last());
- QJsonObject scope;
- scope[QLatin1String("index")] = i;
- scope[QLatin1String("type")] = type;
- scopes.push_back(scope);
+ // Only type and index are used by Qt Creator, so we keep it easy:
+ QVector<QV4::Heap::ExecutionContext::ContextType> scopeTypes =
+ QV4DataCollector::getScopeTypes(debugger->engine(), frameNr);
+ for (int i = 0, ei = scopeTypes.count(); i != ei; ++i) {
+ int type = encodeScopeType(scopeTypes[i]);
+ if (type == -1)
+ continue;
+
+ QJsonObject scope;
+ scope[QLatin1String("index")] = i;
+ scope[QLatin1String("type")] = type;
+ scopes.push_back(scope);
+ }
}
frame[QLatin1String("scopes")] = scopes;
@@ -835,11 +844,16 @@ QJsonObject QV4DebugServiceImpl::buildScope(int frameNr, int scopeNr,
QJsonObject scope;
QJsonObject object;
- QV4::Debugging::RefHolder holder(theCollector.data(), &collectedRefs);
+ RefHolder holder(theCollector.data(), &collectedRefs);
theCollector->collectScope(&object, debugger, frameNr, scopeNr);
- QVector<QV4::Heap::ExecutionContext::ContextType> scopeTypes = debugger->getScopeTypes(frameNr);
- scope[QLatin1String("type")] = encodeScopeType(scopeTypes[scopeNr]);
+ if (debugger->state() == QV4::Debugging::Debugger::Paused) {
+ QVector<QV4::Heap::ExecutionContext::ContextType> scopeTypes =
+ QV4DataCollector::getScopeTypes(debugger->engine(), frameNr);
+ scope[QLatin1String("type")] = encodeScopeType(scopeTypes[scopeNr]);
+ } else {
+ scope[QLatin1String("type")] = -1;
+ }
scope[QLatin1String("index")] = scopeNr;
scope[QLatin1String("frameIndex")] = frameNr;
scope[QLatin1String("object")] = object;
@@ -847,9 +861,9 @@ QJsonObject QV4DebugServiceImpl::buildScope(int frameNr, int scopeNr,
return scope;
}
-QJsonValue QV4DebugServiceImpl::lookup(QV4::Debugging::DataCollector::Ref refId)
+QJsonValue QV4DebugServiceImpl::lookup(QV4DataCollector::Ref refId)
{
- QV4::Debugging::RefHolder holder(theCollector.data(), &collectedRefs);
+ RefHolder holder(theCollector.data(), &collectedRefs);
return theCollector->lookupRef(refId);
}
@@ -858,7 +872,7 @@ QJsonArray QV4DebugServiceImpl::buildRefs()
QJsonArray refs;
std::sort(collectedRefs.begin(), collectedRefs.end());
for (int i = 0, ei = collectedRefs.size(); i != ei; ++i) {
- QV4::Debugging::DataCollector::Ref ref = collectedRefs.at(i);
+ QV4DataCollector::Ref ref = collectedRefs.at(i);
if (i > 0 && ref == collectedRefs.at(i - 1))
continue;
refs.append(lookup(ref));
@@ -868,14 +882,14 @@ QJsonArray QV4DebugServiceImpl::buildRefs()
return refs;
}
-QJsonValue QV4DebugServiceImpl::toRef(QV4::Debugging::DataCollector::Ref ref)
+QJsonValue QV4DebugServiceImpl::toRef(QV4DataCollector::Ref ref)
{
QJsonObject dict;
dict.insert(QStringLiteral("ref"), qint64(ref));
return dict;
}
-QV4::Debugging::DataCollector *QV4DebugServiceImpl::collector() const
+QV4DataCollector *QV4DebugServiceImpl::collector() const
{
return theCollector.data();
}
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
index 6e5113600d..c80ad78cc8 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
@@ -47,6 +47,7 @@
#include "qqmlconfigurabledebugservice.h"
#include "qv4debuggeragent.h"
+#include "qv4datacollector.h"
#include <private/qqmldebugserviceinterfaces_p.h>
#include <private/qv4debugging_p.h>
@@ -77,8 +78,8 @@ public:
QJsonObject buildScope(int frameNr, int scopeNr, QV4::Debugging::Debugger *debugger);
QJsonArray buildRefs();
- QJsonValue lookup(QV4::Debugging::DataCollector::Ref refId);
- QJsonValue toRef(QV4::Debugging::DataCollector::Ref ref);
+ QJsonValue lookup(QV4DataCollector::Ref refId);
+ QJsonValue toRef(QV4DataCollector::Ref ref);
QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr,
QV4::Debugging::Debugger *debugger);
@@ -87,7 +88,7 @@ public:
void clearHandles(QV4::ExecutionEngine *engine);
- QV4::Debugging::DataCollector *collector() const;
+ QV4DataCollector *collector() const;
QV4DebuggerAgent debuggerAgent;
protected:
@@ -109,9 +110,9 @@ private:
static int debuggerIndex;
static int sequence;
const int version;
- QV4::Debugging::DataCollector::Refs collectedRefs;
+ QV4DataCollector::Refs collectedRefs;
- QScopedPointer<QV4::Debugging::DataCollector> theCollector;
+ QScopedPointer<QV4DataCollector> theCollector;
int theSelectedFrame;
void addHandler(V8CommandHandler* handler);
diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h
index c698a6c9c3..1e051ed850 100644
--- a/src/qml/jsruntime/qv4context_p.h
+++ b/src/qml/jsruntime/qv4context_p.h
@@ -181,7 +181,7 @@ struct Q_QML_EXPORT ExecutionContext : public Managed
}
};
-struct CallContext : public ExecutionContext
+struct Q_QML_EXPORT CallContext : public ExecutionContext
{
V4_MANAGED(CallContext, ExecutionContext)
diff --git a/src/qml/jsruntime/qv4debugging.cpp b/src/qml/jsruntime/qv4debugging.cpp
index 6f3e48352e..ceeef80b9f 100644
--- a/src/qml/jsruntime/qv4debugging.cpp
+++ b/src/qml/jsruntime/qv4debugging.cpp
@@ -54,60 +54,49 @@ QT_BEGIN_NAMESPACE
using namespace QV4;
using namespace QV4::Debugging;
-namespace {
-class JavaScriptJob: public Debugger::Job
-{
- QV4::ExecutionEngine *engine;
- int frameNr;
- const QString &script;
-
-public:
- JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr, const QString &script)
- : engine(engine)
- , frameNr(frameNr)
- , script(script)
- {}
+Debugger::JavaScriptJob::JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr,
+ const QString &script)
+ : engine(engine)
+ , frameNr(frameNr)
+ , script(script)
+{}
- void run()
- {
- Scope scope(engine);
+void Debugger::JavaScriptJob::run()
+{
+ Scope scope(engine);
- ExecutionContextSaver saver(scope, engine->currentContext());
+ ExecutionContextSaver saver(scope, engine->currentContext());
- if (frameNr > 0) {
- Value *savedContexts = scope.alloc(frameNr);
- for (int i = 0; i < frameNr; ++i) {
- savedContexts[i] = engine->currentContext();
- engine->popContext();
- }
+ if (frameNr > 0) {
+ Value *savedContexts = scope.alloc(frameNr);
+ for (int i = 0; i < frameNr; ++i) {
+ savedContexts[i] = engine->currentContext();
+ engine->popContext();
}
-
- ScopedContext ctx(scope, engine->currentContext());
- QV4::Script script(ctx, this->script);
- 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 (!scope.engine->hasException)
- result = script.run();
- if (scope.engine->hasException)
- result = scope.engine->catchException();
- handleResult(result);
}
-protected:
- virtual void handleResult(QV4::ScopedValue &result) = 0;
-};
+ ScopedContext ctx(scope, engine->currentContext());
+ QV4::Script script(ctx, this->script);
+ 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 (!scope.engine->hasException)
+ result = script.run();
+ if (scope.engine->hasException)
+ result = scope.engine->catchException();
+ handleResult(result);
+}
-class EvalJob: public JavaScriptJob
+class EvalJob: public Debugger::JavaScriptJob
{
bool result;
public:
EvalJob(QV4::ExecutionEngine *engine, const QString &script)
- : JavaScriptJob(engine, /*frameNr*/-1, script)
+ : Debugger::JavaScriptJob(engine, /*frameNr*/-1, script)
, result(false)
{}
@@ -122,265 +111,6 @@ public:
}
};
-class ExpressionEvalJob: public JavaScriptJob
-{
- QV4::Debugging::DataCollector *collector;
-
-public:
- ExpressionEvalJob(ExecutionEngine *engine, int frameNr, const QString &expression,
- QV4::Debugging::DataCollector *collector)
- : JavaScriptJob(engine, frameNr, expression)
- , collector(collector)
- {
- }
-
- virtual void handleResult(QV4::ScopedValue &result)
- {
- collector->collect(result);
- }
-};
-
-class GatherSourcesJob: public Debugger::Job
-{
- QV4::ExecutionEngine *engine;
- const int seq;
-
-public:
- GatherSourcesJob(QV4::ExecutionEngine *engine, int seq)
- : engine(engine)
- , seq(seq)
- {}
-
- ~GatherSourcesJob() {}
-
- void run()
- {
- QStringList sources;
-
- foreach (QV4::CompiledData::CompilationUnit *unit, engine->compilationUnits) {
- QString fileName = unit->fileName();
- if (!fileName.isEmpty())
- sources.append(fileName);
- }
-
- Debugger *debugger = engine->debugger;
- emit debugger->sourcesCollected(debugger, sources, seq);
- }
-};
-}
-
-
-DataCollector::DataCollector(QV4::ExecutionEngine *engine)
- : m_engine(engine), m_collectedRefs(Q_NULLPTR)
-{
- values.set(engine, engine->newArrayObject());
-}
-
-DataCollector::~DataCollector()
-{
-}
-
-void DataCollector::collect(const ScopedValue &value)
-{
- if (m_collectedRefs)
- m_collectedRefs->append(addRef(value));
-}
-
-QJsonObject DataCollector::lookupRef(Ref ref)
-{
- QJsonObject dict;
- if (lookupSpecialRef(ref, &dict))
- return dict;
-
- dict.insert(QStringLiteral("handle"), qint64(ref));
-
- QV4::Scope scope(engine());
- QV4::ScopedValue value(scope, getValue(ref));
- switch (value->type()) {
- case QV4::Value::Empty_Type:
- Q_ASSERT(!"empty Value encountered");
- break;
- case QV4::Value::Undefined_Type:
- dict.insert(QStringLiteral("type"), QStringLiteral("undefined"));
- break;
- case QV4::Value::Null_Type:
- dict.insert(QStringLiteral("type"), QStringLiteral("null"));
- break;
- case QV4::Value::Boolean_Type:
- dict.insert(QStringLiteral("type"), QStringLiteral("boolean"));
- dict.insert(QStringLiteral("value"), value->booleanValue() ? QStringLiteral("true")
- : QStringLiteral("false"));
- break;
- case QV4::Value::Managed_Type:
- if (QV4::String *s = value->as<QV4::String>()) {
- dict.insert(QStringLiteral("type"), QStringLiteral("string"));
- dict.insert(QStringLiteral("value"), s->toQString());
- } else if (QV4::Object *o = value->as<QV4::Object>()) {
- dict.insert(QStringLiteral("type"), QStringLiteral("object"));
- dict.insert(QStringLiteral("properties"), collectProperties(o));
- } else {
- Q_UNREACHABLE();
- }
- break;
- case QV4::Value::Integer_Type:
- dict.insert(QStringLiteral("type"), QStringLiteral("number"));
- dict.insert(QStringLiteral("value"), value->integerValue());
- break;
- default: // double
- dict.insert(QStringLiteral("type"), QStringLiteral("number"));
- dict.insert(QStringLiteral("value"), value->doubleValue());
- break;
- }
-
- return dict;
-}
-
-DataCollector::Ref DataCollector::addFunctionRef(const QString &functionName)
-{
- Ref ref = addRef(QV4::Primitive::emptyValue(), false);
-
- QJsonObject dict;
- dict.insert(QStringLiteral("handle"), qint64(ref));
- dict.insert(QStringLiteral("type"), QStringLiteral("function"));
- dict.insert(QStringLiteral("className"), QStringLiteral("Function"));
- dict.insert(QStringLiteral("name"), functionName);
- specialRefs.insert(ref, dict);
-
- return ref;
-}
-
-DataCollector::Ref DataCollector::addScriptRef(const QString &scriptName)
-{
- 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);
- specialRefs.insert(ref, dict);
-
- return ref;
-}
-
-void DataCollector::collectScope(QJsonObject *dict, Debugger *debugger, int frameNr, int scopeNr)
-{
- QStringList names;
-
- Refs refs;
- {
- RefHolder holder(this, &refs);
- debugger->collectArgumentsInContext(this, &names, frameNr, scopeNr);
- debugger->collectLocalsInContext(this, &names, frameNr, scopeNr);
- }
-
- QV4::Scope scope(engine());
- QV4::ScopedObject scopeObject(scope, engine()->newObject());
-
- Q_ASSERT(names.size() == refs.size());
- for (int i = 0, ei = refs.size(); i != ei; ++i)
- scopeObject->put(engine(), names.at(i),
- QV4::Value::fromReturnedValue(getValue(refs.at(i))));
-
- Ref scopeObjectRef = addRef(scopeObject);
- dict->insert(QStringLiteral("ref"), qint64(scopeObjectRef));
- if (m_collectedRefs)
- m_collectedRefs->append(scopeObjectRef);
-}
-
-DataCollector::Ref DataCollector::addRef(Value value, bool deduplicate)
-{
- class ExceptionStateSaver
- {
- quint32 *hasExceptionLoc;
- quint32 hadException;
-
- public:
- ExceptionStateSaver(QV4::ExecutionEngine *engine)
- : hasExceptionLoc(&engine->hasException)
- , hadException(false)
- { std::swap(*hasExceptionLoc, hadException); }
-
- ~ExceptionStateSaver()
- { std::swap(*hasExceptionLoc, hadException); }
- };
-
- // if we wouldn't do this, the putIndexed won't work.
- ExceptionStateSaver resetExceptionState(engine());
- QV4::Scope scope(engine());
- QV4::ScopedObject array(scope, values.value());
- if (deduplicate) {
- for (Ref i = 0; i < array->getLength(); ++i) {
- if (array->getIndexed(i) == value.rawValue())
- return i;
- }
- }
- Ref ref = array->getLength();
- array->putIndexed(ref, value);
- Q_ASSERT(array->getLength() - 1 == ref);
- return ref;
-}
-
-ReturnedValue DataCollector::getValue(Ref ref)
-{
- QV4::Scope scope(engine());
- QV4::ScopedObject array(scope, values.value());
- Q_ASSERT(ref < array->getLength());
- return array->getIndexed(ref, Q_NULLPTR);
-}
-
-bool DataCollector::lookupSpecialRef(Ref ref, QJsonObject *dict)
-{
- SpecialRefs::const_iterator it = specialRefs.find(ref);
- if (it == specialRefs.end())
- return false;
-
- *dict = it.value();
- return true;
-}
-
-QJsonArray DataCollector::collectProperties(Object *object)
-{
- QJsonArray res;
-
- QV4::Scope scope(engine());
- QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
- QV4::ScopedValue name(scope);
- QV4::ScopedValue value(scope);
- while (true) {
- QV4::Value v;
- name = it.nextPropertyNameAsString(&v);
- if (name->isNull())
- break;
- QString key = name->toQStringNoThrow();
- value = v;
- res.append(collectAsJson(key, value));
- }
-
- return res;
-}
-
-QJsonObject DataCollector::collectAsJson(const QString &name, const ScopedValue &value)
-{
- QJsonObject dict;
- if (!name.isNull())
- dict.insert(QStringLiteral("name"), name);
- Ref ref = addRef(value);
- dict.insert(QStringLiteral("ref"), qint64(ref));
- if (m_collectedRefs)
- m_collectedRefs->append(ref);
-
- // TODO: enable this when creator can handle it.
- if (false) {
- if (value->isManaged() && !value->isString()) {
- QV4::Scope scope(engine());
- QV4::ScopedObject obj(scope, value->as<QV4::Object>());
- dict.insert(QStringLiteral("propertycount"), qint64(obj->getLength()));
- }
- }
-
- return dict;
-}
-
Debugger::Debugger(QV4::ExecutionEngine *engine)
: m_engine(engine)
, m_state(Running)
@@ -396,18 +126,6 @@ Debugger::Debugger(QV4::ExecutionEngine *engine)
qMetaTypeId<PauseReason>();
}
-void Debugger::gatherSources(int requestSequenceNr)
-{
- QMutexLocker locker(&m_lock);
-
- m_gatherSources = new GatherSourcesJob(m_engine, requestSequenceNr);
- if (m_state == Paused) {
- runInEngine_havingLock(m_gatherSources);
- delete m_gatherSources;
- m_gatherSources = 0;
- }
-}
-
void Debugger::pause()
{
QMutexLocker locker(&m_lock);
@@ -465,256 +183,6 @@ QVector<StackFrame> Debugger::stackTrace(int frameLimit) const
return m_engine->stackTrace(frameLimit);
}
-static inline Heap::CallContext *findContext(Heap::ExecutionContext *ctxt, int frame)
-{
- if (!ctxt)
- return 0;
-
- Scope scope(ctxt->engine);
- ScopedContext ctx(scope, ctxt);
- while (ctx) {
- CallContext *cCtxt = ctx->asCallContext();
- if (cCtxt && cCtxt->d()->function) {
- if (frame < 1)
- return cCtxt->d();
- --frame;
- }
- ctx = ctx->d()->parent;
- }
-
- return 0;
-}
-
-static inline Heap::CallContext *findScope(Heap::ExecutionContext *ctxt, int scope)
-{
- if (!ctxt)
- return 0;
-
- Scope s(ctxt->engine);
- ScopedContext ctx(s, ctxt);
- for (; scope > 0 && ctx; --scope)
- ctx = ctx->d()->outer;
-
- return (ctx && ctx->d()) ? ctx->asCallContext()->d() : 0;
-}
-
-void Debugger::collectArgumentsInContext(DataCollector *collector, QStringList *names, int frameNr,
- int scopeNr)
-{
- if (state() != Paused)
- return;
-
- class ArgumentCollectJob: public Job
- {
- QV4::ExecutionEngine *engine;
- QV4::Debugging::DataCollector *collector;
- QStringList *names;
- int frameNr;
- int scopeNr;
-
- public:
- ArgumentCollectJob(QV4::ExecutionEngine *engine, DataCollector *collector,
- QStringList *names, int frameNr, int scopeNr)
- : engine(engine)
- , collector(collector)
- , names(names)
- , frameNr(frameNr)
- , scopeNr(scopeNr)
- {}
-
- ~ArgumentCollectJob() {}
-
- void run()
- {
- if (frameNr < 0)
- return;
-
- Scope scope(engine);
- Scoped<CallContext> ctxt(scope, findScope(findContext(engine->currentContext(), frameNr), scopeNr));
- if (!ctxt)
- return;
-
- ScopedValue v(scope);
- int nFormals = ctxt->formalCount();
- for (unsigned i = 0, ei = nFormals; i != ei; ++i) {
- QString qName;
- if (Identifier *name = ctxt->formals()[nFormals - i - 1])
- qName = name->string;
- names->append(qName);
- v = ctxt->argument(i);
- collector->collect(v);
- }
- }
- };
-
- ArgumentCollectJob job(m_engine, collector, names, frameNr, scopeNr);
- runInEngine(&job);
-}
-
-/// Same as \c retrieveArgumentsFromContext, but now for locals.
-void Debugger::collectLocalsInContext(DataCollector *collector, QStringList *names, int frameNr,
- int scopeNr)
-{
- Q_ASSERT(collector);
- Q_ASSERT(names);
-
- if (state() != Paused)
- return;
-
- class LocalCollectJob: public Job
- {
- QV4::ExecutionEngine *engine;
- DataCollector *collector;
- QStringList *names;
- int frameNr;
- int scopeNr;
-
- public:
- LocalCollectJob(QV4::ExecutionEngine *engine, DataCollector *collector, QStringList *names, int frameNr, int scopeNr)
- : engine(engine)
- , collector(collector)
- , names(names)
- , frameNr(frameNr)
- , scopeNr(scopeNr)
- {}
-
- void run()
- {
- if (frameNr < 0)
- return;
-
- Scope scope(engine);
- Scoped<CallContext> ctxt(scope, findScope(findContext(engine->currentContext(), frameNr), scopeNr));
- if (!ctxt)
- return;
-
- ScopedValue v(scope);
- for (unsigned i = 0, ei = ctxt->variableCount(); i != ei; ++i) {
- QString qName;
- if (Identifier *name = ctxt->variables()[i])
- qName = name->string;
- names->append(qName);
- v = ctxt->d()->locals[i];
- collector->collect(v);
- }
- }
- };
-
- LocalCollectJob job(m_engine, collector, names, frameNr, scopeNr);
- runInEngine(&job);
-}
-
-bool Debugger::collectThisInContext(DataCollector *collector, int frame)
-{
- if (state() != Paused)
- return false;
-
- class ThisCollectJob: public Job
- {
- QV4::ExecutionEngine *engine;
- DataCollector *collector;
- int frameNr;
- bool *foundThis;
-
- public:
- ThisCollectJob(QV4::ExecutionEngine *engine, DataCollector *collector, int frameNr,
- bool *foundThis)
- : engine(engine)
- , collector(collector)
- , frameNr(frameNr)
- , foundThis(foundThis)
- {}
-
- void run()
- {
- *foundThis = myRun();
- }
-
- bool myRun()
- {
- Scope scope(engine);
- ScopedContext ctxt(scope, findContext(engine->currentContext(), frameNr));
- while (ctxt) {
- if (CallContext *cCtxt = ctxt->asCallContext())
- if (cCtxt->d()->activation)
- break;
- ctxt = ctxt->d()->outer;
- }
-
- if (!ctxt)
- return false;
-
- ScopedValue o(scope, ctxt->asCallContext()->d()->activation);
- collector->collect(o);
- return true;
- }
- };
-
- bool foundThis = false;
- ThisCollectJob job(m_engine, collector, frame, &foundThis);
- runInEngine(&job);
- return foundThis;
-}
-
-bool Debugger::collectThrownValue(DataCollector *collector)
-{
- if (state() != Paused || !m_engine->hasException)
- return false;
-
- class ExceptionCollectJob: public Job
- {
- QV4::ExecutionEngine *engine;
- DataCollector *collector;
-
- public:
- ExceptionCollectJob(QV4::ExecutionEngine *engine, DataCollector *collector)
- : engine(engine)
- , collector(collector)
- {}
-
- void run()
- {
- Scope scope(engine);
- ScopedValue v(scope, *engine->exceptionValue);
- collector->collect(v);
- }
- };
-
- ExceptionCollectJob job(m_engine, collector);
- runInEngine(&job);
- return true;
-}
-
-QVector<Heap::ExecutionContext::ContextType> Debugger::getScopeTypes(int frame) const
-{
- QVector<Heap::ExecutionContext::ContextType> types;
-
- if (state() != Paused)
- return types;
-
- Scope scope(m_engine);
- Scoped<CallContext> sctxt(scope, findContext(m_engine->currentContext(), frame));
- if (!sctxt || sctxt->d()->type < Heap::ExecutionContext::Type_QmlContext)
- return types;
-
- ScopedContext it(scope, sctxt->d());
- for (; it; it = it->d()->outer)
- types.append(it->d()->type);
-
- return types;
-}
-
-
-void Debugger::evaluateExpression(int frameNr, const QString &expression,
- DataCollector *resultsCollector)
-{
- Q_ASSERT(state() == Paused);
-
- Q_ASSERT(m_runningJob == 0);
- ExpressionEvalJob job(m_engine, frameNr, expression, resultsCollector);
- runInEngine(&job);
-}
-
void Debugger::maybeBreakAtInstruction()
{
if (m_runningJob) // do not re-enter when we're doing a job for the debugger.
diff --git a/src/qml/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h
index b85611f457..ac0c934d42 100644
--- a/src/qml/jsruntime/qv4debugging_p.h
+++ b/src/qml/jsruntime/qv4debugging_p.h
@@ -79,73 +79,31 @@ inline bool operator==(const DebuggerBreakPoint &a, const DebuggerBreakPoint &b)
typedef QHash<DebuggerBreakPoint, QString> BreakPoints;
-class Q_QML_PRIVATE_EXPORT DataCollector
-{
-public:
- typedef uint Ref;
- typedef QVector<uint> Refs;
-
- DataCollector(QV4::ExecutionEngine *engine);
- ~DataCollector();
-
- void collect(const QV4::ScopedValue &value);
-
- QJsonObject lookupRef(Ref ref);
-
- Ref addFunctionRef(const QString &functionName);
- Ref addScriptRef(const QString &scriptName);
-
- void collectScope(QJsonObject *dict, QV4::Debugging::Debugger *debugger, int frameNr,
- int scopeNr);
-
- QV4::ExecutionEngine *engine() const { return m_engine; }
-
-private:
- friend class RefHolder;
-
- Ref addRef(QV4::Value value, bool deduplicate = true);
- QV4::ReturnedValue getValue(Ref ref);
- bool lookupSpecialRef(Ref ref, QJsonObject *dict);
-
- QJsonArray collectProperties(QV4::Object *object);
- QJsonObject collectAsJson(const QString &name, const QV4::ScopedValue &value);
-
- QV4::ExecutionEngine *m_engine;
- Refs *m_collectedRefs;
- QV4::PersistentValue values;
- typedef QHash<Ref, QJsonObject> SpecialRefs;
- SpecialRefs specialRefs;
-};
-
-class RefHolder {
-public:
- RefHolder(DataCollector *collector, DataCollector::Refs *target) :
- m_collector(collector), m_previousRefs(collector->m_collectedRefs)
- {
- m_collector->m_collectedRefs = target;
- }
-
- ~RefHolder()
- {
- std::swap(m_collector->m_collectedRefs, m_previousRefs);
- }
-
-private:
- DataCollector *m_collector;
- DataCollector::Refs *m_previousRefs;
-};
-
class Q_QML_EXPORT Debugger : public QObject
{
Q_OBJECT
public:
- class Job
+ class Q_QML_EXPORT Job
{
public:
virtual ~Job() = 0;
virtual void run() = 0;
};
+ class Q_QML_EXPORT JavaScriptJob: public Job
+ {
+ QV4::ExecutionEngine *engine;
+ int frameNr;
+ const QString &script;
+
+ public:
+ JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr, const QString &script);
+ void run();
+
+ protected:
+ virtual void handleResult(QV4::ScopedValue &result) = 0;
+ };
+
enum State {
Running,
Paused
@@ -165,7 +123,6 @@ public:
ExecutionEngine *engine() const
{ return m_engine; }
- void gatherSources(int requestSequenceNr);
void pause();
void resume(Speed speed);
@@ -189,16 +146,10 @@ public:
}
QVector<StackFrame> stackTrace(int frameLimit = -1) const;
- void collectArgumentsInContext(DataCollector *collector, QStringList *names, int frameNr = 0,
- int scopeNr = 0);
- void collectLocalsInContext(DataCollector *collector, QStringList *names, int frameNr = 0,
- int scopeNr = 0);
- bool collectThisInContext(DataCollector *collector, int frame = 0);
- bool collectThrownValue(DataCollector *collector);
QVector<Heap::ExecutionContext::ContextType> getScopeTypes(int frame = 0) const;
- void evaluateExpression(int frameNr, const QString &expression,
- DataCollector *resultsCollector);
+ Function *getFunction() const;
+ void runInEngine(Job *job);
public: // compile-time interface
void maybeBreakAtInstruction();
@@ -213,14 +164,9 @@ signals:
void debuggerPaused(QV4::Debugging::Debugger *self, QV4::Debugging::PauseReason reason);
private:
- Function *getFunction() const;
-
// requires lock to be held
void pauseAndWait(PauseReason reason);
-
bool reallyHitTheBreakPoint(const QString &filename, int linenr);
-
- void runInEngine(Job *job);
void runInEngine_havingLock(Debugger::Job *job);
private:
diff --git a/tests/auto/qml/qv4debugger/qv4debugger.pro b/tests/auto/qml/qv4debugger/qv4debugger.pro
index 2a318955f3..540cab70e6 100644
--- a/tests/auto/qml/qv4debugger/qv4debugger.pro
+++ b/tests/auto/qml/qv4debugger/qv4debugger.pro
@@ -2,6 +2,14 @@ CONFIG += testcase
TARGET = tst_qv4debugger
macx:CONFIG -= app_bundle
-SOURCES += tst_qv4debugger.cpp
+SOURCES += \
+ $$PWD/tst_qv4debugger.cpp \
+ $$PWD/../../../../src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
+
+HEADERS += \
+ $$PWD/../../../../src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h
+
+INCLUDEPATH += \
+ $$PWD/../../../../src/plugins/qmltooling/qmldbg_debugger
QT += core-private gui-private qml-private network testlib
diff --git a/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp
index ac780d272c..ca308a4f49 100644
--- a/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp
+++ b/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp
@@ -32,6 +32,8 @@
****************************************************************************/
#include <QtTest/QtTest>
+#include "qv4datacollector.h"
+
#include <QJSEngine>
#include <QQmlEngine>
#include <QQmlComponent>
@@ -97,14 +99,14 @@ class TestAgent : public QObject
{
Q_OBJECT
public:
- typedef QV4::Debugging::DataCollector::Refs Refs;
- typedef QV4::Debugging::DataCollector::Ref Ref;
+ typedef QV4DataCollector::Refs Refs;
+ typedef QV4DataCollector::Ref Ref;
struct NamedRefs {
- NamedRefs(DataCollector *collector = 0): collector(collector) {}
+ NamedRefs(QV4DataCollector *collector = 0): collector(collector) {}
QStringList names;
Refs refs;
- DataCollector *collector;
+ QV4DataCollector *collector;
int size() const {
Q_ASSERT(names.size() == refs.size());
@@ -164,13 +166,14 @@ public slots:
m_pauseReason = reason;
m_statesWhenPaused << debugger->currentExecutionState();
- {
+ if (debugger->state() == QV4::Debugging::Debugger::Paused &&
+ debugger->engine()->hasException) {
Refs refs;
- QV4::Debugging::RefHolder holder(&collector, &refs);
- if (debugger->collectThrownValue(&collector)) {
- Q_ASSERT(refs.size() > 0);
- m_thrownValue = refs.first();
- }
+ RefHolder holder(&collector, &refs);
+ ExceptionCollectJob job(debugger->engine(), &collector);
+ debugger->runInEngine(&job);
+ Q_ASSERT(refs.size() > 0);
+ m_thrownValue = refs.first();
}
foreach (const TestBreakPoint &bp, m_breakPointsToAddWhenPaused)
@@ -180,10 +183,13 @@ public slots:
m_stackTrace = debugger->stackTrace();
while (!m_expressionRequests.isEmpty()) {
+ Q_ASSERT(debugger->state() == QV4::Debugging::Debugger::Paused);
ExpressionRequest request = m_expressionRequests.takeFirst();
m_expressionResults << Refs();
RefHolder holder(&collector, &m_expressionResults.last());
- debugger->evaluateExpression(request.frameNr, request.expression, &collector);
+ ExpressionEvalJob job(debugger->engine(), request.frameNr, request.expression,
+ &collector);
+ debugger->runInEngine(&job);
}
if (m_captureContextInfo)
@@ -207,11 +213,15 @@ public:
for (int i = 0, ei = m_stackTrace.size(); i != ei; ++i) {
m_capturedArguments.append(NamedRefs(&collector));
RefHolder argHolder(&collector, &m_capturedArguments.last().refs);
- debugger->collectArgumentsInContext(&collector, &m_capturedArguments.last().names, i);
+ ArgumentCollectJob argumentsJob(debugger->engine(), &collector,
+ &m_capturedArguments.last().names, i, 0);
+ debugger->runInEngine(&argumentsJob);
m_capturedLocals.append(NamedRefs(&collector));
RefHolder localHolder(&collector, &m_capturedLocals.last().refs);
- debugger->collectLocalsInContext(&collector, &m_capturedLocals.last().names, i);
+ LocalCollectJob localsJob(debugger->engine(), &collector,
+ &m_capturedLocals.last().names, i, 0);
+ debugger->runInEngine(&localsJob);
}
}
@@ -234,7 +244,7 @@ public:
QVector<NamedRefs> m_capturedArguments;
QVector<NamedRefs> m_capturedLocals;
qint64 m_thrownValue;
- QV4::Debugging::DataCollector collector;
+ QV4DataCollector collector;
struct ExpressionRequest {
QString expression;