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 <simon.hausmann@theqtcompany.com>
This commit is contained in:
parent
706238e037
commit
ebb08ee84e
|
@ -104,6 +104,28 @@ 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_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<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() == 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<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()
|
||||
|
|
|
@ -60,6 +60,7 @@ 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);
|
||||
|
||||
|
@ -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();
|
||||
|
|
|
@ -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<QV4::StackFrame> 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<QV4::StackFrame> 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<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) :
|
||||
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<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)
|
||||
: 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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -285,26 +285,16 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
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(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<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(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<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;
|
||||
}
|
||||
|
@ -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<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, QV4Debugger *debugger)
|
||||
{
|
||||
QJsonObject scope;
|
||||
|
||||
QJsonObject object;
|
||||
debugger->collector()->collectScope(&object, debugger, frameNr, scopeNr);
|
||||
|
||||
if (debugger->state() == QV4Debugger::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::toRef(QV4DataCollector::Ref ref)
|
||||
{
|
||||
QJsonObject dict;
|
||||
dict.insert(QStringLiteral("ref"), qint64(ref));
|
||||
return dict;
|
||||
}
|
||||
|
||||
void QV4DebugServiceImpl::selectFrame(int frameNr)
|
||||
{
|
||||
theSelectedFrame = frameNr;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<QV4Debugger::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;
|
||||
|
||||
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue