aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@theqtcompany.com>2015-11-30 12:04:59 +0100
committerUlf Hermann <ulf.hermann@theqtcompany.com>2016-01-07 16:25:12 +0000
commiteae5a20b099450985442c70186fd7a7be442f133 (patch)
treee7be003e4ed86ef69ad334eebfd8689cdb1e25b3 /src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
parent881bb537c92684d25fae4fec9ac2dd61e1f9723c (diff)
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 <simon.hausmann@theqtcompany.com>
Diffstat (limited to 'src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp')
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp151
1 files changed, 110 insertions, 41 deletions
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<QV4DataCollector::Ref>::max();
+
QV4::CallContext *QV4DataCollector::findContext(QV4::ExecutionEngine *engine, int frame)
{
QV4::ExecutionContext *ctx = engine->currentContext;
@@ -90,21 +93,16 @@ QVector<QV4::Heap::ExecutionContext::ContextType> 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,20 +491,15 @@ 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));
while (ctxt) {
@@ -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