From ebb08ee84e8141042ed16dfc5892697ef96077c4 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 30 Nov 2015 12:04:59 +0100 Subject: [PATCH] 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: Id93548dc65133246deac71f73188c715e9dc01e4 Reviewed-by: Simon Hausmann --- .../qmldbg_debugger/qv4datacollector.cpp | 110 ++++++++++- .../qmldbg_debugger/qv4datacollector.h | 4 +- .../qmldbg_debugger/qv4debugjob.cpp | 177 +++++++----------- .../qmltooling/qmldbg_debugger/qv4debugjob.h | 91 +++++---- .../qmldbg_debugger/qv4debugservice.cpp | 139 +++----------- .../qmldbg_debugger/qv4debugservice.h | 6 - .../debugger/qv4debugger/tst_qv4debugger.cpp | 107 ++++++----- 7 files changed, 305 insertions(+), 329 deletions(-) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp index b1d928db52..a3f59870a2 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -104,6 +104,28 @@ 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_values.set(engine, engine->newArrayObject()); @@ -227,19 +249,35 @@ bool QV4DataCollector::isValidRef(QV4DataCollector::Ref ref) const return ref < array->getLength(); } -void QV4DataCollector::collectScope(QJsonObject *dict, QV4Debugger *debugger, int frameNr, - int scopeNr) +bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr) { QStringList names; - if (debugger->state() == QV4Debugger::Paused) { - 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::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() == m_collectedRefs.size()); @@ -250,6 +288,62 @@ void QV4DataCollector::collectScope(QJsonObject *dict, QV4Debugger *debugger, in Ref scopeObjectRef = addRef(scopeObject); dict->insert(QStringLiteral("ref"), qint64(scopeObjectRef)); m_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) +{ + 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; + 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() diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h index 1679c2eab7..1c3a05960c 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h @@ -60,6 +60,7 @@ 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); @@ -70,7 +71,8 @@ public: bool isValidRef(Ref ref) const; QJsonObject lookupRef(Ref ref); - void collectScope(QJsonObject *dict, QV4Debugger *debugger, int frameNr, int scopeNr); + bool collectScope(QJsonObject *dict, int frameNr, int scopeNr); + QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr); QV4::ExecutionEngine *engine() const { return m_engine; } QJsonArray flushCollectedRefs(); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp index 020be688f3..d60db6cf82 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp @@ -118,8 +118,79 @@ bool JavaScriptJob::hasExeption() const return resultIsException; } +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); + for (int i = fromFrame; i < toFrame && i < frames.size(); ++i) + frameArray.push_back(collector->buildFrame(frames[i], i)); + 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 { + result = collector->buildFrame(frames[frameNr], frameNr); + collectedRefs = collector->flushCollectedRefs(); + success = true; + } +} + +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; + success = collector->collectScope(&object, 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) : - collector(collector), handles(handles) {} + CollectJob(collector), handles(handles) {} void ValueLookupJob::run() { @@ -152,16 +223,6 @@ const QString &ValueLookupJob::exceptionMessage() const return exception; } -const QJsonObject &ValueLookupJob::returnValue() const -{ - return result; -} - -const QJsonArray &ValueLookupJob::refs() const -{ - return collectedRefs; -} - ExpressionEvalJob::ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr, const QString &expression, QV4DataCollector *collector) : JavaScriptJob(engine, frameNr, expression), collector(collector) @@ -209,100 +270,6 @@ const QStringList &GatherSourcesJob::result() const return sources; } -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) - : engine(engine) - , collector(collector) - , frameNr(frameNr) - , thisRef(QV4DataCollector::s_invalidRef) -{} - -void ThisCollectJob::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; - - QV4::ScopedValue o(scope, ctxt->asCallContext()->d()->activation); - thisRef = collector->collect(o); -} - -QV4DataCollector::Ref ThisCollectJob::foundRef() const -{ - return thisRef; -} - ExceptionCollectJob::ExceptionCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector) : engine(engine) , collector(collector) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h index e48b5b1e5e..a1adbeff40 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h @@ -72,20 +72,59 @@ protected: virtual void handleResult(QV4::ScopedValue &result) = 0; }; -class ValueLookupJob: public QV4DebugJob +class CollectJob : public QV4DebugJob { +protected: QV4DataCollector *collector; - const QJsonArray handles; QJsonObject result; QJsonArray collectedRefs; +public: + CollectJob(QV4DataCollector *collector) : collector(collector) {} + const QJsonObject &returnValue() const { return result; } + const QJsonArray &refs() const { return collectedRefs; } +}; + +class BacktraceJob: public CollectJob +{ + int fromFrame; + int toFrame; +public: + BacktraceJob(QV4DataCollector *collector, int fromFrame, int toFrame); + void run(); +}; + +class FrameJob: public CollectJob +{ + int frameNr; + bool success; + +public: + FrameJob(QV4DataCollector *collector, int frameNr); + void run(); + bool wasSuccessful() const; +}; + +class ScopeJob: public CollectJob +{ + int frameNr; + int scopeNr; + bool success; + +public: + ScopeJob(QV4DataCollector *collector, int frameNr, int scopeNr); + void run(); + bool wasSuccessful() const; +}; + +class ValueLookupJob: public CollectJob +{ + const QJsonArray handles; QString exception; public: ValueLookupJob(const QJsonArray &handles, QV4DataCollector *collector); void run(); const QString &exceptionMessage() const; - const QJsonObject &returnValue() const; - const QJsonArray &refs() const; }; class ExpressionEvalJob: public JavaScriptJob @@ -115,47 +154,6 @@ public: const QStringList &result() const; }; -class ArgumentCollectJob: public QV4DebugJob -{ - 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 QV4DebugJob -{ - 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 QV4DebugJob -{ - QV4::ExecutionEngine *engine; - QV4DataCollector *collector; - int frameNr; - QV4DataCollector::Ref thisRef; - -public: - ThisCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector, int frameNr); - void run(); - QV4DataCollector::Ref foundRef() const; -}; - class ExceptionCollectJob: public QV4DebugJob { QV4::ExecutionEngine *engine; @@ -176,8 +174,7 @@ public: EvalJob(QV4::ExecutionEngine *engine, const QString &script); virtual void handleResult(QV4::ScopedValue &result); - bool resultAsBoolean() const -; + bool resultAsBoolean() const; }; QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp index ba953eb21d..5ee9e5e9e9 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp @@ -285,26 +285,16 @@ public: return; } - 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(debugger->collector(), fromFrame, toFrame); + debugger->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(debugger->collector()->flushCollectedRefs()); + addBody(job.returnValue()); + addRefs(job.refs()); } }; @@ -326,22 +316,27 @@ public: return; } - 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(debugger->collector(), frameNr); + debugger->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(debugger->collector()->flushCollectedRefs()); + addBody(job.returnValue()); + addRefs(job.refs()); } }; @@ -364,8 +359,7 @@ public: return; } - QVector frames = debugger->stackTrace(frameNr + 1); - if (frameNr < 0 || frameNr >= frames.size()) { + if (frameNr < 0) { createErrorResponse(QStringLiteral("scope command has invalid frame number")); return; } @@ -374,15 +368,20 @@ public: return; } - QJsonObject scope = debugService->buildScope(frameNr, scopeNr, debugger); + ScopeJob job(debugger->collector(), frameNr, scopeNr); + debugger->runInEngine(&job); + if (!job.wasSuccessful()) { + createErrorResponse(QStringLiteral("scope retrieval failed")); + return; + } // response: addCommand(); addRequestSequence(); addSuccess(true); addRunning(); - addBody(scope); - addRefs(debugger->collector()->flushCollectedRefs()); + addBody(job.returnValue()); + addRefs(job.refs()); } }; @@ -836,98 +835,6 @@ void QV4DebugServiceImpl::send(QJsonObject v8Payload) emit messageToClient(name(), packMessage("v8message", responseData)); } -QJsonObject QV4DebugServiceImpl::buildFrame(const QV4::StackFrame &stackFrame, int frameNr, - QV4Debugger *debugger) -{ - QV4DataCollector::Ref ref; - - QJsonObject frame; - frame[QLatin1String("index")] = frameNr; - frame[QLatin1String("debuggerFrame")] = false; - ref = debugger->collector()->addFunctionRef(stackFrame.function); - frame[QLatin1String("func")] = toRef(ref); - ref = debugger->collector()->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; - Q_ASSERT (debugger->state() == QV4Debugger::Paused); - - ThisCollectJob job(debugger->engine(), debugger->collector(), frameNr); - debugger->runInEngine(&job); - if (job.foundRef() != QV4DataCollector::s_invalidRef) - frame[QLatin1String("receiver")] = toRef(job.foundRef()); - - // 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, QV4Debugger *debugger) -{ - QJsonObject scope; - - QJsonObject object; - debugger->collector()->collectScope(&object, debugger, frameNr, scopeNr); - - if (debugger->state() == QV4Debugger::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::toRef(QV4DataCollector::Ref ref) -{ - QJsonObject dict; - dict.insert(QStringLiteral("ref"), qint64(ref)); - return dict; -} - 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 df9da2c07f..69e32189b8 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h @@ -83,10 +83,6 @@ public: void signalEmitted(const QString &signal) Q_DECL_OVERRIDE; void send(QJsonObject v8Payload); - QJsonObject buildScope(int frameNr, int scopeNr, QV4Debugger *debugger); - QJsonValue toRef(QV4DataCollector::Ref ref); - - QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr, QV4Debugger *debugger); int selectedFrame() const; void selectFrame(int frameNr); @@ -104,11 +100,9 @@ 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; static int sequence; - int theSelectedFrame; void addHandler(V8CommandHandler* handler); diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index fd504c50d3..7398e97326 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -100,16 +100,24 @@ public: typedef QV4DataCollector::Refs Refs; typedef QV4DataCollector::Ref Ref; struct NamedRefs { - QStringList names; QJsonArray refs; + QJsonObject scope; int size() const { - Q_ASSERT(names.size() == refs.size()); - return names.size(); + return scope.size(); } bool contains(const QString &name) const { - return names.contains(name); + return scope.contains(name); + } + + QJsonObject resolveRef(int ref) const { + foreach (const QJsonValue &val, refs) { + QJsonObject obj = val.toObject(); + if (obj.value(QLatin1String("handle")).toInt() == ref) + return obj; + } + return QJsonObject(); } #define DUMP_JSON(x) {\ @@ -119,7 +127,7 @@ public: QJsonObject rawValue(const QString &name) const { Q_ASSERT(contains(name)); - return refs.at(names.indexOf(name)).toObject(); + return scope[name].toObject(); } QJsonValue value(const QString &name) const { @@ -136,7 +144,7 @@ public: return; } - QJsonObject o = refs.at(names.indexOf(name)).toObject(); + QJsonObject o = scope[name].toObject(); QJsonDocument d; d.setObject(o); qDebug() << name << "=" << d.toJson(QJsonDocument::Indented); @@ -202,17 +210,30 @@ public: void captureContextInfo(QV4Debugger *debugger) { for (int i = 0, ei = m_stackTrace.size(); i != ei; ++i) { - m_capturedArguments.append(NamedRefs()); - ArgumentCollectJob argumentsJob(debugger->engine(), &collector, - &m_capturedArguments.last().names, i, 0); - debugger->runInEngine(&argumentsJob); - m_capturedArguments.last().refs = collector.flushCollectedRefs(); - - m_capturedLocals.append(NamedRefs()); - LocalCollectJob localsJob(debugger->engine(), &collector, - &m_capturedLocals.last().names, i, 0); - debugger->runInEngine(&localsJob); - m_capturedLocals.last().refs = collector.flushCollectedRefs(); + m_capturedScope.append(NamedRefs()); + ScopeJob job(&collector, i, 0); + debugger->runInEngine(&job); + NamedRefs &refs = m_capturedScope.last(); + refs.refs = job.refs(); + QJsonObject object = job.returnValue(); + object = object.value(QLatin1String("object")).toObject(); + int ref = object.value(QLatin1String("ref")).toInt(); + object = refs.resolveRef(ref); + foreach (const QJsonValue &value, object.value(QLatin1String("properties")).toArray()) { + QJsonObject property = value.toObject(); + QString name = property.value(QLatin1String("name")).toString(); + property.remove(QLatin1String("name")); + if (property.contains(QLatin1String("ref"))) { + int childRef = property.value(QLatin1String("ref")).toInt(); + if (childRef >= 0 && refs.refs.size() > childRef) { + property.remove(QLatin1String("ref")); + property.insert(QLatin1String("properties"), + refs.resolveRef(childRef).value( + QLatin1String("properties")).toArray()); + } + } + refs.scope.insert(name, property); + } } } @@ -229,8 +250,7 @@ public: QList m_statesWhenPaused; QList m_breakPointsToAddWhenPaused; QVector m_stackTrace; - QVector m_capturedArguments; - QVector m_capturedLocals; + QVector m_capturedScope; qint64 m_thrownValue; QV4DataCollector collector; @@ -444,8 +464,8 @@ void tst_qv4debugger::conditionalBreakPoint() QCOMPARE(state.fileName, QString("conditionalBreakPoint")); QCOMPARE(state.lineNumber, 3); - QVERIFY(m_debuggerAgent->m_capturedLocals.size() > 1); - const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedLocals.at(0); + QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1); + const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0); QCOMPARE(frame0.size(), 2); QVERIFY(frame0.contains("i")); QCOMPARE(frame0.value("i").toInt(), 11); @@ -501,13 +521,13 @@ void tst_qv4debugger::readArguments() debugger()->addBreakPoint("readArguments", 2); evaluateJavaScript(script, "readArguments"); QVERIFY(m_debuggerAgent->m_wasPaused); - QVERIFY(m_debuggerAgent->m_capturedArguments.size() > 1); - const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedArguments.at(0); + QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1); + const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0); QCOMPARE(frame0.size(), 4); QVERIFY(frame0.contains(QStringLiteral("a"))); QCOMPARE(frame0.type(QStringLiteral("a")), QStringLiteral("number")); QCOMPARE(frame0.value(QStringLiteral("a")).toDouble(), 1.0); - QVERIFY(frame0.names.contains("b")); + QVERIFY(frame0.scope.contains("b")); QCOMPARE(frame0.type(QStringLiteral("b")), QStringLiteral("string")); QCOMPARE(frame0.value(QStringLiteral("b")).toString(), QStringLiteral("two")); } @@ -525,9 +545,9 @@ void tst_qv4debugger::readLocals() debugger()->addBreakPoint("readLocals", 3); evaluateJavaScript(script, "readLocals"); QVERIFY(m_debuggerAgent->m_wasPaused); - QVERIFY(m_debuggerAgent->m_capturedLocals.size() > 1); - const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedLocals.at(0); - QCOMPARE(frame0.size(), 2); + QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1); + const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0); + QCOMPARE(frame0.size(), 4); // locals and parameters QVERIFY(frame0.contains("c")); QCOMPARE(frame0.type("c"), QStringLiteral("number")); QCOMPARE(frame0.value("c").toDouble(), 3.0); @@ -547,9 +567,9 @@ void tst_qv4debugger::readObject() debugger()->addBreakPoint("readObject", 3); evaluateJavaScript(script, "readObject"); QVERIFY(m_debuggerAgent->m_wasPaused); - QVERIFY(m_debuggerAgent->m_capturedLocals.size() > 1); - const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedLocals.at(0); - QCOMPARE(frame0.size(), 1); + QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1); + const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0); + QCOMPARE(frame0.size(), 2); QVERIFY(frame0.contains("b")); QCOMPARE(frame0.type("b"), QStringLiteral("object")); QJsonObject b = frame0.rawValue("b"); @@ -600,28 +620,23 @@ void tst_qv4debugger::readContextInAllFrames() evaluateJavaScript(script, "readFormalsInAllFrames"); QVERIFY(m_debuggerAgent->m_wasPaused); QCOMPARE(m_debuggerAgent->m_stackTrace.size(), 13); - QCOMPARE(m_debuggerAgent->m_capturedArguments.size(), 13); - QCOMPARE(m_debuggerAgent->m_capturedLocals.size(), 13); + QCOMPARE(m_debuggerAgent->m_capturedScope.size(), 13); for (int i = 0; i < 12; ++i) { - const TestAgent::NamedRefs &args = m_debuggerAgent->m_capturedArguments.at(i); - QCOMPARE(args.size(), 1); - QVERIFY(args.contains("n")); - QCOMPARE(args.type("n"), QStringLiteral("number")); - QCOMPARE(args.value("n").toDouble(), i + 1.0); - - const TestAgent::NamedRefs &locals = m_debuggerAgent->m_capturedLocals.at(i); - QCOMPARE(locals.size(), 1); - QVERIFY(locals.contains("n_1")); + const TestAgent::NamedRefs &scope = m_debuggerAgent->m_capturedScope.at(i); + QCOMPARE(scope.size(), 2); + QVERIFY(scope.contains("n")); + QCOMPARE(scope.type("n"), QStringLiteral("number")); + QCOMPARE(scope.value("n").toDouble(), i + 1.0); + QVERIFY(scope.contains("n_1")); if (i == 0) { - QCOMPARE(locals.type("n_1"), QStringLiteral("undefined")); + QCOMPARE(scope.type("n_1"), QStringLiteral("undefined")); } else { - QCOMPARE(locals.type("n_1"), QStringLiteral("number")); - QCOMPARE(locals.value("n_1").toInt(), i); + QCOMPARE(scope.type("n_1"), QStringLiteral("number")); + QCOMPARE(scope.value("n_1").toInt(), i); } } - QCOMPARE(m_debuggerAgent->m_capturedArguments[12].size(), 0); - QCOMPARE(m_debuggerAgent->m_capturedLocals[12].size(), 0); + QCOMPARE(m_debuggerAgent->m_capturedScope[12].size(), 0); } void tst_qv4debugger::pauseOnThrow()