From 5537027094d26e517fc4ad3173ea84d254dd3013 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 30 Nov 2015 12:04:59 +0100 Subject: V4 Debugger: Avoid looking up values in debugger thread To avoid interaction with the engine from the debugger thread we move the value lookup functionality into the data collector, and drop the RefHolder. Also, we define some more debugger jobs to move the work the request handlers do into the GUI thread. Task-number: QTBUG-50481 Change-Id: Ia73ebd01e715f13110ef010a766e9b0a1e202df9 Reviewed-by: Nils Jeisecke Reviewed-by: Simon Hausmann --- .../qmldbg_debugger/qv4datacollector.cpp | 378 ++++++++++++++------- .../qmltooling/qmldbg_debugger/qv4datacollector.h | 114 ++++--- .../qmltooling/qmldbg_debugger/qv4debugservice.cpp | 208 +++--------- .../qmltooling/qmldbg_debugger/qv4debugservice.h | 10 - 4 files changed, 361 insertions(+), 349 deletions(-) (limited to 'src') diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp index 4cca1900de..a86373b627 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -89,9 +89,30 @@ QVector QV4DataCollector::getScopeType return types; } +int QV4DataCollector::encodeScopeType(QV4::Heap::ExecutionContext::ContextType scopeType) +{ + switch (scopeType) { + case QV4::Heap::ExecutionContext::Type_GlobalContext: + return 0; + break; + case QV4::Heap::ExecutionContext::Type_CatchContext: + return 4; + break; + case QV4::Heap::ExecutionContext::Type_WithContext: + return 2; + break; + case QV4::Heap::ExecutionContext::Type_SimpleCallContext: + case QV4::Heap::ExecutionContext::Type_CallContext: + return 1; + break; + case QV4::Heap::ExecutionContext::Type_QmlContext: + default: + return -1; + } +} QV4DataCollector::QV4DataCollector(QV4::ExecutionEngine *engine) - : m_engine(engine), m_collectedRefs(Q_NULLPTR) + : m_engine(engine) { values.set(engine, engine->newArrayObject()); } @@ -100,10 +121,11 @@ QV4DataCollector::~QV4DataCollector() { } -void QV4DataCollector::collect(const QV4::ScopedValue &value) +QV4DataCollector::Ref QV4DataCollector::collect(const QV4::ScopedValue &value) { - if (m_collectedRefs) - m_collectedRefs->append(addRef(value)); + Ref ref = addRef(value); + collectedRefs.append(ref); + return ref; } const QV4::Object *collectProperty(const QV4::ScopedValue &value, QV4::ExecutionEngine *engine, @@ -191,6 +213,7 @@ QV4DataCollector::Ref QV4DataCollector::addFunctionRef(const QString &functionNa dict.insert(QStringLiteral("type"), QStringLiteral("function")); dict.insert(QStringLiteral("name"), functionName); specialRefs.insert(ref, dict); + collectedRefs.append(ref); return ref; } @@ -204,36 +227,133 @@ QV4DataCollector::Ref QV4DataCollector::addScriptRef(const QString &scriptName) dict.insert(QStringLiteral("type"), QStringLiteral("script")); dict.insert(QStringLiteral("name"), scriptName); specialRefs.insert(ref, dict); + collectedRefs.append(ref); return ref; } -void QV4DataCollector::collectScope(QJsonObject *dict, QV4::Debugging::V4Debugger *debugger, +bool QV4DataCollector::isValidRef(QV4DataCollector::Ref ref) const +{ + QV4::Scope scope(engine()); + QV4::ScopedObject array(scope, values.value()); + return ref < array->getLength(); +} + +bool QV4DataCollector::collectScope(QJsonObject *dict, QV4::Debugging::V4Debugger *debugger, int frameNr, int scopeNr) { QStringList names; - Refs refs; + QV4::Scope scope(engine()); if (debugger->state() == QV4::Debugging::V4Debugger::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::Scoped ctxt(scope, findScope(findContext(engine(), frameNr), scopeNr)); + if (!ctxt) + return false; + + 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); + collect(v); + } + + for (unsigned i = 0, ei = ctxt->variableCount(); i != ei; ++i) { + QString qName; + if (QV4::Identifier *name = ctxt->variables()[i]) + qName = name->string; + names.append(qName); + v = ctxt->d()->locals[i]; + collect(v); + } } - 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) + Q_ASSERT(names.size() == collectedRefs.size()); + for (int i = 0, ei = collectedRefs.size(); i != ei; ++i) scopeObject->put(engine(), names.at(i), - QV4::Value::fromReturnedValue(getValue(refs.at(i)))); + QV4::Value::fromReturnedValue(getValue(collectedRefs.at(i)))); Ref scopeObjectRef = addRef(scopeObject); dict->insert(QStringLiteral("ref"), qint64(scopeObjectRef)); - if (m_collectedRefs) - m_collectedRefs->append(scopeObjectRef); + collectedRefs.append(scopeObjectRef); + return true; +} + +QJsonObject toRef(QV4DataCollector::Ref ref) { + QJsonObject dict; + dict.insert(QStringLiteral("ref"), qint64(ref)); + return dict; +} + +QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int frameNr, + QV4::Debugging::V4Debugger *debugger) +{ + QV4DataCollector::Ref ref; + + QJsonObject frame; + frame[QLatin1String("index")] = frameNr; + frame[QLatin1String("debuggerFrame")] = false; + ref = addFunctionRef(stackFrame.function); + frame[QLatin1String("func")] = toRef(ref); + ref = addScriptRef(stackFrame.source); + frame[QLatin1String("script")] = toRef(ref); + frame[QLatin1String("line")] = stackFrame.line - 1; + if (stackFrame.column >= 0) + frame[QLatin1String("column")] = stackFrame.column; + + QJsonArray scopes; + if (debugger->state() == QV4::Debugging::V4Debugger::Paused) { + QV4::Scope scope(engine()); + QV4::ScopedContext ctxt(scope, findContext(engine(), frameNr)); + while (ctxt) { + if (QV4::CallContext *cCtxt = ctxt->asCallContext()) { + if (cCtxt->d()->activation) + break; + } + ctxt = ctxt->d()->outer; + } + + if (ctxt) { + QV4::ScopedValue o(scope, ctxt->asCallContext()->d()->activation); + frame[QLatin1String("receiver")] = toRef(collect(o)); + } + + // Only type and index are used by Qt Creator, so we keep it easy: + QVector scopeTypes = getScopeTypes(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; + + return frame; +} + +QJsonArray QV4DataCollector::flushCollectedRefs() +{ + QJsonArray refs; + std::sort(collectedRefs.begin(), collectedRefs.end()); + for (int i = 0, ei = collectedRefs.size(); i != ei; ++i) { + QV4DataCollector::Ref ref = collectedRefs.at(i); + if (i > 0 && ref == collectedRefs.at(i - 1)) + continue; + refs.append(lookupRef(ref)); + } + + collectedRefs.clear(); + return refs; } QV4DataCollector::Ref QV4DataCollector::addRef(QV4::Value value, bool deduplicate) @@ -316,14 +436,117 @@ QJsonObject QV4DataCollector::collectAsJson(const QString &name, const QV4::Scop if (value->isManaged() && !value->isString()) { Ref ref = addRef(value); dict.insert(QStringLiteral("ref"), qint64(ref)); - if (m_collectedRefs) - m_collectedRefs->append(ref); + collectedRefs.append(ref); } collectProperty(value, engine(), dict); return dict; } +BacktraceJob::BacktraceJob(QV4DataCollector *collector, int fromFrame, int toFrame) : + CollectJob(collector), fromFrame(fromFrame), toFrame(toFrame) +{ +} + +void BacktraceJob::run() +{ + QJsonArray frameArray; + QVector frames = collector->engine()->stackTrace(toFrame); + QV4::Debugging::V4Debugger *debugger = + qobject_cast(collector->engine()->debugger); + if (debugger) { + for (int i = fromFrame; i < toFrame && i < frames.size(); ++i) + frameArray.push_back(collector->buildFrame(frames[i], i, debugger)); + } + if (frameArray.isEmpty()) { + result.insert(QStringLiteral("totalFrames"), 0); + } else { + result.insert(QStringLiteral("fromFrame"), fromFrame); + result.insert(QStringLiteral("toFrame"), fromFrame + frameArray.size()); + result.insert(QStringLiteral("frames"), frameArray); + } + collectedRefs = collector->flushCollectedRefs(); +} + +FrameJob::FrameJob(QV4DataCollector *collector, int frameNr) : + CollectJob(collector), frameNr(frameNr), success(false) +{ +} + +void FrameJob::run() +{ + QVector frames = collector->engine()->stackTrace(frameNr + 1); + if (frameNr >= frames.length()) { + success = false; + } else { + QV4::Debugging::V4Debugger *debugger = + qobject_cast(collector->engine()->debugger); + if (debugger) { + result = collector->buildFrame(frames[frameNr], frameNr, debugger); + collectedRefs = collector->flushCollectedRefs(); + success = true; + } else { + success = false; + } + } +} + +bool FrameJob::wasSuccessful() const +{ + return success; +} + +ScopeJob::ScopeJob(QV4DataCollector *collector, int frameNr, int scopeNr) : + CollectJob(collector), frameNr(frameNr), scopeNr(scopeNr), success(false) +{ +} + +void ScopeJob::run() +{ + QJsonObject object; + QV4::Debugging::V4Debugger *debugger = + qobject_cast(collector->engine()->debugger); + success = (debugger && collector->collectScope(&object, debugger, frameNr, scopeNr)); + + if (success) { + QVector scopeTypes = + QV4DataCollector::getScopeTypes(collector->engine(), frameNr); + result[QLatin1String("type")] = QV4DataCollector::encodeScopeType(scopeTypes[scopeNr]); + } else { + result[QLatin1String("type")] = -1; + } + result[QLatin1String("index")] = scopeNr; + result[QLatin1String("frameIndex")] = frameNr; + result[QLatin1String("object")] = object; + collectedRefs = collector->flushCollectedRefs(); +} + +bool ScopeJob::wasSuccessful() const +{ + return success; +} + +ValueLookupJob::ValueLookupJob(const QJsonArray &handles, QV4DataCollector *collector) : + CollectJob(collector), handles(handles) {} + +void ValueLookupJob::run() +{ + foreach (const QJsonValue &handle, handles) { + QV4DataCollector::Ref ref = handle.toInt(); + if (!collector->isValidRef(ref)) { + exception = QString::fromLatin1("Invalid Ref: %1").arg(ref); + break; + } + result[QString::number(ref)] = collector->lookupRef(ref); + } + collectedRefs = collector->flushCollectedRefs(); +} + +const QString &ValueLookupJob::exceptionMessage() const +{ + return exception; +} + ExpressionEvalJob::ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr, const QString &expression, QV4DataCollector *collector) @@ -336,7 +559,8 @@ void ExpressionEvalJob::handleResult(QV4::ScopedValue &result) { if (hasExeption()) exception = result->toQStringNoThrow(); - collector->collect(result); + value = collector->lookupRef(collector->collect(result)); + collectedRefs = collector->flushCollectedRefs(); } const QString &ExpressionEvalJob::exceptionMessage() const @@ -344,6 +568,16 @@ const QString &ExpressionEvalJob::exceptionMessage() const return exception; } +const QJsonObject &ExpressionEvalJob::returnValue() const +{ + return value; +} + +const QJsonArray &ExpressionEvalJob::refs() const +{ + return collectedRefs; +} + GatherSourcesJob::GatherSourcesJob(QV4::ExecutionEngine *engine, int seq) : engine(engine) , seq(seq) @@ -364,111 +598,21 @@ void GatherSourcesJob::run() 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 ctxt( - scope, QV4DataCollector::findScope(QV4DataCollector::findContext(engine, 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 ctxt( - scope, QV4DataCollector::findScope(QV4DataCollector::findContext(engine, 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) +ExceptionCollectJob::ExceptionCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector) : engine(engine) , collector(collector) - , frameNr(frameNr) - , foundThis(foundThis) + , exception(-1) {} -void ThisCollectJob::run() -{ - *foundThis = myRun(); -} - -bool ThisCollectJob::myRun() -{ +void ExceptionCollectJob::run() { QV4::Scope scope(engine); - QV4::ScopedContext ctxt(scope, QV4DataCollector::findContext(engine, 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; + QV4::ScopedValue v(scope, *engine->exceptionValue); + exception = collector->collect(v); } -ExceptionCollectJob::ExceptionCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector) - : engine(engine) - , collector(collector) -{} - -void ExceptionCollectJob::run() +QV4DataCollector::Ref ExceptionCollectJob::exceptionValue() const { - QV4::Scope scope(engine); - QV4::ScopedValue v(scope, *engine->exceptionValue); - collector->collect(v); + return exception; } QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h index d0e4a4ad18..5a2c8ff33b 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h @@ -37,6 +37,9 @@ #include #include +#include +#include + QT_BEGIN_NAMESPACE class QV4DataCollector @@ -49,25 +52,27 @@ public: static QV4::Heap::CallContext *findScope(QV4::ExecutionContext *ctxt, int scope); static QVector getScopeTypes( QV4::ExecutionEngine *engine, int frame); + static int encodeScopeType(QV4::Heap::ExecutionContext::ContextType scopeType); QV4DataCollector(QV4::ExecutionEngine *engine); ~QV4DataCollector(); - void collect(const QV4::ScopedValue &value); - + Ref collect(const QV4::ScopedValue &value); + bool isValidRef(Ref ref) const; QJsonObject lookupRef(Ref ref); Ref addFunctionRef(const QString &functionName); Ref addScriptRef(const QString &scriptName); - void collectScope(QJsonObject *dict, QV4::Debugging::V4Debugger *debugger, int frameNr, + bool collectScope(QJsonObject *dict, QV4::Debugging::V4Debugger *debugger, int frameNr, int scopeNr); + QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr, + QV4::Debugging::V4Debugger *debugger); QV4::ExecutionEngine *engine() const { return m_engine; } + QJsonArray flushCollectedRefs(); private: - friend class RefHolder; - Ref addRef(QV4::Value value, bool deduplicate = true); QV4::ReturnedValue getValue(Ref ref); bool lookupSpecialRef(Ref ref, QJsonObject *dict); @@ -77,102 +82,103 @@ private: void collectArgumentsInContext(); QV4::ExecutionEngine *m_engine; - Refs *m_collectedRefs; + Refs collectedRefs; QV4::PersistentValue values; typedef QHash SpecialRefs; SpecialRefs specialRefs; }; -class RefHolder { +class CollectJob : public QV4::Debugging::V4Debugger::Job +{ +protected: + QV4DataCollector *collector; + QJsonObject result; + QJsonArray collectedRefs; 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; + CollectJob(QV4DataCollector *collector) : collector(collector) {} + const QJsonObject &returnValue() const { return result; } + const QJsonArray &refs() const { return collectedRefs; } }; -class ExpressionEvalJob: public QV4::Debugging::V4Debugger::JavaScriptJob +class BacktraceJob: public CollectJob { - QV4DataCollector *collector; - QString exception; - + int fromFrame; + int toFrame; public: - ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr, const QString &expression, - QV4DataCollector *collector); - virtual void handleResult(QV4::ScopedValue &result); - const QString &exceptionMessage() const; + BacktraceJob(QV4DataCollector *collector, int fromFrame, int toFrame); + void run(); }; -class GatherSourcesJob: public QV4::Debugging::V4Debugger::Job +class FrameJob: public CollectJob { - QV4::ExecutionEngine *engine; - const int seq; + int frameNr; + bool success; public: - GatherSourcesJob(QV4::ExecutionEngine *engine, int seq); + FrameJob(QV4DataCollector *collector, int frameNr); void run(); + bool wasSuccessful() const; }; -class ArgumentCollectJob: public QV4::Debugging::V4Debugger::Job +class ScopeJob: public CollectJob { - QV4::ExecutionEngine *engine; - QV4DataCollector *collector; - QStringList *names; int frameNr; int scopeNr; + bool success; public: - ArgumentCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector, - QStringList *names, int frameNr, int scopeNr); + ScopeJob(QV4DataCollector *collector, int frameNr, int scopeNr); void run(); + bool wasSuccessful() const; }; -class LocalCollectJob: public QV4::Debugging::V4Debugger::Job +class ValueLookupJob: public CollectJob { - QV4::ExecutionEngine *engine; - QV4DataCollector *collector; - QStringList *names; - int frameNr; - int scopeNr; + const QJsonArray handles; + QString exception; public: - LocalCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector, QStringList *names, - int frameNr, int scopeNr); + ValueLookupJob(const QJsonArray &handles, QV4DataCollector *collector); void run(); + const QString &exceptionMessage() const; }; -class ThisCollectJob: public QV4::Debugging::V4Debugger::Job +class ExpressionEvalJob: public QV4::Debugging::V4Debugger::JavaScriptJob { - QV4::ExecutionEngine *engine; QV4DataCollector *collector; - int frameNr; - bool *foundThis; + QString exception; + QJsonObject value; + QJsonArray collectedRefs; public: - ThisCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector, int frameNr, - bool *foundThis); + ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr, const QString &expression, + QV4DataCollector *collector); + virtual void handleResult(QV4::ScopedValue &result); + const QString &exceptionMessage() const; + const QJsonObject &returnValue() const; + const QJsonArray &refs() const; +}; + +class GatherSourcesJob: public QV4::Debugging::V4Debugger::Job +{ + QV4::ExecutionEngine *engine; + const int seq; + +public: + GatherSourcesJob(QV4::ExecutionEngine *engine, int seq); void run(); - bool myRun(); }; class ExceptionCollectJob: public QV4::Debugging::V4Debugger::Job { QV4::ExecutionEngine *engine; QV4DataCollector *collector; + QV4DataCollector::Ref exception; public: ExceptionCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector); void run(); + QV4DataCollector::Ref exceptionValue() const; }; QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp index 5233a09992..e9bcd772e5 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp @@ -114,9 +114,9 @@ protected: response.insert(QStringLiteral("running"), debugService->debuggerAgent.isRunning()); } - void addRefs() + void addRefs(const QJsonArray &refs) { - response.insert(QStringLiteral("refs"), debugService->buildRefs()); + response.insert(QStringLiteral("refs"), refs); } void createErrorResponse(const QString &msg) @@ -271,28 +271,16 @@ public: int toFrame = arguments.value(QStringLiteral("toFrame")).toInt(fromFrame + 10); // no idea what the bottom property is for, so we'll ignore it. - QV4::Debugging::V4Debugger *debugger = debugService->debuggerAgent.firstDebugger(); - - QJsonArray frameArray; - QVector frames = debugger->stackTrace(toFrame); - for (int i = fromFrame; i < toFrame && i < frames.size(); ++i) - frameArray.push_back(debugService->buildFrame(frames[i], i, debugger)); + BacktraceJob job(debugService->collector(), fromFrame, toFrame); + debugService->debuggerAgent.firstDebugger()->runInEngine(&job); // response: addCommand(); addRequestSequence(); addSuccess(true); addRunning(); - QJsonObject body; - if (frameArray.isEmpty()) { - body.insert(QStringLiteral("totalFrames"), 0); - } else { - body.insert(QStringLiteral("fromFrame"), fromFrame); - body.insert(QStringLiteral("toFrame"), fromFrame + frameArray.size()); - body.insert(QStringLiteral("frames"), frameArray); - } - addBody(body); - addRefs(); + addBody(job.returnValue()); + addRefs(job.refs()); } }; @@ -308,23 +296,27 @@ public: const int frameNr = arguments.value(QStringLiteral("number")).toInt( debugService->selectedFrame()); - QV4::Debugging::V4Debugger *debugger = debugService->debuggerAgent.firstDebugger(); - QVector frames = debugger->stackTrace(frameNr + 1); - if (frameNr < 0 || frameNr >= frames.size()) { + if (frameNr < 0) { createErrorResponse(QStringLiteral("frame command has invalid frame number")); return; } + FrameJob job(debugService->collector(), frameNr); + debugService->debuggerAgent.firstDebugger()->runInEngine(&job); + if (!job.wasSuccessful()) { + createErrorResponse(QStringLiteral("frame retrieval failed")); + return; + } + debugService->selectFrame(frameNr); - QJsonObject frame = debugService->buildFrame(frames[frameNr], frameNr, debugger); // response: addCommand(); addRequestSequence(); addSuccess(true); addRunning(); - addBody(frame); - addRefs(); + addBody(job.returnValue()); + addRefs(job.refs()); } }; @@ -341,9 +333,7 @@ public: debugService->selectedFrame()); const int scopeNr = arguments.value(QStringLiteral("number")).toInt(0); - QV4::Debugging::V4Debugger *debugger = debugService->debuggerAgent.firstDebugger(); - QVector frames = debugger->stackTrace(frameNr + 1); - if (frameNr < 0 || frameNr >= frames.size()) { + if (frameNr < 0) { createErrorResponse(QStringLiteral("scope command has invalid frame number")); return; } @@ -352,15 +342,20 @@ public: return; } - QJsonObject scope = debugService->buildScope(frameNr, scopeNr, debugger); + ScopeJob job(debugService->collector(), frameNr, scopeNr); + debugService->debuggerAgent.firstDebugger()->runInEngine(&job); + if (!job.wasSuccessful()) { + createErrorResponse(QStringLiteral("scope retrieval failed")); + return; + } // response: addCommand(); addRequestSequence(); addSuccess(true); addRunning(); - addBody(scope); - addRefs(); + addBody(job.returnValue()); + addRefs(job.refs()); } }; @@ -375,17 +370,19 @@ public: QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); QJsonArray handles = arguments.value(QStringLiteral("handles")).toArray(); - QJsonObject body; - foreach (const QJsonValue &handle, handles) - body[QString::number(handle.toInt())] = debugService->lookup(handle.toInt()); - - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - addBody(body); - addRefs(); + ValueLookupJob job(handles, debugService->collector()); + debugService->debuggerAgent.firstDebugger()->runInEngine(&job); + if (!job.exceptionMessage().isEmpty()) { + createErrorResponse(job.exceptionMessage()); + } else { + // response: + addCommand(); + addRequestSequence(); + addSuccess(true); + addRunning(); + addBody(job.returnValue()); + addRefs(job.refs()); + } } }; @@ -557,7 +554,6 @@ public: const int frame = arguments.value(QStringLiteral("frame")).toInt(0); QV4DataCollector *collector = debugService->collector(); - RefHolder holder(collector, debugService->refs()); ExpressionEvalJob job(debugger->engine(), frame, expression, collector); debugger->runInEngine(&job); if (job.hasExeption()) { @@ -567,8 +563,8 @@ public: addRequestSequence(); addSuccess(true); addRunning(); - addBody(collector->lookupRef(debugService->refs()->last())); - addRefs(); + addBody(job.returnValue()); + addRefs(job.refs()); } } else { createErrorResponse(QStringLiteral("Debugger has to be paused for evaluate to work.")); @@ -789,135 +785,11 @@ void QV4DebugServiceImpl::clearHandles(QV4::ExecutionEngine *engine) theCollector.reset(new QV4DataCollector(engine)); } -QJsonObject QV4DebugServiceImpl::buildFrame(const QV4::StackFrame &stackFrame, int frameNr, - QV4::Debugging::V4Debugger *debugger) -{ - QV4DataCollector::Ref ref; - - QJsonObject frame; - frame[QLatin1String("index")] = frameNr; - frame[QLatin1String("debuggerFrame")] = false; - ref = theCollector->addFunctionRef(stackFrame.function); - collectedRefs.append(ref); - frame[QLatin1String("func")] = toRef(ref); - ref = theCollector->addScriptRef(stackFrame.source); - collectedRefs.append(ref); - frame[QLatin1String("script")] = toRef(ref); - frame[QLatin1String("line")] = stackFrame.line - 1; - if (stackFrame.column >= 0) - frame[QLatin1String("column")] = stackFrame.column; - - QJsonArray scopes; - if (debugger->state() == QV4::Debugging::V4Debugger::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()); - - // Only type and index are used by Qt Creator, so we keep it easy: - QVector 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; - - return frame; -} - -int QV4DebugServiceImpl::encodeScopeType(QV4::Heap::ExecutionContext::ContextType scopeType) -{ - switch (scopeType) { - case QV4::Heap::ExecutionContext::Type_GlobalContext: - return 0; - break; - case QV4::Heap::ExecutionContext::Type_CatchContext: - return 4; - break; - case QV4::Heap::ExecutionContext::Type_WithContext: - return 2; - break; - case QV4::Heap::ExecutionContext::Type_SimpleCallContext: - case QV4::Heap::ExecutionContext::Type_CallContext: - return 1; - break; - case QV4::Heap::ExecutionContext::Type_QmlContext: - default: - return -1; - } -} - -QJsonObject QV4DebugServiceImpl::buildScope(int frameNr, int scopeNr, - QV4::Debugging::V4Debugger *debugger) -{ - QJsonObject scope; - - QJsonObject object; - RefHolder holder(theCollector.data(), &collectedRefs); - theCollector->collectScope(&object, debugger, frameNr, scopeNr); - - if (debugger->state() == QV4::Debugging::V4Debugger::Paused) { - QVector 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; - - return scope; -} - -QJsonValue QV4DebugServiceImpl::lookup(QV4DataCollector::Ref refId) -{ - RefHolder holder(theCollector.data(), &collectedRefs); - return theCollector->lookupRef(refId); -} - -QJsonArray QV4DebugServiceImpl::buildRefs() -{ - QJsonArray refs; - std::sort(collectedRefs.begin(), collectedRefs.end()); - for (int i = 0, ei = collectedRefs.size(); i != ei; ++i) { - QV4DataCollector::Ref ref = collectedRefs.at(i); - if (i > 0 && ref == collectedRefs.at(i - 1)) - continue; - refs.append(lookup(ref)); - } - - collectedRefs.clear(); - return refs; -} - -QJsonValue QV4DebugServiceImpl::toRef(QV4DataCollector::Ref ref) -{ - QJsonObject dict; - dict.insert(QStringLiteral("ref"), qint64(ref)); - return dict; -} - QV4DataCollector *QV4DebugServiceImpl::collector() const { return theCollector.data(); } -QV4DataCollector::Refs *QV4DebugServiceImpl::refs() -{ - return &collectedRefs; -} - void QV4DebugServiceImpl::selectFrame(int frameNr) { theSelectedFrame = frameNr; diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h index 273e5ffd62..658b1b3998 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h @@ -78,13 +78,6 @@ public: void signalEmitted(const QString &signal); void send(QJsonObject v8Payload); - QJsonObject buildScope(int frameNr, int scopeNr, QV4::Debugging::V4Debugger *debugger); - QJsonArray buildRefs(); - QJsonValue lookup(QV4DataCollector::Ref refId); - QJsonValue toRef(QV4DataCollector::Ref ref); - - QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr, - QV4::Debugging::V4Debugger *debugger); int selectedFrame() const; void selectFrame(int frameNr); @@ -92,7 +85,6 @@ public: QV4DataCollector *collector() const; QV4DebuggerAgent debuggerAgent; - QV4DataCollector::Refs *refs(); protected: void messageReceived(const QByteArray &); @@ -106,14 +98,12 @@ private: const QByteArray &message = QByteArray()); void processCommand(const QByteArray &command, const QByteArray &data); V8CommandHandler *v8CommandHandler(const QString &command) const; - int encodeScopeType(QV4::Heap::ExecutionContext::ContextType scopeType); QStringList breakOnSignals; QMap debuggerMap; static int debuggerIndex; static int sequence; const int version; - QV4DataCollector::Refs collectedRefs; QScopedPointer theCollector; int theSelectedFrame; -- cgit v1.2.3