aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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;