From eae5a20b099450985442c70186fd7a7be442f133 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 30 Nov 2015 12:04:59 +0100 Subject: V4 Debugger: Allow expression evaluation without pausing We can schedule jobs into the GUI thread just fine, even if the debugger is running. They will run in global scope then. The only restriction is that we need exactly one engine to be running in order to do that, as otherwise we cannot decide which engine to use. To avoid interaction with the engine from the debugger thread we move the value lookup functionality into the data collector, and drop the RefHolder. Change-Id: Ifae124d70f42e488ed9a1b6794baef638992ddb1 Reviewed-by: Simon Hausmann --- .../qmldbg_debugger/qv4datacollector.cpp | 151 +++++++++++++----- .../qmltooling/qmldbg_debugger/qv4datacollector.h | 67 ++++---- .../qmltooling/qmldbg_debugger/qv4debugger.cpp | 25 ++- .../qmltooling/qmldbg_debugger/qv4debugger.h | 7 + .../qmldbg_debugger/qv4debuggeragent.cpp | 2 +- .../qmltooling/qmldbg_debugger/qv4debugservice.cpp | 174 +++++++++------------ .../qmltooling/qmldbg_debugger/qv4debugservice.h | 8 - 7 files changed, 257 insertions(+), 177 deletions(-) (limited to 'src/plugins') diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp index 0469cd51d6..ac46905f1c 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -44,6 +44,9 @@ QT_BEGIN_NAMESPACE +const QV4DataCollector::Ref QV4DataCollector::s_invalidRef = + std::numeric_limits::max(); + QV4::CallContext *QV4DataCollector::findContext(QV4::ExecutionEngine *engine, int frame) { QV4::ExecutionContext *ctx = engine->currentContext; @@ -90,21 +93,16 @@ QVector QV4DataCollector::getScopeType return types; } - -QV4DataCollector::QV4DataCollector(QV4::ExecutionEngine *engine) - : m_engine(engine), m_collectedRefs(Q_NULLPTR) +QV4DataCollector::QV4DataCollector(QV4::ExecutionEngine *engine) : m_engine(engine) { - values.set(engine, engine->newArrayObject()); + m_values.set(engine, engine->newArrayObject()); } -QV4DataCollector::~QV4DataCollector() +QV4DataCollector::Ref QV4DataCollector::collect(const QV4::ScopedValue &value) { -} - -void QV4DataCollector::collect(const QV4::ScopedValue &value) -{ - if (m_collectedRefs) - m_collectedRefs->append(addRef(value)); + Ref ref = addRef(value); + m_collectedRefs.append(ref); + return ref; } const QV4::Object *collectProperty(const QV4::ScopedValue &value, QV4::ExecutionEngine *engine, @@ -191,7 +189,8 @@ QV4DataCollector::Ref QV4DataCollector::addFunctionRef(const QString &functionNa dict.insert(QStringLiteral("handle"), qint64(ref)); dict.insert(QStringLiteral("type"), QStringLiteral("function")); dict.insert(QStringLiteral("name"), functionName); - specialRefs.insert(ref, dict); + m_specialRefs.insert(ref, dict); + m_collectedRefs.append(ref); return ref; } @@ -204,19 +203,25 @@ QV4DataCollector::Ref QV4DataCollector::addScriptRef(const QString &scriptName) dict.insert(QStringLiteral("handle"), qint64(ref)); dict.insert(QStringLiteral("type"), QStringLiteral("script")); dict.insert(QStringLiteral("name"), scriptName); - specialRefs.insert(ref, dict); + m_specialRefs.insert(ref, dict); + m_collectedRefs.append(ref); return ref; } +bool QV4DataCollector::isValidRef(QV4DataCollector::Ref ref) const +{ + QV4::Scope scope(engine()); + QV4::ScopedObject array(scope, m_values.value()); + return ref < array->getLength(); +} + void QV4DataCollector::collectScope(QJsonObject *dict, QV4Debugger *debugger, int frameNr, int scopeNr) { QStringList names; - Refs refs; if (debugger->state() == QV4Debugger::Paused) { - RefHolder holder(this, &refs); ArgumentCollectJob argumentsJob(m_engine, this, &names, frameNr, scopeNr); debugger->runInEngine(&argumentsJob); LocalCollectJob localsJob(m_engine, this, &names, frameNr, scopeNr); @@ -226,15 +231,36 @@ void QV4DataCollector::collectScope(QJsonObject *dict, QV4Debugger *debugger, in 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() == m_collectedRefs.size()); + for (int i = 0, ei = m_collectedRefs.size(); i != ei; ++i) scopeObject->put(engine(), names.at(i), - QV4::Value::fromReturnedValue(getValue(refs.at(i)))); + QV4::Value::fromReturnedValue(getValue(m_collectedRefs.at(i)))); Ref scopeObjectRef = addRef(scopeObject); dict->insert(QStringLiteral("ref"), qint64(scopeObjectRef)); - if (m_collectedRefs) - m_collectedRefs->append(scopeObjectRef); + m_collectedRefs.append(scopeObjectRef); +} + +QJsonArray QV4DataCollector::flushCollectedRefs() +{ + QJsonArray refs; + std::sort(m_collectedRefs.begin(), m_collectedRefs.end()); + for (int i = 0, ei = m_collectedRefs.size(); i != ei; ++i) { + QV4DataCollector::Ref ref = m_collectedRefs.at(i); + if (i > 0 && ref == m_collectedRefs.at(i - 1)) + continue; + refs.append(lookupRef(ref)); + } + + m_collectedRefs.clear(); + return refs; +} + +void QV4DataCollector::clear() +{ + m_values.set(engine(), engine()->newArrayObject()); + m_collectedRefs.clear(); + m_specialRefs.clear(); } QV4DataCollector::Ref QV4DataCollector::addRef(QV4::Value value, bool deduplicate) @@ -257,10 +283,10 @@ QV4DataCollector::Ref QV4DataCollector::addRef(QV4::Value value, bool deduplicat // if we wouldn't do this, the putIndexed won't work. ExceptionStateSaver resetExceptionState(engine()); QV4::Scope scope(engine()); - QV4::ScopedObject array(scope, values.value()); + QV4::ScopedObject array(scope, m_values.value()); if (deduplicate) { for (Ref i = 0; i < array->getLength(); ++i) { - if (array->getIndexed(i) == value.rawValue() && !specialRefs.contains(i)) + if (array->getIndexed(i) == value.rawValue() && !m_specialRefs.contains(i)) return i; } } @@ -273,15 +299,15 @@ QV4DataCollector::Ref QV4DataCollector::addRef(QV4::Value value, bool deduplicat QV4::ReturnedValue QV4DataCollector::getValue(Ref ref) { QV4::Scope scope(engine()); - QV4::ScopedObject array(scope, values.value()); + QV4::ScopedObject array(scope, m_values.value()); Q_ASSERT(ref < array->getLength()); return array->getIndexed(ref, Q_NULLPTR); } bool QV4DataCollector::lookupSpecialRef(Ref ref, QJsonObject *dict) { - SpecialRefs::const_iterator it = specialRefs.constFind(ref); - if (it == specialRefs.cend()) + SpecialRefs::const_iterator it = m_specialRefs.constFind(ref); + if (it == m_specialRefs.cend()) return false; *dict = it.value(); @@ -317,14 +343,41 @@ 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); + m_collectedRefs.append(ref); } collectProperty(value, engine(), dict); return dict; } +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; +} + +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) @@ -333,11 +386,12 @@ ExpressionEvalJob::ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr, { } -void ExpressionEvalJob::handleResult(QV4::ScopedValue &result) +void ExpressionEvalJob::handleResult(QV4::ScopedValue &value) { if (hasExeption()) - exception = result->toQStringNoThrow(); - collector->collect(result); + exception = value->toQStringNoThrow(); + result = collector->lookupRef(collector->collect(value)); + collectedRefs = collector->flushCollectedRefs(); } const QString &ExpressionEvalJob::exceptionMessage() const @@ -345,6 +399,16 @@ const QString &ExpressionEvalJob::exceptionMessage() const return exception; } +const QJsonObject &ExpressionEvalJob::returnValue() const +{ + return result; +} + +const QJsonArray &ExpressionEvalJob::refs() const +{ + return collectedRefs; +} + GatherSourcesJob::GatherSourcesJob(QV4::ExecutionEngine *engine) : engine(engine) {} @@ -427,19 +491,14 @@ void LocalCollectJob::run() } ThisCollectJob::ThisCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector, - int frameNr, bool *foundThis) + int frameNr) : engine(engine) , collector(collector) , frameNr(frameNr) - , foundThis(foundThis) + , thisRef(QV4DataCollector::s_invalidRef) {} void ThisCollectJob::run() -{ - *foundThis = myRun(); -} - -bool ThisCollectJob::myRun() { QV4::Scope scope(engine); QV4::ScopedContext ctxt(scope, QV4DataCollector::findContext(engine, frameNr)); @@ -451,23 +510,33 @@ bool ThisCollectJob::myRun() } if (!ctxt) - return false; + return; QV4::ScopedValue o(scope, ctxt->asCallContext()->d()->activation); - collector->collect(o); - return true; + thisRef = collector->collect(o); +} + +QV4DataCollector::Ref ThisCollectJob::foundRef() const +{ + return thisRef; } ExceptionCollectJob::ExceptionCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector) : engine(engine) , collector(collector) + , exception(QV4DataCollector::s_invalidRef) {} void ExceptionCollectJob::run() { QV4::Scope scope(engine); QV4::ScopedValue v(scope, *engine->exceptionValue); - collector->collect(v); + exception = collector->collect(v); +} + +QV4DataCollector::Ref ExceptionCollectJob::exceptionValue() const +{ + return exception; } QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h index bfa3bd2fe1..d1ff98f9b0 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h @@ -38,6 +38,9 @@ #include #include +#include +#include + QT_BEGIN_NAMESPACE class QV4DataCollector @@ -45,6 +48,7 @@ class QV4DataCollector public: typedef uint Ref; typedef QVector Refs; + static const Ref s_invalidRef; static QV4::CallContext *findContext(QV4::ExecutionEngine *engine, int frame); static QV4::Heap::CallContext *findScope(QV4::ExecutionContext *ctxt, int scope); @@ -52,22 +56,21 @@ public: QV4::ExecutionEngine *engine, int frame); QV4DataCollector(QV4::ExecutionEngine *engine); - ~QV4DataCollector(); - - void collect(const QV4::ScopedValue &value); - - QJsonObject lookupRef(Ref ref); + Ref collect(const QV4::ScopedValue &value); Ref addFunctionRef(const QString &functionName); Ref addScriptRef(const QString &scriptName); + bool isValidRef(Ref ref) const; + QJsonObject lookupRef(Ref ref); + void collectScope(QJsonObject *dict, QV4Debugger *debugger, int frameNr, int scopeNr); QV4::ExecutionEngine *engine() const { return m_engine; } + QJsonArray flushCollectedRefs(); + void clear(); private: - friend class RefHolder; - Ref addRef(QV4::Value value, bool deduplicate = true); QV4::ReturnedValue getValue(Ref ref); bool lookupSpecialRef(Ref ref, QJsonObject *dict); @@ -77,40 +80,43 @@ private: void collectArgumentsInContext(); QV4::ExecutionEngine *m_engine; - Refs *m_collectedRefs; - QV4::PersistentValue values; + Refs m_collectedRefs; + QV4::PersistentValue m_values; typedef QHash SpecialRefs; - SpecialRefs specialRefs; + SpecialRefs m_specialRefs; }; -class RefHolder { -public: - RefHolder(QV4DataCollector *collector, QV4DataCollector::Refs *target) : - m_collector(collector), m_previousRefs(collector->m_collectedRefs) - { - m_collector->m_collectedRefs = target; - } - - ~RefHolder() - { - std::swap(m_collector->m_collectedRefs, m_previousRefs); - } +class ValueLookupJob: public QV4Debugger::Job +{ + QV4DataCollector *collector; + const QJsonArray handles; + QJsonObject result; + QJsonArray collectedRefs; + QString exception; -private: - QV4DataCollector *m_collector; - QV4DataCollector::Refs *m_previousRefs; +public: + ValueLookupJob(const QJsonArray &handles, QV4DataCollector *collector) : + collector(collector), handles(handles) {} + void run(); + const QString &exceptionMessage() const; + const QJsonObject &returnValue() const; + const QJsonArray &refs() const; }; class ExpressionEvalJob: public QV4Debugger::JavaScriptJob { QV4DataCollector *collector; QString exception; + QJsonObject result; + QJsonArray collectedRefs; public: ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr, const QString &expression, QV4DataCollector *collector); - virtual void handleResult(QV4::ScopedValue &result); + virtual void handleResult(QV4::ScopedValue &value); const QString &exceptionMessage() const; + const QJsonObject &returnValue() const; + const QJsonArray &refs() const; }; class GatherSourcesJob: public QV4Debugger::Job @@ -157,23 +163,24 @@ class ThisCollectJob: public QV4Debugger::Job QV4::ExecutionEngine *engine; QV4DataCollector *collector; int frameNr; - bool *foundThis; + QV4DataCollector::Ref thisRef; public: - ThisCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector, int frameNr, - bool *foundThis); + ThisCollectJob(QV4::ExecutionEngine *engine, QV4DataCollector *collector, int frameNr); void run(); - bool myRun(); + QV4DataCollector::Ref foundRef() const; }; class ExceptionCollectJob: public QV4Debugger::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/qv4debugger.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp index 014de1f4cb..a7ef1ede6b 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp @@ -32,6 +32,7 @@ ****************************************************************************/ #include "qv4debugger.h" +#include "qv4datacollector.h" #include #include @@ -127,6 +128,7 @@ QV4Debugger::QV4Debugger(QV4::ExecutionEngine *engine) , m_returnedValue(engine, QV4::Primitive::undefinedValue()) , m_gatherSources(0) , m_runningJob(0) + , m_collector(new QV4DataCollector(engine)) { static int debuggerId = qRegisterMetaType(); static int pauseReasonId = qRegisterMetaType(); @@ -134,11 +136,21 @@ QV4Debugger::QV4Debugger(QV4::ExecutionEngine *engine) Q_UNUSED(pauseReasonId); } +QV4Debugger::~QV4Debugger() +{ + delete m_collector; +} + QV4::ExecutionEngine *QV4Debugger::engine() const { return m_engine; } +QV4DataCollector *QV4Debugger::collector() const +{ + return m_collector; +} + void QV4Debugger::pause() { QMutexLocker locker(&m_lock); @@ -300,6 +312,14 @@ QV4::Function *QV4Debugger::getFunction() const return context->d()->engine->globalCode; } +void QV4Debugger::runJobUnpaused() +{ + QMutexLocker locker(&m_lock); + if (m_runningJob) + m_runningJob->run(); + m_jobIsRunning.wakeAll(); +} + void QV4Debugger::pauseAndWait(PauseReason reason) { if (m_runningJob) @@ -352,7 +372,10 @@ void QV4Debugger::runInEngine_havingLock(QV4Debugger::Job *job) Q_ASSERT(m_runningJob == 0); m_runningJob = job; - m_runningCondition.wakeAll(); + if (state() == Paused) + m_runningCondition.wakeAll(); + else + QMetaObject::invokeMethod(this, "runJobUnpaused", Qt::QueuedConnection); m_jobIsRunning.wait(&m_lock); m_runningJob = 0; } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h index 8662259264..abb43f82f3 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h @@ -55,6 +55,7 @@ QT_BEGIN_NAMESPACE +class QV4DataCollector; class QV4Debugger : public QV4::Debugging::Debugger { Q_OBJECT @@ -110,8 +111,10 @@ public: }; QV4Debugger(QV4::ExecutionEngine *engine); + ~QV4Debugger(); QV4::ExecutionEngine *engine() const; + QV4DataCollector *collector() const; void pause(); void resume(Speed speed); @@ -153,6 +156,9 @@ public: signals: void debuggerPaused(QV4Debugger *self, QV4Debugger::PauseReason reason); +private slots: + void runJobUnpaused(); + private: // requires lock to be held void pauseAndWait(PauseReason reason); @@ -174,6 +180,7 @@ private: Job *m_gatherSources; Job *m_runningJob; + QV4DataCollector *m_collector; QWaitCondition m_jobIsRunning; }; diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp index 8bd9547032..c563a97fe2 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp @@ -63,7 +63,7 @@ void QV4DebuggerAgent::debuggerPaused(QV4Debugger *debugger, QV4Debugger::PauseR { Q_UNUSED(reason); - m_debugService->clearHandles(debugger->engine()); + debugger->collector()->clear(); QJsonObject event, body, script; event.insert(QStringLiteral("type"), QStringLiteral("event")); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp index dea1fae779..6524a1ff28 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) @@ -169,6 +169,7 @@ public: QJsonObject body; body.insert(QStringLiteral("V8Version"), QLatin1String("this is not V8, this is V4 in Qt " QT_VERSION_STR)); + body.insert(QStringLiteral("UnpausedEvaluate"), true); addBody(body); } }; @@ -296,7 +297,7 @@ public: body.insert(QStringLiteral("frames"), frameArray); } addBody(body); - addRefs(); + addRefs(debugger->collector()->flushCollectedRefs()); } }; @@ -333,7 +334,7 @@ public: addSuccess(true); addRunning(); addBody(frame); - addRefs(); + addRefs(debugger->collector()->flushCollectedRefs()); } }; @@ -374,7 +375,7 @@ public: addSuccess(true); addRunning(); addBody(scope); - addRefs(); + addRefs(debugger->collector()->flushCollectedRefs()); } }; @@ -389,17 +390,32 @@ 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()); + QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger(); + if (!debugger) { + const QList &debuggers = debugService->debuggerAgent.debuggers(); + if (debuggers.count() > 1) { + createErrorResponse(QStringLiteral("Cannot lookup values if multiple debuggers are running and none is paused")); + return; + } else if (debuggers.count() == 0) { + createErrorResponse(QStringLiteral("No debuggers available to lookup values")); + return; + } + debugger = debuggers.first(); + } - // response: - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - addBody(body); - addRefs(); + ValueLookupJob job(handles, debugger->collector()); + debugger->runInEngine(&job); + if (!job.exceptionMessage().isEmpty()) { + createErrorResponse(job.exceptionMessage()); + } else { + // response: + addCommand(); + addRequestSequence(); + addSuccess(true); + addRunning(); + addBody(job.returnValue()); + addRefs(job.refs()); + } } }; @@ -587,27 +603,34 @@ public: virtual void handleRequest() { QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger(); - if (debugger) { - QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); - QString expression = arguments.value(QStringLiteral("expression")).toString(); - 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()) { - createErrorResponse(job.exceptionMessage()); - } else { - addCommand(); - addRequestSequence(); - addSuccess(true); - addRunning(); - addBody(collector->lookupRef(debugService->refs()->last())); - addRefs(); + + if (!debugger) { + const QList &debuggers = debugService->debuggerAgent.debuggers(); + if (debuggers.count() > 1) { + createErrorResponse(QStringLiteral("Cannot evaluate expressions if multiple debuggers are running and none is paused")); + return; + } else if (debuggers.count() == 0) { + createErrorResponse(QStringLiteral("No debuggers available to evaluate expressions")); + return; } + debugger = debuggers.first(); + } + + QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject(); + QString expression = arguments.value(QStringLiteral("expression")).toString(); + const int frame = arguments.value(QStringLiteral("frame")).toInt(0); + + ExpressionEvalJob job(debugger->engine(), frame, expression, debugger->collector()); + debugger->runInEngine(&job); + if (job.hasExeption()) { + createErrorResponse(job.exceptionMessage()); } else { - createErrorResponse(QStringLiteral("Debugger has to be paused for evaluate to work.")); + addCommand(); + addRequestSequence(); + addSuccess(true); + addRunning(); + addBody(job.returnValue()); + addRefs(job.refs()); } } }; @@ -805,11 +828,6 @@ void QV4DebugServiceImpl::send(QJsonObject v8Payload) emit messageToClient(name(), packMessage("v8message", responseData)); } -void QV4DebugServiceImpl::clearHandles(QV4::ExecutionEngine *engine) -{ - theCollector.reset(new QV4DataCollector(engine)); -} - QJsonObject QV4DebugServiceImpl::buildFrame(const QV4::StackFrame &stackFrame, int frameNr, QV4Debugger *debugger) { @@ -818,38 +836,34 @@ QJsonObject QV4DebugServiceImpl::buildFrame(const QV4::StackFrame &stackFrame, i QJsonObject frame; frame[QLatin1String("index")] = frameNr; frame[QLatin1String("debuggerFrame")] = false; - ref = theCollector->addFunctionRef(stackFrame.function); - collectedRefs.append(ref); + ref = debugger->collector()->addFunctionRef(stackFrame.function); frame[QLatin1String("func")] = toRef(ref); - ref = theCollector->addScriptRef(stackFrame.source); - collectedRefs.append(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; - if (debugger->state() == QV4Debugger::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()); + 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; - // 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); - } + QJsonObject scope; + scope[QLatin1String("index")] = i; + scope[QLatin1String("type")] = type; + scopes.push_back(scope); } frame[QLatin1String("scopes")] = scopes; @@ -883,8 +897,7 @@ QJsonObject QV4DebugServiceImpl::buildScope(int frameNr, int scopeNr, QV4Debugge QJsonObject scope; QJsonObject object; - RefHolder holder(theCollector.data(), &collectedRefs); - theCollector->collectScope(&object, debugger, frameNr, scopeNr); + debugger->collector()->collectScope(&object, debugger, frameNr, scopeNr); if (debugger->state() == QV4Debugger::Paused) { QVector scopeTypes = @@ -900,27 +913,6 @@ QJsonObject QV4DebugServiceImpl::buildScope(int frameNr, int scopeNr, QV4Debugge 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; @@ -928,16 +920,6 @@ QJsonValue QV4DebugServiceImpl::toRef(QV4DataCollector::Ref 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 678ebe43b8..2d9932b838 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h @@ -78,19 +78,13 @@ public: void send(QJsonObject v8Payload); QJsonObject buildScope(int frameNr, int scopeNr, QV4Debugger *debugger); - QJsonArray buildRefs(); - QJsonValue lookup(QV4DataCollector::Ref refId); QJsonValue toRef(QV4DataCollector::Ref ref); QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr, QV4Debugger *debugger); int selectedFrame() const; void selectFrame(int frameNr); - void clearHandles(QV4::ExecutionEngine *engine); - - QV4DataCollector *collector() const; QV4DebuggerAgent debuggerAgent; - QV4DataCollector::Refs *refs(); protected: void messageReceived(const QByteArray &) Q_DECL_OVERRIDE; @@ -108,9 +102,7 @@ private: QStringList breakOnSignals; static int sequence; - QV4DataCollector::Refs collectedRefs; - QScopedPointer theCollector; int theSelectedFrame; void addHandler(V8CommandHandler* handler); -- cgit v1.2.3