aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp312
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h8
-rw-r--r--src/qml/jsruntime/qv4debugging.cpp353
-rw-r--r--src/qml/jsruntime/qv4debugging_p.h99
-rw-r--r--tests/auto/qml/qv4debugger/tst_qv4debugger.cpp293
5 files changed, 536 insertions, 529 deletions
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
index 0aaa6e7e92..62436f18c9 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
@@ -42,6 +42,7 @@
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
+#include <QtCore/QJsonValue>
const char *const V4_CONNECT = "connect";
const char *const V4_DISCONNECT = "disconnect";
@@ -61,248 +62,6 @@ QT_BEGIN_NAMESPACE
class V8CommandHandler;
class UnknownV8CommandHandler;
-class VariableCollector: public QV4::Debugging::Debugger::Collector
-{
-public:
- VariableCollector(QV4::ExecutionEngine *engine)
- : Collector(engine)
- , destination(0)
- {}
-
- virtual ~VariableCollector() {}
-
- void collectScope(QJsonArray *dest, QV4::Debugging::Debugger *debugger, int frameNr, int scopeNr)
- {
- qSwap(destination, dest);
- bool oldIsProp = isProperty();
- setIsProperty(true);
- debugger->collectArgumentsInContext(this, frameNr, scopeNr);
- debugger->collectLocalsInContext(this, frameNr, scopeNr);
- setIsProperty(oldIsProp);
- qSwap(destination, dest);
- }
-
- void setDestination(QJsonArray *dest)
- { destination = dest; }
-
- QJsonArray retrieveRefsToInclude()
- {
- QJsonArray result;
- qSwap(refsToInclude, result);
- return result;
- }
-
- QJsonValue lookup(int handle, bool addRefs = true)
- {
- if (handle < 0)
- handle = -handle;
-
- if (addRefs)
- foreach (int ref, refsByHandle[handle])
- refsToInclude.append(lookup(ref, false));
- return refs[handle];
- }
-
- QJsonObject makeRef(int refId)
- {
- QJsonObject ref;
- ref[QLatin1String("ref")] = refId;
- return ref;
- }
-
- QJsonObject addFunctionRef(const QString &name)
- {
- const int refId = newRefId();
-
- QJsonObject func;
- func[QLatin1String("handle")] = refId;
- func[QLatin1String("type")] = QStringLiteral("function");
- func[QLatin1String("className")] = QStringLiteral("Function");
- func[QLatin1String("name")] = name;
- insertRef(func, refId);
-
- return makeRef(refId);
- }
-
- QJsonObject addScriptRef(const QString &name)
- {
- const int refId = newRefId();
-
- QJsonObject func;
- func[QLatin1String("handle")] = refId;
- func[QLatin1String("type")] = QStringLiteral("script");
- func[QLatin1String("name")] = name;
- insertRef(func, refId);
-
- return makeRef(refId);
- }
-
- QJsonObject addObjectRef(QJsonObject obj, bool anonymous)
- {
- int ref = newRefId();
-
- if (anonymous)
- ref = -ref;
- obj[QLatin1String("handle")] = ref;
- obj[QLatin1String("type")] = QStringLiteral("object");
- insertRef(obj, ref);
- QSet<int> used;
- qSwap(usedRefs, used);
- refsByHandle.insert(ref, used);
-
- return makeRef(ref);
- }
-
-protected:
- virtual void addUndefined(const QString &name)
- {
- QJsonObject o;
- addHandle(name, o, QStringLiteral("undefined"));
- }
-
- virtual void addNull(const QString &name)
- {
- QJsonObject o;
- addHandle(name, o, QStringLiteral("null"));
- }
-
- virtual void addBoolean(const QString &name, bool value)
- {
- QJsonObject o;
- o[QLatin1String("value")] = value;
- addHandle(name, o, QStringLiteral("boolean"));
- }
-
- virtual void addString(const QString &name, const QString &value)
- {
- QJsonObject o;
- o[QLatin1String("value")] = value;
- addHandle(name, o, QStringLiteral("string"));
- }
-
- virtual void addObject(const QString &name, const QV4::Value &value)
- {
- QV4::Scope scope(engine());
- QV4::ScopedObject obj(scope, value.as<QV4::Object>());
-
- int ref = cachedObjectRef(obj);
- if (ref != -1) {
- addNameRefPair(name, ref);
- } else {
- int ref = newRefId();
- cacheObjectRef(obj, ref);
-
- QJsonArray properties, *prev = &properties;
- QSet<int> used;
- qSwap(usedRefs, used);
- qSwap(destination, prev);
- collect(obj);
- qSwap(destination, prev);
- qSwap(usedRefs, used);
-
- QJsonObject o;
- o[QLatin1String("properties")] = properties;
- addHandle(name, o, QStringLiteral("object"), ref);
- refsByHandle.insert(ref, used);
- }
- }
-
- virtual void addInteger(const QString &name, int value)
- {
- QJsonObject o;
- o[QLatin1String("value")] = value;
- addHandle(name, o, QStringLiteral("number"));
- }
-
- virtual void addDouble(const QString &name, double value)
- {
- QJsonObject o;
- o[QLatin1String("value")] = value;
- addHandle(name, o, QStringLiteral("number"));
- }
-
-private:
- int addHandle(const QString &name, QJsonObject object, const QString &type, int suppliedRef = -1)
- {
- Q_ASSERT(destination);
-
- object[QLatin1String("type")] = type;
-
- QJsonDocument tmp;
- tmp.setObject(object);
- QByteArray key = tmp.toJson(QJsonDocument::Compact);
-
- int ref;
- if (suppliedRef == -1) {
- ref = refCache.value(key, -1);
- if (ref == -1) {
- ref = newRefId();
- object[QLatin1String("handle")] = ref;
- insertRef(object, ref);
- refCache.insert(key, ref);
- }
- } else {
- ref = suppliedRef;
- object[QLatin1String("handle")] = ref;
- insertRef(object, ref);
- refCache.insert(key, ref);
- }
-
- addNameRefPair(name, ref);
- return ref;
- }
-
- void addNameRefPair(const QString &name, int ref)
- {
- QJsonObject nameValuePair;
- nameValuePair[QLatin1String("name")] = name;
- if (isProperty()) {
- nameValuePair[QLatin1String("ref")] = ref;
- } else {
- QJsonObject refObj;
- refObj[QLatin1String("ref")] = ref;
- nameValuePair[QLatin1String("value")] = refObj;
- }
- destination->append(nameValuePair);
- usedRefs.insert(ref);
- }
-
- int newRefId()
- {
- int ref = refs.count();
- refs.insert(ref, QJsonValue());
- return ref;
- }
-
- void insertRef(const QJsonValue &value, int refId)
- {
- if (refId < 0)
- refId = -refId;
-
- refs.insert(refId, value);
- refsToInclude.append(value);
- }
-
- void cacheObjectRef(QV4::Object *obj, int ref)
- {
- objectRefs.insert(obj, ref);
- }
-
- int cachedObjectRef(QV4::Object *obj) const
- {
- return objectRefs.value(obj, -1);
- }
-
-private:
- QJsonArray refsToInclude;
- QHash<int, QJsonValue> refs;
- QHash<QByteArray, int> refCache;
- QJsonArray *destination;
- QSet<int> usedRefs;
- QHash<int, QSet<int> > refsByHandle;
- QHash<QV4::Object *, int> objectRefs;
-};
-
int QV4DebugServiceImpl::debuggerIndex = 0;
int QV4DebugServiceImpl::sequence = 0;
@@ -796,20 +555,19 @@ public:
QV4::Debugging::Debugger *debugger = debugService->debuggerAgent.firstDebugger();
Q_ASSERT(debugger->state() == QV4::Debugging::Debugger::Paused);
- VariableCollector *collector = debugService->collector();
- QJsonArray dest;
- collector->setDestination(&dest);
+ QV4::Debugging::DataCollector *collector = debugService->collector();
+ QV4::Debugging::DataCollector::Refs refs;
+ QV4::Debugging::RefHolder holder(collector, &refs);
debugger->evaluateExpression(frame, expression, collector);
- const int ref = dest.at(0).toObject().value(QStringLiteral("value")).toObject()
- .value(QStringLiteral("ref")).toInt();
+ Q_ASSERT(refs.size() == 1);
// response:
addCommand();
addRequestSequence();
addSuccess(true);
addRunning();
- addBody(collector->lookup(ref).toObject());
+ addBody(collector->lookupRef(refs.first()));
addRefs();
}
};
@@ -1088,27 +846,31 @@ void QV4DebugServiceImpl::send(QJsonObject v8Payload)
void QV4DebugServiceImpl::clearHandles(QV4::ExecutionEngine *engine)
{
- theCollector.reset(new VariableCollector(engine));
+ theCollector.reset(new QV4::Debugging::DataCollector(engine));
}
QJsonObject QV4DebugServiceImpl::buildFrame(const QV4::StackFrame &stackFrame, int frameNr,
QV4::Debugging::Debugger *debugger)
{
+ QV4::Debugging::DataCollector::Ref ref;
+
QJsonObject frame;
frame[QLatin1String("index")] = frameNr;
frame[QLatin1String("debuggerFrame")] = false;
- frame[QLatin1String("func")] = theCollector->addFunctionRef(stackFrame.function);
- frame[QLatin1String("script")] = theCollector->addScriptRef(stackFrame.source);
+ 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 properties;
- theCollector->setDestination(&properties);
- if (debugger->collectThisInContext(theCollector.data(), frameNr)) {
- QJsonObject obj;
- obj[QLatin1String("properties")] = properties;
- frame[QLatin1String("receiver")] = theCollector->addObjectRef(obj, false);
+ {
+ QV4::Debugging::RefHolder holder(theCollector.data(), &collectedRefs);
+ if (debugger->collectThisInContext(theCollector.data(), frameNr))
+ frame[QLatin1String("receiver")] = toRef(collectedRefs.last());
}
QJsonArray scopes;
@@ -1156,32 +918,48 @@ QJsonObject QV4DebugServiceImpl::buildScope(int frameNr, int scopeNr,
{
QJsonObject scope;
- QJsonArray properties;
- theCollector->collectScope(&properties, debugger, frameNr, scopeNr);
-
- QJsonObject anonymous;
- anonymous[QLatin1String("properties")] = properties;
+ QJsonObject object;
+ QV4::Debugging::RefHolder holder(theCollector.data(), &collectedRefs);
+ theCollector->collectScope(&object, debugger, frameNr, scopeNr);
QVector<QV4::Heap::ExecutionContext::ContextType> scopeTypes = debugger->getScopeTypes(frameNr);
scope[QLatin1String("type")] = encodeScopeType(scopeTypes[scopeNr]);
scope[QLatin1String("index")] = scopeNr;
scope[QLatin1String("frameIndex")] = frameNr;
- scope[QLatin1String("object")] = theCollector->addObjectRef(anonymous, true);
+ scope[QLatin1String("object")] = object;
return scope;
}
-QJsonValue QV4DebugServiceImpl::lookup(int refId) const
+QJsonValue QV4DebugServiceImpl::lookup(QV4::Debugging::DataCollector::Ref refId)
{
- return theCollector->lookup(refId);
+ QV4::Debugging::RefHolder holder(theCollector.data(), &collectedRefs);
+ return theCollector->lookupRef(refId);
}
QJsonArray QV4DebugServiceImpl::buildRefs()
{
- return theCollector->retrieveRefsToInclude();
+ QJsonArray refs;
+ std::sort(collectedRefs.begin(), collectedRefs.end());
+ for (int i = 0, ei = collectedRefs.size(); i != ei; ++i) {
+ QV4::Debugging::DataCollector::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(QV4::Debugging::DataCollector::Ref ref)
+{
+ QJsonObject dict;
+ dict.insert(QStringLiteral("ref"), qint64(ref));
+ return dict;
}
-VariableCollector *QV4DebugServiceImpl::collector() const
+QV4::Debugging::DataCollector *QV4DebugServiceImpl::collector() const
{
return theCollector.data();
}
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
index 4829ae414f..23bffa368a 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
@@ -94,7 +94,8 @@ public:
QJsonObject buildScope(int frameNr, int scopeNr, QV4::Debugging::Debugger *debugger);
QJsonArray buildRefs();
- QJsonValue lookup(int refId) const;
+ QJsonValue lookup(QV4::Debugging::DataCollector::Ref refId);
+ QJsonValue toRef(QV4::Debugging::DataCollector::Ref ref);
QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr,
QV4::Debugging::Debugger *debugger);
@@ -103,7 +104,7 @@ public:
void clearHandles(QV4::ExecutionEngine *engine);
- VariableCollector *collector() const;
+ QV4::Debugging::DataCollector *collector() const;
QV4DebuggerAgent debuggerAgent;
protected:
@@ -125,8 +126,9 @@ private:
static int debuggerIndex;
static int sequence;
const int version;
+ QV4::Debugging::DataCollector::Refs collectedRefs;
- QScopedPointer<VariableCollector> theCollector;
+ QScopedPointer<QV4::Debugging::DataCollector> theCollector;
int theSelectedFrame;
void addHandler(V8CommandHandler* handler);
diff --git a/src/qml/jsruntime/qv4debugging.cpp b/src/qml/jsruntime/qv4debugging.cpp
index ed8baa3f69..c48bb03844 100644
--- a/src/qml/jsruntime/qv4debugging.cpp
+++ b/src/qml/jsruntime/qv4debugging.cpp
@@ -38,13 +38,17 @@
#include "qv4instr_moth_p.h"
#include "qv4runtime_p.h"
#include "qv4script_p.h"
-#include "qv4objectiterator_p.h"
#include "qv4identifier_p.h"
#include "qv4string_p.h"
-#include <iostream>
+#include "qv4objectiterator_p.h"
+#include <iostream>
#include <algorithm>
+#include <QtCore/QJsonArray>
+#include <QtCore/QJsonDocument>
+#include <QtCore/QJsonValue>
+
QT_BEGIN_NAMESPACE
using namespace QV4;
@@ -120,10 +124,11 @@ public:
class ExpressionEvalJob: public JavaScriptJob
{
- Debugger::Collector *collector;
+ QV4::Debugging::DataCollector *collector;
public:
- ExpressionEvalJob(ExecutionEngine *engine, int frameNr, const QString &expression, Debugger::Collector *collector)
+ ExpressionEvalJob(ExecutionEngine *engine, int frameNr, const QString &expression,
+ QV4::Debugging::DataCollector *collector)
: JavaScriptJob(engine, frameNr, expression)
, collector(collector)
{
@@ -131,7 +136,7 @@ public:
virtual void handleResult(QV4::ScopedValue &result)
{
- collector->collect(QStringLiteral("body"), result);
+ collector->collect(result);
}
};
@@ -167,6 +172,218 @@ public:
};
}
+
+DataCollector::DataCollector(QV4::ExecutionEngine *engine)
+ : m_engine(engine), m_collectedRefs(Q_NULLPTR)
+{
+ values.set(engine, engine->newArrayObject());
+}
+
+DataCollector::~DataCollector()
+{
+}
+
+void DataCollector::collect(const ScopedValue &value)
+{
+ if (m_collectedRefs)
+ m_collectedRefs->append(addRef(value));
+}
+
+QJsonObject DataCollector::lookupRef(Ref ref)
+{
+ QJsonObject dict;
+ if (lookupSpecialRef(ref, &dict))
+ return dict;
+
+ dict.insert(QStringLiteral("handle"), qint64(ref));
+
+ QV4::Scope scope(engine());
+ QV4::ScopedValue value(scope, getValue(ref));
+ switch (value->type()) {
+ case QV4::Value::Empty_Type:
+ Q_ASSERT(!"empty Value encountered");
+ break;
+ case QV4::Value::Undefined_Type:
+ dict.insert(QStringLiteral("type"), QStringLiteral("undefined"));
+ break;
+ case QV4::Value::Null_Type:
+ dict.insert(QStringLiteral("type"), QStringLiteral("null"));
+ break;
+ case QV4::Value::Boolean_Type:
+ dict.insert(QStringLiteral("type"), QStringLiteral("boolean"));
+ dict.insert(QStringLiteral("value"), value->booleanValue() ? QStringLiteral("true")
+ : QStringLiteral("false"));
+ break;
+ case QV4::Value::Managed_Type:
+ if (QV4::String *s = value->as<QV4::String>()) {
+ dict.insert(QStringLiteral("type"), QStringLiteral("string"));
+ dict.insert(QStringLiteral("value"), s->toQString());
+ } else if (QV4::Object *o = value->as<QV4::Object>()) {
+ dict.insert(QStringLiteral("type"), QStringLiteral("object"));
+ dict.insert(QStringLiteral("properties"), collectProperties(o));
+ } else {
+ Q_UNREACHABLE();
+ }
+ break;
+ case QV4::Value::Integer_Type:
+ dict.insert(QStringLiteral("type"), QStringLiteral("number"));
+ dict.insert(QStringLiteral("value"), value->integerValue());
+ break;
+ default: // double
+ dict.insert(QStringLiteral("type"), QStringLiteral("number"));
+ dict.insert(QStringLiteral("value"), value->doubleValue());
+ break;
+ }
+
+ return dict;
+}
+
+DataCollector::Ref DataCollector::addFunctionRef(const QString &functionName)
+{
+ Ref ref = addRef(QV4::Primitive::emptyValue(), false);
+
+ QJsonObject dict;
+ dict.insert(QStringLiteral("handle"), qint64(ref));
+ dict.insert(QStringLiteral("type"), QStringLiteral("function"));
+ dict.insert(QStringLiteral("className"), QStringLiteral("Function"));
+ dict.insert(QStringLiteral("name"), functionName);
+ specialRefs.insert(ref, dict);
+
+ return ref;
+}
+
+DataCollector::Ref DataCollector::addScriptRef(const QString &scriptName)
+{
+ Ref ref = addRef(QV4::Primitive::emptyValue(), false);
+
+ QJsonObject dict;
+ dict.insert(QStringLiteral("handle"), qint64(ref));
+ dict.insert(QStringLiteral("type"), QStringLiteral("script"));
+ dict.insert(QStringLiteral("name"), scriptName);
+ specialRefs.insert(ref, dict);
+
+ return ref;
+}
+
+void DataCollector::collectScope(QJsonObject *dict, Debugger *debugger, int frameNr, int scopeNr)
+{
+ QStringList names;
+
+ Refs refs;
+ {
+ RefHolder holder(this, &refs);
+ debugger->collectArgumentsInContext(this, &names, frameNr, scopeNr);
+ debugger->collectLocalsInContext(this, &names, frameNr, scopeNr);
+ }
+
+ 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)
+ scopeObject->put(engine(), names.at(i),
+ QV4::Value::fromReturnedValue(getValue(refs.at(i))));
+
+ Ref scopeObjectRef = addRef(scopeObject);
+ dict->insert(QStringLiteral("ref"), qint64(scopeObjectRef));
+ if (m_collectedRefs)
+ m_collectedRefs->append(scopeObjectRef);
+}
+
+DataCollector::Ref DataCollector::addRef(Value value, bool deduplicate)
+{
+ class ExceptionStateSaver
+ {
+ quint32 *hasExceptionLoc;
+ quint32 hadException;
+
+ public:
+ ExceptionStateSaver(QV4::ExecutionEngine *engine)
+ : hasExceptionLoc(&engine->hasException)
+ , hadException(false)
+ { std::swap(*hasExceptionLoc, hadException); }
+
+ ~ExceptionStateSaver()
+ { std::swap(*hasExceptionLoc, hadException); }
+ };
+
+ // if we wouldn't do this, the putIndexed won't work.
+ ExceptionStateSaver resetExceptionState(engine());
+ QV4::Scope scope(engine());
+ QV4::ScopedObject array(scope, values.value());
+ if (deduplicate) {
+ for (Ref i = 0; i < array->getLength(); ++i) {
+ if (array->getIndexed(i) == value.rawValue())
+ return i;
+ }
+ }
+ Ref ref = array->getLength();
+ array->putIndexed(ref, value);
+ Q_ASSERT(array->getLength() - 1 == ref);
+ return ref;
+}
+
+ReturnedValue DataCollector::getValue(Ref ref)
+{
+ QV4::Scope scope(engine());
+ QV4::ScopedObject array(scope, values.value());
+ Q_ASSERT(ref < array->getLength());
+ return array->getIndexed(ref, Q_NULLPTR);
+}
+
+bool DataCollector::lookupSpecialRef(Ref ref, QJsonObject *dict)
+{
+ SpecialRefs::const_iterator it = specialRefs.find(ref);
+ if (it == specialRefs.end())
+ return false;
+
+ *dict = it.value();
+ return true;
+}
+
+QJsonArray DataCollector::collectProperties(Object *object)
+{
+ QJsonArray res;
+
+ QV4::Scope scope(engine());
+ QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
+ QV4::ScopedValue name(scope);
+ QV4::ScopedValue value(scope);
+ while (true) {
+ QV4::Value v;
+ name = it.nextPropertyNameAsString(&v);
+ if (name->isNull())
+ break;
+ QString key = name->toQStringNoThrow();
+ value = v;
+ res.append(collectAsJson(key, value));
+ }
+
+ return res;
+}
+
+QJsonObject DataCollector::collectAsJson(const QString &name, const ScopedValue &value)
+{
+ QJsonObject dict;
+ if (!name.isNull())
+ dict.insert(QStringLiteral("name"), name);
+ Ref ref = addRef(value);
+ dict.insert(QStringLiteral("ref"), qint64(ref));
+ if (m_collectedRefs)
+ m_collectedRefs->append(ref);
+
+ // TODO: enable this when creator can handle it.
+ if (false) {
+ if (value->isManaged() && !value->isString()) {
+ QV4::Scope scope(engine());
+ QV4::ScopedObject obj(scope, value->as<QV4::Object>());
+ dict.insert(QStringLiteral("propertycount"), qint64(obj->getLength()));
+ }
+ }
+
+ return dict;
+}
+
Debugger::Debugger(QV4::ExecutionEngine *engine)
: m_engine(engine)
, m_agent(0)
@@ -308,7 +525,8 @@ static inline Heap::CallContext *findScope(Heap::ExecutionContext *ctxt, int sco
return (ctx && ctx->d()) ? ctx->asCallContext()->d() : 0;
}
-void Debugger::collectArgumentsInContext(Collector *collector, int frameNr, int scopeNr)
+void Debugger::collectArgumentsInContext(DataCollector *collector, QStringList *names, int frameNr,
+ int scopeNr)
{
if (state() != Paused)
return;
@@ -316,14 +534,17 @@ void Debugger::collectArgumentsInContext(Collector *collector, int frameNr, int
class ArgumentCollectJob: public Job
{
QV4::ExecutionEngine *engine;
- Collector *collector;
+ QV4::Debugging::DataCollector *collector;
+ QStringList *names;
int frameNr;
int scopeNr;
public:
- ArgumentCollectJob(QV4::ExecutionEngine *engine, Collector *collector, int frameNr, int scopeNr)
+ ArgumentCollectJob(QV4::ExecutionEngine *engine, DataCollector *collector,
+ QStringList *names, int frameNr, int scopeNr)
: engine(engine)
, collector(collector)
+ , names(names)
, frameNr(frameNr)
, scopeNr(scopeNr)
{}
@@ -346,33 +567,40 @@ void Debugger::collectArgumentsInContext(Collector *collector, int frameNr, int
QString qName;
if (Identifier *name = ctxt->formals()[nFormals - i - 1])
qName = name->string;
+ names->append(qName);
v = ctxt->argument(i);
- collector->collect(qName, v);
+ collector->collect(v);
}
}
};
- ArgumentCollectJob job(m_engine, collector, frameNr, scopeNr);
+ ArgumentCollectJob job(m_engine, collector, names, frameNr, scopeNr);
runInEngine(&job);
}
/// Same as \c retrieveArgumentsFromContext, but now for locals.
-void Debugger::collectLocalsInContext(Collector *collector, int frameNr, int scopeNr)
+void Debugger::collectLocalsInContext(DataCollector *collector, QStringList *names, int frameNr,
+ int scopeNr)
{
+ Q_ASSERT(collector);
+ Q_ASSERT(names);
+
if (state() != Paused)
return;
class LocalCollectJob: public Job
{
QV4::ExecutionEngine *engine;
- Collector *collector;
+ DataCollector *collector;
+ QStringList *names;
int frameNr;
int scopeNr;
public:
- LocalCollectJob(QV4::ExecutionEngine *engine, Collector *collector, int frameNr, int scopeNr)
+ LocalCollectJob(QV4::ExecutionEngine *engine, DataCollector *collector, QStringList *names, int frameNr, int scopeNr)
: engine(engine)
, collector(collector)
+ , names(names)
, frameNr(frameNr)
, scopeNr(scopeNr)
{}
@@ -392,17 +620,18 @@ void Debugger::collectLocalsInContext(Collector *collector, int frameNr, int sco
QString qName;
if (Identifier *name = ctxt->variables()[i])
qName = name->string;
+ names->append(qName);
v = ctxt->d()->locals[i];
- collector->collect(qName, v);
+ collector->collect(v);
}
}
};
- LocalCollectJob job(m_engine, collector, frameNr, scopeNr);
+ LocalCollectJob job(m_engine, collector, names, frameNr, scopeNr);
runInEngine(&job);
}
-bool Debugger::collectThisInContext(Debugger::Collector *collector, int frame)
+bool Debugger::collectThisInContext(DataCollector *collector, int frame)
{
if (state() != Paused)
return false;
@@ -410,12 +639,13 @@ bool Debugger::collectThisInContext(Debugger::Collector *collector, int frame)
class ThisCollectJob: public Job
{
QV4::ExecutionEngine *engine;
- Collector *collector;
+ DataCollector *collector;
int frameNr;
bool *foundThis;
public:
- ThisCollectJob(QV4::ExecutionEngine *engine, Collector *collector, int frameNr, bool *foundThis)
+ ThisCollectJob(QV4::ExecutionEngine *engine, DataCollector *collector, int frameNr,
+ bool *foundThis)
: engine(engine)
, collector(collector)
, frameNr(frameNr)
@@ -441,7 +671,7 @@ bool Debugger::collectThisInContext(Debugger::Collector *collector, int frame)
if (!ctxt)
return false;
- ScopedObject o(scope, ctxt->asCallContext()->d()->activation);
+ ScopedValue o(scope, ctxt->asCallContext()->d()->activation);
collector->collect(o);
return true;
}
@@ -453,18 +683,18 @@ bool Debugger::collectThisInContext(Debugger::Collector *collector, int frame)
return foundThis;
}
-void Debugger::collectThrownValue(Collector *collector)
+bool Debugger::collectThrownValue(DataCollector *collector)
{
if (state() != Paused || !m_engine->hasException)
- return;
+ return false;
- class ThisCollectJob: public Job
+ class ExceptionCollectJob: public Job
{
QV4::ExecutionEngine *engine;
- Collector *collector;
+ DataCollector *collector;
public:
- ThisCollectJob(QV4::ExecutionEngine *engine, Collector *collector)
+ ExceptionCollectJob(QV4::ExecutionEngine *engine, DataCollector *collector)
: engine(engine)
, collector(collector)
{}
@@ -473,22 +703,13 @@ void Debugger::collectThrownValue(Collector *collector)
{
Scope scope(engine);
ScopedValue v(scope, *engine->exceptionValue);
- collector->collect(QStringLiteral("exception"), v);
+ collector->collect(v);
}
};
- ThisCollectJob job(m_engine, collector);
+ ExceptionCollectJob job(m_engine, collector);
runInEngine(&job);
-}
-
-void Debugger::collectReturnedValue(Collector *collector) const
-{
- if (state() != Paused)
- return;
-
- Scope scope(m_engine);
- ScopedObject o(scope, m_returnedValue.valueRef());
- collector->collect(o);
+ return true;
}
QVector<Heap::ExecutionContext::ContextType> Debugger::getScopeTypes(int frame) const
@@ -511,7 +732,8 @@ QVector<Heap::ExecutionContext::ContextType> Debugger::getScopeTypes(int frame)
}
-void Debugger::evaluateExpression(int frameNr, const QString &expression, Debugger::Collector *resultsCollector)
+void Debugger::evaluateExpression(int frameNr, const QString &expression,
+ DataCollector *resultsCollector)
{
Q_ASSERT(state() == Paused);
@@ -776,63 +998,6 @@ DebuggerAgent::~DebuggerAgent()
Q_ASSERT(m_debuggers.isEmpty());
}
-Debugger::Collector::~Collector()
-{
-}
-
-void Debugger::Collector::collect(const QString &name, const ScopedValue &value)
-{
- switch (value->type()) {
- case Value::Empty_Type:
- Q_ASSERT(!"empty Value encountered");
- break;
- case Value::Undefined_Type:
- addUndefined(name);
- break;
- case Value::Null_Type:
- addNull(name);
- break;
- case Value::Boolean_Type:
- addBoolean(name, value->booleanValue());
- break;
- case Value::Managed_Type:
- if (const String *s = value->as<String>())
- addString(name, s->toQString());
- else
- addObject(name, value);
- break;
- case Value::Integer_Type:
- addInteger(name, value->int_32());
- break;
- default: // double
- addDouble(name, value->doubleValue());
- break;
- }
-}
-
-void Debugger::Collector::collect(Object *object)
-{
- bool property = true;
- qSwap(property, m_isProperty);
-
- Scope scope(m_engine);
- ObjectIterator it(scope, object, ObjectIterator::EnumerableOnly);
- ScopedValue name(scope);
- ScopedValue value(scope);
- while (true) {
- Value v;
- name = it.nextPropertyNameAsString(&v);
- if (name->isNull())
- break;
- QString key = name->toQStringNoThrow();
- value = v;
- collect(key, value);
- }
-
- qSwap(property, m_isProperty);
-}
-
-
Debugger::Job::~Job()
{
}
diff --git a/src/qml/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h
index e6a9750351..424459a7d9 100644
--- a/src/qml/jsruntime/qv4debugging_p.h
+++ b/src/qml/jsruntime/qv4debugging_p.h
@@ -44,6 +44,8 @@
#include <QMutex>
#include <QWaitCondition>
+#include <QtCore/QJsonObject>
+
QT_BEGIN_NAMESPACE
namespace QV4 {
@@ -79,6 +81,61 @@ inline bool operator==(const DebuggerBreakPoint &a, const DebuggerBreakPoint &b)
typedef QHash<DebuggerBreakPoint, QString> BreakPoints;
+class Q_QML_PRIVATE_EXPORT DataCollector
+{
+public:
+ typedef uint Ref;
+ typedef QVector<uint> Refs;
+
+ DataCollector(QV4::ExecutionEngine *engine);
+ ~DataCollector();
+
+ void collect(const QV4::ScopedValue &value);
+
+ QJsonObject lookupRef(Ref ref);
+
+ Ref addFunctionRef(const QString &functionName);
+ Ref addScriptRef(const QString &scriptName);
+
+ void collectScope(QJsonObject *dict, QV4::Debugging::Debugger *debugger, int frameNr,
+ int scopeNr);
+
+ QV4::ExecutionEngine *engine() const { return m_engine; }
+
+private:
+ friend class RefHolder;
+
+ Ref addRef(QV4::Value value, bool deduplicate = true);
+ QV4::ReturnedValue getValue(Ref ref);
+ bool lookupSpecialRef(Ref ref, QJsonObject *dict);
+
+ QJsonArray collectProperties(QV4::Object *object);
+ QJsonObject collectAsJson(const QString &name, const QV4::ScopedValue &value);
+
+ QV4::ExecutionEngine *m_engine;
+ Refs *m_collectedRefs;
+ QV4::PersistentValue values;
+ typedef QHash<Ref, QJsonObject> SpecialRefs;
+ SpecialRefs specialRefs;
+};
+
+class RefHolder {
+public:
+ RefHolder(DataCollector *collector, DataCollector::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:
+ DataCollector *m_collector;
+ DataCollector::Refs *m_previousRefs;
+};
class Q_QML_EXPORT Debugger
{
@@ -90,34 +147,6 @@ public:
virtual void run() = 0;
};
- class Q_QML_EXPORT Collector
- {
- public:
- Collector(ExecutionEngine *engine): m_engine(engine), m_isProperty(false) {}
- virtual ~Collector();
-
- void collect(const QString &name, const ScopedValue &value);
- void collect(Object *object);
-
- protected:
- virtual void addUndefined(const QString &name) = 0;
- virtual void addNull(const QString &name) = 0;
- virtual void addBoolean(const QString &name, bool value) = 0;
- virtual void addString(const QString &name, const QString &value) = 0;
- virtual void addObject(const QString &name, const Value &value) = 0;
- virtual void addInteger(const QString &name, int value) = 0;
- virtual void addDouble(const QString &name, double value) = 0;
-
- QV4::ExecutionEngine *engine() const { return m_engine; }
-
- bool isProperty() const { return m_isProperty; }
- void setIsProperty(bool onoff) { m_isProperty = onoff; }
-
- private:
- QV4::ExecutionEngine *m_engine;
- bool m_isProperty;
- };
-
enum State {
Running,
Paused
@@ -166,14 +195,16 @@ public:
}
QVector<StackFrame> stackTrace(int frameLimit = -1) const;
- void collectArgumentsInContext(Collector *collector, int frameNr = 0, int scopeNr = 0);
- void collectLocalsInContext(Collector *collector, int frameNr = 0, int scopeNr = 0);
- bool collectThisInContext(Collector *collector, int frame = 0);
- void collectThrownValue(Collector *collector);
- void collectReturnedValue(Collector *collector) const;
+ void collectArgumentsInContext(DataCollector *collector, QStringList *names, int frameNr = 0,
+ int scopeNr = 0);
+ void collectLocalsInContext(DataCollector *collector, QStringList *names, int frameNr = 0,
+ int scopeNr = 0);
+ bool collectThisInContext(DataCollector *collector, int frame = 0);
+ bool collectThrownValue(DataCollector *collector);
QVector<Heap::ExecutionContext::ContextType> getScopeTypes(int frame = 0) const;
- void evaluateExpression(int frameNr, const QString &expression, Collector *resultsCollector);
+ void evaluateExpression(int frameNr, const QString &expression,
+ DataCollector *resultsCollector);
public: // compile-time interface
void maybeBreakAtInstruction();
diff --git a/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp
index ee0f55813d..78aa113450 100644
--- a/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp
+++ b/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp
@@ -38,6 +38,7 @@
#include <private/qv4engine_p.h>
#include <private/qv4debugging_p.h>
#include <private/qv8engine_p.h>
+#include <private/qv4objectiterator_p.h>
using namespace QV4;
using namespace QV4::Debugging;
@@ -92,92 +93,83 @@ signals:
void evaluateFinished();
};
-
-namespace {
-class TestCollector: public QV4::Debugging::Debugger::Collector
+class TestAgent : public QV4::Debugging::DebuggerAgent
{
+ Q_OBJECT
public:
- TestCollector(QV4::ExecutionEngine *engine)
- : Collector(engine)
- , destination(0)
- {}
-
- virtual ~TestCollector() {}
-
- void setDestination(QVariantMap *dest)
- { destination = dest; }
-
-protected:
- virtual void addUndefined(const QString &name)
- {
- destination->insert(name, QStringLiteral("undefined")); // TODO: add a user-defined type for this
- }
-
- virtual void addNull(const QString &name)
- {
- destination->insert(name, QStringLiteral("null")); // TODO: add a user-defined type for this
- }
-
- virtual void addBoolean(const QString &name, bool value)
- {
- destination->insert(name, value);
- }
+ typedef QV4::Debugging::DataCollector::Refs Refs;
+ typedef QV4::Debugging::DataCollector::Ref Ref;
+ struct NamedRefs {
+ NamedRefs(DataCollector *collector = 0): collector(collector) {}
+
+ QStringList names;
+ Refs refs;
+ DataCollector *collector;
+
+ int size() const {
+ Q_ASSERT(names.size() == refs.size());
+ return names.size();
+ }
- virtual void addString(const QString &name, const QString &value)
- {
- destination->insert(name, value);
- }
+ bool contains(const QString &name) const {
+ return names.contains(name);
+ }
- virtual void addObject(const QString &name, const QV4::Value &value)
- {
- QV4::Scope scope(engine());
- QV4::ScopedObject obj(scope, value.as<Object>());
+#define DUMP_JSON(x) {\
+ QJsonDocument doc(x);\
+ qDebug() << #x << "=" << doc.toJson(QJsonDocument::Indented);\
+}
- QVariantMap props, *prev = &props;
- qSwap(destination, prev);
- collect(obj);
- qSwap(destination, prev);
+ QJsonObject rawValue(const QString &name) const {
+ Q_ASSERT(contains(name));
+ return collector->lookupRef(refs.at(names.indexOf(name)));
+ }
- destination->insert(name, props);
- }
+ QJsonValue value(const QString &name) const {
+ return rawValue(name).value(QStringLiteral("value"));
+ }
- virtual void addInteger(const QString &name, int value)
- {
- destination->insert(name, QVariant::fromValue<double>(static_cast<double>(value)));
- }
+ QString type(const QString &name) const {
+ return rawValue(name).value(QStringLiteral("type")).toString();
+ }
- virtual void addDouble(const QString &name, double value)
- {
- destination->insert(name, QVariant::fromValue<double>(value));
- }
+ void dump(const QString &name) const {
+ if (!contains(name)) {
+ qDebug() << "no" << name;
+ return;
+ }
-private:
- QVariantMap *destination;
-};
-}
+ QJsonObject o = collector->lookupRef(refs.at(names.indexOf(name)));
+ QJsonDocument d;
+ d.setObject(o);
+ qDebug() << name << "=" << d.toJson(QJsonDocument::Indented);
+ }
+ };
-class TestAgent : public QV4::Debugging::DebuggerAgent
-{
- Q_OBJECT
-public:
- TestAgent()
+ TestAgent(QV4::ExecutionEngine *engine)
: m_wasPaused(false)
, m_captureContextInfo(false)
+ , m_thrownValue(-1)
+ , collector(engine)
{
}
virtual void debuggerPaused(Debugger *debugger, PauseReason reason)
{
Q_ASSERT(m_debuggers.count() == 1 && m_debuggers.first() == debugger);
+ Q_ASSERT(debugger->engine() == collector.engine());
m_wasPaused = true;
m_pauseReason = reason;
m_statesWhenPaused << debugger->currentExecutionState();
- TestCollector collector(debugger->engine());
- QVariantMap tmp;
- collector.setDestination(&tmp);
- debugger->collectThrownValue(&collector);
- m_thrownValue = tmp["exception"];
+ {
+ Refs refs;
+ QV4::Debugging::RefHolder holder(&collector, &refs);
+ if (debugger->collectThrownValue(&collector)) {
+ Q_ASSERT(refs.size() > 0);
+ m_thrownValue = refs.first();
+ }
+ }
foreach (const TestBreakPoint &bp, m_breakPointsToAddWhenPaused)
debugger->addBreakPoint(bp.fileName, bp.lineNumber);
@@ -187,10 +179,9 @@ public:
while (!m_expressionRequests.isEmpty()) {
ExpressionRequest request = m_expressionRequests.takeFirst();
- QVariantMap result;
- collector.setDestination(&result);
+ m_expressionResults << Refs();
+ RefHolder holder(&collector, &m_expressionResults.last());
debugger->evaluateExpression(request.frameNr, request.expression, &collector);
- m_expressionResults << result[QString::fromLatin1("body")];
}
if (m_captureContextInfo)
@@ -219,18 +210,14 @@ public:
void captureContextInfo(Debugger *debugger)
{
- TestCollector collector(debugger->engine());
-
for (int i = 0, ei = m_stackTrace.size(); i != ei; ++i) {
- QVariantMap args;
- collector.setDestination(&args);
- debugger->collectArgumentsInContext(&collector, i);
- m_capturedArguments.append(args);
-
- QVariantMap locals;
- collector.setDestination(&locals);
- debugger->collectLocalsInContext(&collector, i);
- m_capturedLocals.append(locals);
+ m_capturedArguments.append(NamedRefs(&collector));
+ RefHolder argHolder(&collector, &m_capturedArguments.last().refs);
+ debugger->collectArgumentsInContext(&collector, &m_capturedArguments.last().names, i);
+
+ m_capturedLocals.append(NamedRefs(&collector));
+ RefHolder localHolder(&collector, &m_capturedLocals.last().refs);
+ debugger->collectLocalsInContext(&collector, &m_capturedLocals.last().names, i);
}
}
@@ -240,16 +227,17 @@ public:
QList<Debugger::ExecutionState> m_statesWhenPaused;
QList<TestBreakPoint> m_breakPointsToAddWhenPaused;
QVector<QV4::StackFrame> m_stackTrace;
- QList<QVariantMap> m_capturedArguments;
- QList<QVariantMap> m_capturedLocals;
- QVariant m_thrownValue;
+ QVector<NamedRefs> m_capturedArguments;
+ QVector<NamedRefs> m_capturedLocals;
+ qint64 m_thrownValue;
+ QV4::Debugging::DataCollector collector;
struct ExpressionRequest {
QString expression;
int frameNr;
};
QVector<ExpressionRequest> m_expressionRequests;
- QVector<QVariant> m_expressionResults;
+ QVector<Refs> m_expressionResults;
// Utility methods:
void dumpStackTrace() const
@@ -315,7 +303,7 @@ void tst_qv4debugger::init()
m_v4->enableDebugger();
m_engine->moveToThread(m_javaScriptThread);
m_javaScriptThread->start();
- m_debuggerAgent = new TestAgent;
+ m_debuggerAgent = new TestAgent(m_v4);
m_debuggerAgent->addDebugger(m_v4->debugger);
}
@@ -446,9 +434,12 @@ void tst_qv4debugger::conditionalBreakPoint()
QV4::Debugging::Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.first();
QCOMPARE(state.fileName, QString("conditionalBreakPoint"));
QCOMPARE(state.lineNumber, 3);
- QCOMPARE(m_debuggerAgent->m_capturedLocals[0].size(), 2);
- QVERIFY(m_debuggerAgent->m_capturedLocals[0].contains(QStringLiteral("i")));
- QCOMPARE(m_debuggerAgent->m_capturedLocals[0]["i"].toInt(), 11);
+
+ QVERIFY(m_debuggerAgent->m_capturedLocals.size() > 1);
+ const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedLocals.at(0);
+ QCOMPARE(frame0.size(), 2);
+ QVERIFY(frame0.contains("i"));
+ QCOMPARE(frame0.value("i").toInt(), 11);
}
void tst_qv4debugger::conditionalBreakPointInQml()
@@ -459,7 +450,7 @@ void tst_qv4debugger::conditionalBreakPointInQml()
QScopedPointer<QThread> debugThread(new QThread);
debugThread->start();
- QScopedPointer<TestAgent> debuggerAgent(new TestAgent);
+ QScopedPointer<TestAgent> debuggerAgent(new TestAgent(v4));
debuggerAgent->addDebugger(v4->debugger);
debuggerAgent->moveToThread(debugThread.data());
@@ -499,13 +490,15 @@ void tst_qv4debugger::readArguments()
m_debuggerAgent->addBreakPoint("readArguments", 2);
evaluateJavaScript(script, "readArguments");
QVERIFY(m_debuggerAgent->m_wasPaused);
- QCOMPARE(m_debuggerAgent->m_capturedArguments[0].size(), 4);
- QVERIFY(m_debuggerAgent->m_capturedArguments[0].contains(QStringLiteral("a")));
- QCOMPARE(m_debuggerAgent->m_capturedArguments[0]["a"].type(), QVariant::Double);
- QCOMPARE(m_debuggerAgent->m_capturedArguments[0]["a"].toDouble(), 1.0);
- QVERIFY(m_debuggerAgent->m_capturedArguments[0].contains("b"));
- QCOMPARE(m_debuggerAgent->m_capturedArguments[0]["b"].type(), QVariant::String);
- QCOMPARE(m_debuggerAgent->m_capturedArguments[0]["b"].toString(), QLatin1String("two"));
+ QVERIFY(m_debuggerAgent->m_capturedArguments.size() > 1);
+ const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedArguments.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"));
+ QCOMPARE(frame0.type(QStringLiteral("b")), QStringLiteral("string"));
+ QCOMPARE(frame0.value(QStringLiteral("b")).toString(), QStringLiteral("two"));
}
void tst_qv4debugger::readLocals()
@@ -521,12 +514,14 @@ void tst_qv4debugger::readLocals()
m_debuggerAgent->addBreakPoint("readLocals", 3);
evaluateJavaScript(script, "readLocals");
QVERIFY(m_debuggerAgent->m_wasPaused);
- QCOMPARE(m_debuggerAgent->m_capturedLocals[0].size(), 2);
- QVERIFY(m_debuggerAgent->m_capturedLocals[0].contains("c"));
- QCOMPARE(m_debuggerAgent->m_capturedLocals[0]["c"].type(), QVariant::Double);
- QCOMPARE(m_debuggerAgent->m_capturedLocals[0]["c"].toDouble(), 3.0);
- QVERIFY(m_debuggerAgent->m_capturedLocals[0].contains("d"));
- QCOMPARE(m_debuggerAgent->m_capturedLocals[0]["d"].toString(), QString("undefined"));
+ QVERIFY(m_debuggerAgent->m_capturedLocals.size() > 1);
+ const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedLocals.at(0);
+ QCOMPARE(frame0.size(), 2);
+ QVERIFY(frame0.contains("c"));
+ QCOMPARE(frame0.type("c"), QStringLiteral("number"));
+ QCOMPARE(frame0.value("c").toDouble(), 3.0);
+ QVERIFY(frame0.contains("d"));
+ QCOMPARE(frame0.type("d"), QStringLiteral("undefined"));
}
void tst_qv4debugger::readObject()
@@ -541,23 +536,46 @@ void tst_qv4debugger::readObject()
m_debuggerAgent->addBreakPoint("readObject", 3);
evaluateJavaScript(script, "readObject");
QVERIFY(m_debuggerAgent->m_wasPaused);
- QCOMPARE(m_debuggerAgent->m_capturedLocals[0].size(), 1);
- QVERIFY(m_debuggerAgent->m_capturedLocals[0].contains("b"));
- QCOMPARE(m_debuggerAgent->m_capturedLocals[0]["b"].type(), QVariant::Map);
-
- QVariantMap b = m_debuggerAgent->m_capturedLocals[0]["b"].toMap();
- QCOMPARE(b.size(), 2);
- QVERIFY(b.contains("head"));
- QCOMPARE(b["head"].type(), QVariant::Double);
- QCOMPARE(b["head"].toDouble(), 1.0);
- QVERIFY(b.contains("tail"));
- QCOMPARE(b["tail"].type(), QVariant::Map);
-
- QVariantMap b_tail = b["tail"].toMap();
- QCOMPARE(b_tail.size(), 2);
- QVERIFY(b_tail.contains("head"));
- QCOMPARE(b_tail["head"].type(), QVariant::String);
- QCOMPARE(b_tail["head"].toString(), QString("asdf"));
+ QVERIFY(m_debuggerAgent->m_capturedLocals.size() > 1);
+ const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedLocals.at(0);
+ QCOMPARE(frame0.size(), 1);
+ QVERIFY(frame0.contains("b"));
+ QCOMPARE(frame0.type("b"), QStringLiteral("object"));
+ QJsonObject b = frame0.rawValue("b");
+ QVERIFY(b.contains(QStringLiteral("properties")));
+ QVERIFY(b.value("properties").isArray());
+ QJsonArray b_props = b.value("properties").toArray();
+ QCOMPARE(b_props.size(), 2);
+
+ QVERIFY(b_props.at(0).isObject());
+ QJsonObject b_head = b_props.at(0).toObject();
+ QCOMPARE(b_head.value("name").toString(), QStringLiteral("head"));
+ QVERIFY(b_head.contains("ref"));
+ QJsonObject b_head_value = frame0.collector->lookupRef(b_head.value("ref").toInt());
+ QCOMPARE(b_head_value.value("type").toString(), QStringLiteral("number"));
+ QCOMPARE(b_head_value.value("value").toDouble(), 1.0);
+ QVERIFY(b_props.at(1).isObject());
+ QJsonObject b_tail = b_props.at(1).toObject();
+ 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());
+ 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();
+ QCOMPARE(b_tail_props.size(), 2);
+ QJsonObject b_tail_head = b_tail_props.at(0).toObject();
+ QCOMPARE(b_tail_head.value("name").toString(), QStringLiteral("head"));
+ QVERIFY(b_tail_head.contains("ref"));
+ QJsonObject b_tail_head_value = frame0.collector->lookupRef(b_tail_head.value("ref").toInt());
+ QCOMPARE(b_tail_head_value.value("type").toString(), QStringLiteral("string"));
+ QCOMPARE(b_tail_head_value.value("value").toString(), QStringLiteral("asdf"));
+ QJsonObject b_tail_tail = b_tail_props.at(1).toObject();
+ QCOMPARE(b_tail_tail.value("name").toString(), QStringLiteral("tail"));
+ QVERIFY(b_tail_tail.contains("ref"));
+
+ QJsonObject b_tail_tail_value = frame0.collector->lookupRef(b_tail_tail.value("ref").toInt());
+ QCOMPARE(b_tail_tail_value.value("type").toString(), QStringLiteral("null"));
}
void tst_qv4debugger::readContextInAllFrames()
@@ -581,18 +599,20 @@ void tst_qv4debugger::readContextInAllFrames()
QCOMPARE(m_debuggerAgent->m_capturedLocals.size(), 13);
for (int i = 0; i < 12; ++i) {
- QCOMPARE(m_debuggerAgent->m_capturedArguments[i].size(), 1);
- QVERIFY(m_debuggerAgent->m_capturedArguments[i].contains("n"));
- QCOMPARE(m_debuggerAgent->m_capturedArguments[i]["n"].type(), QVariant::Double);
- QCOMPARE(m_debuggerAgent->m_capturedArguments[i]["n"].toDouble(), i + 1.0);
-
- QCOMPARE(m_debuggerAgent->m_capturedLocals[i].size(), 1);
- QVERIFY(m_debuggerAgent->m_capturedLocals[i].contains("n_1"));
+ 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"));
if (i == 0) {
- QCOMPARE(m_debuggerAgent->m_capturedLocals[i]["n_1"].toString(), QString("undefined"));
+ QCOMPARE(locals.type("n_1"), QStringLiteral("undefined"));
} else {
- QCOMPARE(m_debuggerAgent->m_capturedLocals[i]["n_1"].type(), QVariant::Double);
- QCOMPARE(m_debuggerAgent->m_capturedLocals[i]["n_1"].toInt(), i);
+ QCOMPARE(locals.type("n_1"), QStringLiteral("number"));
+ QCOMPARE(locals.value("n_1").toInt(), i);
}
}
QCOMPARE(m_debuggerAgent->m_capturedArguments[12].size(), 0);
@@ -611,8 +631,11 @@ void tst_qv4debugger::pauseOnThrow()
QVERIFY(m_debuggerAgent->m_wasPaused);
QCOMPARE(m_debuggerAgent->m_pauseReason, Throwing);
QCOMPARE(m_debuggerAgent->m_stackTrace.size(), 2);
- QCOMPARE(m_debuggerAgent->m_thrownValue.type(), QVariant::String);
- QCOMPARE(m_debuggerAgent->m_thrownValue.toString(), QString("hard"));
+ QVERIFY(m_debuggerAgent->m_thrownValue >= qint64(0));
+ QJsonObject exception = m_debuggerAgent->collector.lookupRef(m_debuggerAgent->m_thrownValue);
+// DUMP_JSON(exception);
+ QCOMPARE(exception.value("type").toString(), QStringLiteral("string"));
+ QCOMPARE(exception.value("value").toString(), QStringLiteral("hard"));
}
void tst_qv4debugger::breakInCatch()
@@ -674,8 +697,16 @@ void tst_qv4debugger::evaluateExpression()
evaluateJavaScript(script, "evaluateExpression");
QCOMPARE(m_debuggerAgent->m_expressionResults.count(), 2);
- QCOMPARE(m_debuggerAgent->m_expressionResults[0].toInt(), 10);
- QCOMPARE(m_debuggerAgent->m_expressionResults[1].toInt(), 20);
+ QCOMPARE(m_debuggerAgent->m_expressionResults[0].size(), 1);
+ QJsonObject result0 =
+ m_debuggerAgent->collector.lookupRef(m_debuggerAgent->m_expressionResults[0].first());
+ 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(result1.value("type").toString(), QStringLiteral("number"));
+ QCOMPARE(result1.value("value").toInt(), 20);
}
QTEST_MAIN(tst_qv4debugger)