aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@theqtcompany.com>2015-07-27 14:40:26 +0200
committerUlf Hermann <ulf.hermann@theqtcompany.com>2015-08-10 10:05:01 +0000
commitdc341e6c1c524330b838a62ceeaa148a01dc0729 (patch)
treee42e93b6af0ecb347d4f3850b8e04acd796a4b66 /src
parente01bea8999d2f58add58874bd3e6792f509b131b (diff)
Change data collection for debugging to use QV4::Value.
This patch changes the variable collection to store QV4::Value values into a JS array, which is retained by the collector. This prevents any GC issues, and gives a nice mapping from handle (used in the debugging protocol) to JS value. It also allows for easy "shallow" object serialization: any lookup can start with the QV4::Value, and add any values it encounters to the array. Testing is changed to use this collector directly, thereby testing the class that is actually used to generate protocol data. Task-number: QTBUG-47061 Change-Id: Iec75c4f74c08495e2a8af0fedf304f76f8385fd7 Reviewed-by: Erik Verbruggen <erik.verbruggen@theqtcompany.com>
Diffstat (limited to 'src')
-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
4 files changed, 374 insertions, 398 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();