Move DataCollector into debugger plugin
authorUlf Hermann <ulf.hermann@theqtcompany.com>
Wed, 22 Jul 2015 16:50:34 +0000 (18:50 +0200)
committerUlf Hermann <ulf.hermann@theqtcompany.com>
Mon, 10 Aug 2015 10:05:14 +0000 (10:05 +0000)
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>
src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro
src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp [new file with mode: 0644]
src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h [new file with mode: 0644]
src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
src/qml/jsruntime/qv4context_p.h
src/qml/jsruntime/qv4debugging.cpp
src/qml/jsruntime/qv4debugging_p.h
tests/auto/qml/qv4debugger/qv4debugger.pro
tests/auto/qml/qv4debugger/tst_qv4debugger.cpp

index dd0c9a1..d860328 100644 (file)
@@ -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 (file)
index 0000000..64ee5c3
--- /dev/null
@@ -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 (file)
index 0000000..c91b77c
--- /dev/null
@@ -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
index 15421f4..7f22b16 100644 (file)
@@ -33,6 +33,7 @@
 
 #include "qv4debuggeragent.h"
 #include "qv4debugservice.h"
+#include "qv4datacollector.h"
 
 #include <QtCore/qjsonobject.h>
 #include <QtCore/qjsonarray.h>
index 21bfe97..6b68f95 100644 (file)
@@ -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();
 }
index 6e51136..c80ad78 100644 (file)
@@ -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);
index c698a6c..1e051ed 100644 (file)
@@ -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)
 
index 6f3e483..ceeef80 100644 (file)
@@ -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.
index b85611f..ac0c934 100644 (file)
@@ -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:
index 2a31895..540cab7 100644 (file)
@@ -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
index ac780d2..ca308a4 100644 (file)
@@ -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;