diff options
5 files changed, 434 insertions, 414 deletions
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<QV4::Heap::ExecutionContext::ContextType> 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<QV4::CallContext> 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<QV4::Heap::ExecutionContext::ContextType> 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<QV4::StackFrame> frames = collector->engine()->stackTrace(toFrame); + QV4::Debugging::V4Debugger *debugger = + qobject_cast<QV4::Debugging::V4Debugger *>(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<QV4::StackFrame> frames = collector->engine()->stackTrace(frameNr + 1); + if (frameNr >= frames.length()) { + success = false; + } else { + QV4::Debugging::V4Debugger *debugger = + qobject_cast<QV4::Debugging::V4Debugger *>(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<QV4::Debugging::V4Debugger *>(collector->engine()->debugger); + success = (debugger && collector->collectScope(&object, debugger, frameNr, scopeNr)); + + if (success) { + QVector<QV4::Heap::ExecutionContext::ContextType> 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<QV4::CallContext> 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<QV4::CallContext> 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 <private/qv4engine_p.h> #include <private/qv4debugging_p.h> +#include <QtCore/QJsonObject> +#include <QtCore/QJsonArray> + QT_BEGIN_NAMESPACE class QV4DataCollector @@ -49,25 +52,27 @@ public: static QV4::Heap::CallContext *findScope(QV4::ExecutionContext *ctxt, int scope); static QVector<QV4::Heap::ExecutionContext::ContextType> 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<Ref, QJsonObject> 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<QV4::StackFrame> 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<QV4::StackFrame> 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<QV4::StackFrame> 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<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; - - 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<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; - - 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<int, QV4::Debugging::V4Debugger *> debuggerMap; static int debuggerIndex; static int sequence; const int version; - QV4DataCollector::Refs collectedRefs; QScopedPointer<QV4DataCollector> theCollector; int theSelectedFrame; diff --git a/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp index 39a1fbc173..2532f73466 100644 --- a/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp @@ -103,19 +103,24 @@ public: typedef QV4DataCollector::Refs Refs; typedef QV4DataCollector::Ref Ref; struct NamedRefs { - NamedRefs(QV4DataCollector *collector = 0): collector(collector) {} - - QStringList names; - Refs refs; - QV4DataCollector *collector; + 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) {\ @@ -125,7 +130,7 @@ public: QJsonObject rawValue(const QString &name) const { Q_ASSERT(contains(name)); - return collector->lookupRef(refs.at(names.indexOf(name))); + return scope[name].toObject(); } QJsonValue value(const QString &name) const { @@ -142,7 +147,7 @@ public: return; } - QJsonObject o = collector->lookupRef(refs.at(names.indexOf(name))); + QJsonObject o = scope[name].toObject(); QJsonDocument d; d.setObject(o); qDebug() << name << "=" << d.toJson(QJsonDocument::Indented); @@ -169,12 +174,9 @@ public slots: if (debugger->state() == V4Debugger::Paused && debugger->engine()->hasException) { - Refs refs; - RefHolder holder(&collector, &refs); ExceptionCollectJob job(debugger->engine(), &collector); debugger->runInEngine(&job); - Q_ASSERT(refs.size() > 0); - m_thrownValue = refs.first(); + m_thrownValue = job.exceptionValue(); } foreach (const TestBreakPoint &bp, m_breakPointsToAddWhenPaused) @@ -186,11 +188,11 @@ public slots: while (!m_expressionRequests.isEmpty()) { Q_ASSERT(debugger->state() == V4Debugger::Paused); ExpressionRequest request = m_expressionRequests.takeFirst(); - m_expressionResults << Refs(); - RefHolder holder(&collector, &m_expressionResults.last()); ExpressionEvalJob job(debugger->engine(), request.frameNr, request.expression, &collector); debugger->runInEngine(&job); + m_expressionResults << job.returnValue(); + m_expressionRefs << job.refs(); } if (m_captureContextInfo) @@ -212,17 +214,30 @@ public: void captureContextInfo(V4Debugger *debugger) { for (int i = 0, ei = m_stackTrace.size(); i != ei; ++i) { - m_capturedArguments.append(NamedRefs(&collector)); - RefHolder argHolder(&collector, &m_capturedArguments.last().refs); - 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); - LocalCollectJob localsJob(debugger->engine(), &collector, - &m_capturedLocals.last().names, i, 0); - debugger->runInEngine(&localsJob); + 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); + } } } @@ -240,8 +255,7 @@ public: QList<V4Debugger::ExecutionState> m_statesWhenPaused; QList<TestBreakPoint> m_breakPointsToAddWhenPaused; QVector<QV4::StackFrame> m_stackTrace; - QVector<NamedRefs> m_capturedArguments; - QVector<NamedRefs> m_capturedLocals; + QVector<NamedRefs> m_capturedScope; qint64 m_thrownValue; QV4DataCollector collector; @@ -250,7 +264,8 @@ public: int frameNr; }; QVector<ExpressionRequest> m_expressionRequests; - QVector<Refs> m_expressionResults; + QList<QJsonObject> m_expressionResults; + QList<QJsonArray> m_expressionRefs; V4Debugger *m_debugger; // Utility methods: @@ -454,8 +469,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); @@ -511,13 +526,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")); } @@ -535,9 +550,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); @@ -557,9 +572,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"); @@ -578,7 +593,7 @@ void tst_qv4debugger::readObject() QCOMPARE(b_tail.value("name").toString(), QStringLiteral("tail")); QVERIFY(b_tail.contains("ref")); - QJsonObject b_tail_value = frame0.collector->lookupRef(b_tail.value("ref").toInt()); + QJsonObject b_tail_value = m_debuggerAgent->collector.lookupRef(b_tail.value("ref").toInt()); QCOMPARE(b_tail_value.value("type").toString(), QStringLiteral("object")); QVERIFY(b_tail_value.contains("properties")); QJsonArray b_tail_props = b_tail_value.value("properties").toArray(); @@ -610,28 +625,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() @@ -711,15 +721,13 @@ void tst_qv4debugger::evaluateExpression() evaluateJavaScript(script, "evaluateExpression"); - QCOMPARE(m_debuggerAgent->m_expressionResults.count(), 2); - QCOMPARE(m_debuggerAgent->m_expressionResults[0].size(), 1); - QJsonObject result0 = - m_debuggerAgent->collector.lookupRef(m_debuggerAgent->m_expressionResults[0].first()); + QCOMPARE(m_debuggerAgent->m_expressionRefs.count(), 2); + QCOMPARE(m_debuggerAgent->m_expressionRefs[0].size(), 1); + QJsonObject result0 = m_debuggerAgent->m_expressionRefs[0].first().toObject(); QCOMPARE(result0.value("type").toString(), QStringLiteral("number")); QCOMPARE(result0.value("value").toInt(), 10); - QCOMPARE(m_debuggerAgent->m_expressionResults[1].size(), 1); - QJsonObject result1 = - m_debuggerAgent->collector.lookupRef(m_debuggerAgent->m_expressionResults[1].first()); + QCOMPARE(m_debuggerAgent->m_expressionRefs[1].size(), 1); + QJsonObject result1 = m_debuggerAgent->m_expressionRefs[1].first().toObject(); QCOMPARE(result1.value("type").toString(), QStringLiteral("number")); QCOMPARE(result1.value("value").toInt(), 20); } |