diff options
-rw-r--r-- | src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp | 312 | ||||
-rw-r--r-- | src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h | 8 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4debugging.cpp | 353 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4debugging_p.h | 99 | ||||
-rw-r--r-- | tests/auto/qml/qv4debugger/tst_qv4debugger.cpp | 293 |
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) |