diff options
8 files changed, 274 insertions, 113 deletions
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp index aed2759383..5d2e754057 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -118,15 +118,18 @@ int QV4DataCollector::encodeScopeType(QV4::Heap::ExecutionContext::ContextType s } } -QV4DataCollector::QV4DataCollector(QV4::ExecutionEngine *engine) : m_engine(engine) +QV4DataCollector::QV4DataCollector(QV4::ExecutionEngine *engine) + : m_engine(engine), m_namesAsObjects(true), m_redundantRefs(true) { m_values.set(engine, engine->newArrayObject()); } +// TODO: Directly call addRef() once we don't need to support redundantRefs anymore QV4DataCollector::Ref QV4DataCollector::collect(const QV4::ScopedValue &value) { Ref ref = addRef(value); - m_collectedRefs.append(ref); + if (m_redundantRefs) + m_collectedRefs.append(ref); return ref; } @@ -190,24 +193,33 @@ const QV4::Object *collectProperty(const QV4::ScopedValue &value, QV4::Execution } } -QJsonObject QV4DataCollector::lookupRef(Ref ref) +QJsonObject QV4DataCollector::lookupRef(Ref ref, bool deep) { QJsonObject dict; - if (lookupSpecialRef(ref, &dict)) - return dict; + + if (m_namesAsObjects) { + if (lookupSpecialRef(ref, &dict)) + return dict; + } + + if (m_redundantRefs) + deep = true; dict.insert(QStringLiteral("handle"), qint64(ref)); QV4::Scope scope(engine()); QV4::ScopedValue value(scope, getValue(ref)); - if (const QV4::Object *o = collectProperty(value, engine(), dict)) - dict.insert(QStringLiteral("properties"), collectProperties(o)); + const QV4::Object *object = collectProperty(value, engine(), dict); + if (deep && object) + dict.insert(QStringLiteral("properties"), collectProperties(object)); return dict; } +// TODO: Drop this method once we don't need to support namesAsObjects anymore QV4DataCollector::Ref QV4DataCollector::addFunctionRef(const QString &functionName) { + Q_ASSERT(m_namesAsObjects); Ref ref = addRef(QV4::Primitive::emptyValue(), false); QJsonObject dict; @@ -220,8 +232,10 @@ QV4DataCollector::Ref QV4DataCollector::addFunctionRef(const QString &functionNa return ref; } +// TODO: Drop this method once we don't need to support namesAsObjects anymore QV4DataCollector::Ref QV4DataCollector::addScriptRef(const QString &scriptName) { + Q_ASSERT(m_namesAsObjects); Ref ref = addRef(QV4::Primitive::emptyValue(), false); QJsonObject dict; @@ -250,6 +264,7 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr) if (!ctxt) return false; + Refs collectedRefs; QV4::ScopedValue v(scope); int nFormals = ctxt->formalCount(); for (unsigned i = 0, ei = nFormals; i != ei; ++i) { @@ -258,7 +273,7 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr) qName = name->string; names.append(qName); v = ctxt->argument(i); - collect(v); + collectedRefs.append(collect(v)); } for (unsigned i = 0, ei = ctxt->variableCount(); i != ei; ++i) { @@ -267,19 +282,24 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr) qName = name->string; names.append(qName); v = ctxt->d()->locals[i]; - collect(v); + collectedRefs.append(collect(v)); } QV4::ScopedObject scopeObject(scope, engine()->newObject()); - Q_ASSERT(names.size() == m_collectedRefs.size()); - for (int i = 0, ei = m_collectedRefs.size(); i != ei; ++i) + Q_ASSERT(names.size() == collectedRefs.size()); + for (int i = 0, ei = collectedRefs.size(); i != ei; ++i) scopeObject->put(engine(), names.at(i), - QV4::Value::fromReturnedValue(getValue(m_collectedRefs.at(i)))); + QV4::Value::fromReturnedValue(getValue(collectedRefs.at(i)))); Ref scopeObjectRef = addRef(scopeObject); - dict->insert(QStringLiteral("ref"), qint64(scopeObjectRef)); - m_collectedRefs.append(scopeObjectRef); + if (m_redundantRefs) { + dict->insert(QStringLiteral("ref"), qint64(scopeObjectRef)); + m_collectedRefs.append(scopeObjectRef); + } else { + *dict = lookupRef(scopeObjectRef, true); + } + return true; } @@ -291,15 +311,16 @@ QJsonObject toRef(QV4DataCollector::Ref ref) { QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int frameNr) { - QV4DataCollector::Ref ref; - QJsonObject frame; frame[QLatin1String("index")] = frameNr; frame[QLatin1String("debuggerFrame")] = false; - ref = addFunctionRef(stackFrame.function); - frame[QLatin1String("func")] = toRef(ref); - ref = addScriptRef(stackFrame.source); - frame[QLatin1String("script")] = toRef(ref); + if (m_namesAsObjects) { + frame[QLatin1String("func")] = toRef(addFunctionRef(stackFrame.function)); + frame[QLatin1String("script")] = toRef(addScriptRef(stackFrame.source)); + } else { + frame[QLatin1String("func")] = stackFrame.function; + frame[QLatin1String("script")] = stackFrame.source; + } frame[QLatin1String("line")] = stackFrame.line - 1; if (stackFrame.column >= 0) frame[QLatin1String("column")] = stackFrame.column; @@ -338,15 +359,17 @@ QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int return frame; } +// TODO: Drop this method once we don't need to support redundantRefs anymore QJsonArray QV4DataCollector::flushCollectedRefs() { + Q_ASSERT(m_redundantRefs); QJsonArray refs; std::sort(m_collectedRefs.begin(), m_collectedRefs.end()); for (int i = 0, ei = m_collectedRefs.size(); i != ei; ++i) { QV4DataCollector::Ref ref = m_collectedRefs.at(i); if (i > 0 && ref == m_collectedRefs.at(i - 1)) continue; - refs.append(lookupRef(ref)); + refs.append(lookupRef(ref, true)); } m_collectedRefs.clear(); @@ -358,6 +381,8 @@ void QV4DataCollector::clear() m_values.set(engine(), engine()->newArrayObject()); m_collectedRefs.clear(); m_specialRefs.clear(); + m_namesAsObjects = true; + m_redundantRefs = true; } QV4DataCollector::Ref QV4DataCollector::addRef(QV4::Value value, bool deduplicate) @@ -401,8 +426,10 @@ QV4::ReturnedValue QV4DataCollector::getValue(Ref ref) return array->getIndexed(ref, Q_NULLPTR); } +// TODO: Drop this method once we don't need to support namesAsObjects anymore bool QV4DataCollector::lookupSpecialRef(Ref ref, QJsonObject *dict) { + Q_ASSERT(m_namesAsObjects); SpecialRefs::const_iterator it = m_specialRefs.constFind(ref); if (it == m_specialRefs.cend()) return false; @@ -440,7 +467,8 @@ QJsonObject QV4DataCollector::collectAsJson(const QString &name, const QV4::Scop if (value->isManaged() && !value->isString()) { Ref ref = addRef(value); dict.insert(QStringLiteral("ref"), qint64(ref)); - m_collectedRefs.append(ref); + if (m_redundantRefs) + m_collectedRefs.append(ref); } collectProperty(value, engine(), dict); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h index fd6356f22e..2c2514a1b3 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h @@ -66,34 +66,42 @@ public: QV4DataCollector(QV4::ExecutionEngine *engine); - Ref collect(const QV4::ScopedValue &value); - Ref addFunctionRef(const QString &functionName); - Ref addScriptRef(const QString &scriptName); + Ref collect(const QV4::ScopedValue &value); // only for redundantRefs + Ref addFunctionRef(const QString &functionName); // only for namesAsObjects + Ref addScriptRef(const QString &scriptName); // only for namesAsObjects + + void setNamesAsObjects(bool namesAsObjects) { m_namesAsObjects = namesAsObjects; } + bool namesAsObjects() const { return m_namesAsObjects; } + + void setRedundantRefs(bool redundantRefs) { m_redundantRefs = redundantRefs; } + bool redundantRefs() const { return m_redundantRefs; } bool isValidRef(Ref ref) const; - QJsonObject lookupRef(Ref ref); + QJsonObject lookupRef(Ref ref, bool deep); bool collectScope(QJsonObject *dict, int frameNr, int scopeNr); QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr); QV4::ExecutionEngine *engine() const { return m_engine; } - QJsonArray flushCollectedRefs(); + QJsonArray flushCollectedRefs(); // only for redundantRefs void clear(); private: Ref addRef(QV4::Value value, bool deduplicate = true); QV4::ReturnedValue getValue(Ref ref); - bool lookupSpecialRef(Ref ref, QJsonObject *dict); + bool lookupSpecialRef(Ref ref, QJsonObject *dict); // only for namesAsObjects QJsonArray collectProperties(const QV4::Object *object); QJsonObject collectAsJson(const QString &name, const QV4::ScopedValue &value); void collectArgumentsInContext(); QV4::ExecutionEngine *m_engine; - Refs m_collectedRefs; + Refs m_collectedRefs; // only for redundantRefs QV4::PersistentValue m_values; - typedef QHash<Ref, QJsonObject> SpecialRefs; - SpecialRefs m_specialRefs; + typedef QHash<Ref, QJsonObject> SpecialRefs; // only for namesAsObjects + SpecialRefs m_specialRefs; // only for namesAsObjects + bool m_namesAsObjects; + bool m_redundantRefs; }; QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp index df316c1ae6..a624cf22a4 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp @@ -148,7 +148,7 @@ void BacktraceJob::run() result.insert(QStringLiteral("toFrame"), fromFrame + frameArray.size()); result.insert(QStringLiteral("frames"), frameArray); } - collectedRefs = collector->flushCollectedRefs(); + flushRedundantRefs(); } FrameJob::FrameJob(QV4DataCollector *collector, int frameNr) : @@ -163,7 +163,7 @@ void FrameJob::run() success = false; } else { result = collector->buildFrame(frames[frameNr], frameNr); - collectedRefs = collector->flushCollectedRefs(); + flushRedundantRefs(); success = true; } } @@ -193,7 +193,7 @@ void ScopeJob::run() result[QLatin1String("index")] = scopeNr; result[QLatin1String("frameIndex")] = frameNr; result[QLatin1String("object")] = object; - collectedRefs = collector->flushCollectedRefs(); + flushRedundantRefs(); } bool ScopeJob::wasSuccessful() const @@ -223,9 +223,9 @@ void ValueLookupJob::run() exception = QString::fromLatin1("Invalid Ref: %1").arg(ref); break; } - result[QString::number(ref)] = collector->lookupRef(ref); + result[QString::number(ref)] = collector->lookupRef(ref, true); } - collectedRefs = collector->flushCollectedRefs(); + flushRedundantRefs(); if (scopeObject) engine->popContext(); } @@ -246,8 +246,9 @@ void ExpressionEvalJob::handleResult(QV4::ScopedValue &value) { if (hasExeption()) exception = value->toQStringNoThrow(); - result = collector->lookupRef(collector->collect(value)); - collectedRefs = collector->flushCollectedRefs(); + result = collector->lookupRef(collector->collect(value), true); + if (collector->redundantRefs()) + collectedRefs = collector->flushCollectedRefs(); } const QString &ExpressionEvalJob::exceptionMessage() const @@ -260,8 +261,10 @@ const QJsonObject &ExpressionEvalJob::returnValue() const return result; } +// TODO: Drop this method once we don't need to support redundantRefs anymore const QJsonArray &ExpressionEvalJob::refs() const { + Q_ASSERT(collector->redundantRefs()); return collectedRefs; } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h index 00d3e6206a..eca8710e15 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h @@ -78,11 +78,24 @@ class CollectJob : public QV4DebugJob protected: QV4DataCollector *collector; QJsonObject result; - QJsonArray collectedRefs; + QJsonArray collectedRefs; // only for redundantRefs + + void flushRedundantRefs() + { + if (collector->redundantRefs()) + collectedRefs = collector->flushCollectedRefs(); + } + public: CollectJob(QV4DataCollector *collector) : collector(collector) {} const QJsonObject &returnValue() const { return result; } - const QJsonArray &refs() const { return collectedRefs; } + + // TODO: Drop this method once we don't need to support redundantRefs anymore + const QJsonArray &refs() const + { + Q_ASSERT(collector->redundantRefs()); + return collectedRefs; + } }; class BacktraceJob: public CollectJob @@ -133,7 +146,7 @@ class ExpressionEvalJob: public JavaScriptJob QV4DataCollector *collector; QString exception; QJsonObject result; - QJsonArray collectedRefs; + QJsonArray collectedRefs; // only for redundantRefs public: ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr, int context, @@ -141,7 +154,7 @@ public: void handleResult(QV4::ScopedValue &value) override; const QString &exceptionMessage() const; const QJsonObject &returnValue() const; - const QJsonArray &refs() const; + const QJsonArray &refs() const; // only for redundantRefs }; class GatherSourcesJob: public QV4DebugJob diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp index 1d2cc092dc..fbf6397a18 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp @@ -121,8 +121,18 @@ protected: response.insert(QStringLiteral("running"), debugService->debuggerAgent.isRunning()); } + QV4DataCollector *saneCollector(QV4Debugger *debugger) + { + QV4DataCollector *collector = debugger->collector(); + collector->setNamesAsObjects(debugService->clientRequiresNamesAsObjects()); + collector->setRedundantRefs(debugService->clientRequiresRedundantRefs()); + return collector; + } + + // TODO: drop this method once we don't need to support redundantRefs anymore. void addRefs(const QJsonArray &refs) { + Q_ASSERT(debugService->clientRequiresRedundantRefs()); response.insert(QStringLiteral("refs"), refs); } @@ -286,7 +296,7 @@ public: return; } - BacktraceJob job(debugger->collector(), fromFrame, toFrame); + BacktraceJob job(saneCollector(debugger), fromFrame, toFrame); debugger->runInEngine(&job); // response: @@ -295,7 +305,8 @@ public: addSuccess(true); addRunning(); addBody(job.returnValue()); - addRefs(job.refs()); + if (debugService->clientRequiresRedundantRefs()) + addRefs(job.refs()); } }; @@ -322,7 +333,7 @@ public: return; } - FrameJob job(debugger->collector(), frameNr); + FrameJob job(saneCollector(debugger), frameNr); debugger->runInEngine(&job); if (!job.wasSuccessful()) { createErrorResponse(QStringLiteral("frame retrieval failed")); @@ -337,7 +348,8 @@ public: addSuccess(true); addRunning(); addBody(job.returnValue()); - addRefs(job.refs()); + if (debugService->clientRequiresRedundantRefs()) + addRefs(job.refs()); } }; @@ -369,7 +381,7 @@ public: return; } - ScopeJob job(debugger->collector(), frameNr, scopeNr); + ScopeJob job(saneCollector(debugger), frameNr, scopeNr); debugger->runInEngine(&job); if (!job.wasSuccessful()) { createErrorResponse(QStringLiteral("scope retrieval failed")); @@ -382,7 +394,8 @@ public: addSuccess(true); addRunning(); addBody(job.returnValue()); - addRefs(job.refs()); + if (debugService->clientRequiresRedundantRefs()) + addRefs(job.refs()); } }; @@ -410,7 +423,7 @@ public: debugger = debuggers.first(); } - ValueLookupJob job(handles, debugger->collector()); + ValueLookupJob job(handles, saneCollector(debugger)); debugger->runInEngine(&job); if (!job.exceptionMessage().isEmpty()) { createErrorResponse(job.exceptionMessage()); @@ -421,7 +434,8 @@ public: addSuccess(true); addRunning(); addBody(job.returnValue()); - addRefs(job.refs()); + if (debugService->clientRequiresRedundantRefs()) + addRefs(job.refs()); } } }; @@ -630,7 +644,7 @@ public: } ExpressionEvalJob job(debugger->engine(), frame, context, expression, - debugger->collector()); + saneCollector(debugger)); debugger->runInEngine(&job); if (job.hasExeption()) { createErrorResponse(job.exceptionMessage()); @@ -640,7 +654,8 @@ public: addSuccess(true); addRunning(); addBody(job.returnValue()); - addRefs(job.refs()); + if (debugService->clientRequiresRedundantRefs()) + addRefs(job.refs()); } } }; @@ -662,7 +677,7 @@ V8CommandHandler *QV4DebugServiceImpl::v8CommandHandler(const QString &command) QV4DebugServiceImpl::QV4DebugServiceImpl(QObject *parent) : QQmlConfigurableDebugService<QV4DebugService>(1, parent), - debuggerAgent(this), theSelectedFrame(0), + debuggerAgent(this), theSelectedFrame(0), redundantRefs(true), namesAsObjects(true), unknownV8CommandHandler(new UnknownV8CommandHandler) { addHandler(new V8VersionRequest); @@ -766,6 +781,14 @@ void QV4DebugServiceImpl::messageReceived(const QByteArray &message) TRACE_PROTOCOL(qDebug() << "... type:" << type); if (type == V4_CONNECT) { + QJsonObject parameters = QJsonDocument::fromJson(payload).object(); + namesAsObjects = true; + redundantRefs = true; + if (parameters.contains("namesAsObjects")) + namesAsObjects = parameters.value("namesAsObjects").toBool(); + if (parameters.contains("redundantRefs")) + namesAsObjects = parameters.value("redundantRefs").toBool(); + emit messageToClient(name(), packMessage(type)); stopWaiting(); } else if (type == V4_PAUSE) { diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h index 69e32189b8..bb13890ae4 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h @@ -86,6 +86,9 @@ public: int selectedFrame() const; void selectFrame(int frameNr); + bool clientRequiresRedundantRefs() const { return redundantRefs; } + bool clientRequiresNamesAsObjects() const { return namesAsObjects; } + QV4DebuggerAgent debuggerAgent; protected: @@ -105,6 +108,9 @@ private: static int sequence; int theSelectedFrame; + bool redundantRefs; + bool namesAsObjects; + void addHandler(V8CommandHandler* handler); QHash<QString, V8CommandHandler*> handlers; QScopedPointer<UnknownV8CommandHandler> unknownV8CommandHandler; diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp index 31b8d63ec2..d248cf9708 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp +++ b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp @@ -257,7 +257,7 @@ public: stringify = jsEngine.evaluate(QLatin1String("JSON.stringify")); } - void connect(); + void connect(bool redundantRefs = false, bool namesAsObjects = false); void interrupt(); void continueDebugging(StepAction stepAction); @@ -304,9 +304,13 @@ public: }; -void QJSDebugClient::connect() +void QJSDebugClient::connect(bool redundantRefs, bool namesAsObjects) { - sendMessage(packMessage(CONNECT)); + QJSValue jsonVal = parser.call(QJSValueList() << QLatin1String("{}")); + jsonVal.setProperty("redundantRefs", QJSValue(redundantRefs)); + jsonVal.setProperty("namesAsObjects", QJSValue(namesAsObjects)); + sendMessage(packMessage(CONNECT, + stringify.call(QJSValueList() << jsonVal).toString().toUtf8())); } void QJSDebugClient::interrupt() @@ -870,8 +874,10 @@ void tst_QQmlDebugJS::interrupt() //void connect() QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); init(qmlscene); - client->connect(); + client->connect(redundantRefs, namesAsObjects); client->interrupt(); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(interruptRequested()))); @@ -882,8 +888,10 @@ void tst_QQmlDebugJS::getVersion() //void version() QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); init(qmlscene); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(connected()))); client->version(); @@ -894,9 +902,11 @@ void tst_QQmlDebugJS::getVersionWhenAttaching() { //void version() QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); init(qmlscene, QLatin1String(TIMER_QMLFILE), false); - client->connect(); + client->connect(redundantRefs, namesAsObjects); client->version(); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); @@ -907,8 +917,10 @@ void tst_QQmlDebugJS::disconnect() //void disconnect() QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); init(qmlscene); - client->connect(); + client->connect(redundantRefs, namesAsObjects); client->disconnect(); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); @@ -918,12 +930,14 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnCompleted() { //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 34; init(qmlscene, ONCOMPLETED_QMLFILE); client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); QString jsonString(client->response); @@ -939,12 +953,14 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnComponentCreated() { //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 34; init(qmlscene, CREATECOMPONENT_QMLFILE); client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); QString jsonString(client->response); @@ -959,10 +975,12 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnComponentCreated() void tst_QQmlDebugJS::setBreakpointInScriptOnTimerCallback() { QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 35; init(qmlscene, TIMER_QMLFILE); - client->connect(); + client->connect(redundantRefs, namesAsObjects); //We can set the breakpoint after connect() here because the timer is repeating and if we miss //its first iteration we can still catch the second one. client->setBreakpoint(QLatin1String(TIMER_QMLFILE), sourceLine, -1, true); @@ -981,12 +999,14 @@ void tst_QQmlDebugJS::setBreakpointInScriptInDifferentFile() { //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 31; init(qmlscene, LOADJSFILE_QMLFILE); client->setBreakpoint(QLatin1String(TEST_JSFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); QString jsonString(client->response); @@ -1002,13 +1022,15 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnComment() { //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 34; int actualLine = 36; init(qmlscene, BREAKPOINTRELOCATION_QMLFILE); client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QEXPECT_FAIL("", "Relocation of breakpoints is disabled right now", Abort); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()), 1)); @@ -1025,13 +1047,15 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnEmptyLine() { //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 35; int actualLine = 36; init(qmlscene, BREAKPOINTRELOCATION_QMLFILE); client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QEXPECT_FAIL("", "Relocation of breakpoints is disabled right now", Abort); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()), 1)); @@ -1048,12 +1072,14 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnOptimizedBinding() { //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 39; init(qmlscene, BREAKPOINTRELOCATION_QMLFILE); client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); QString jsonString(client->response); @@ -1068,11 +1094,13 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnOptimizedBinding() void tst_QQmlDebugJS::setBreakpointInScriptWithCondition() { QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int out = 10; int sourceLine = 37; init(qmlscene, CONDITION_QMLFILE); - client->connect(); + client->connect(redundantRefs, namesAsObjects); //The breakpoint is in a timer loop so we can set it after connect(). client->setBreakpoint(QLatin1String(CONDITION_QMLFILE), sourceLine, 1, true, QLatin1String("a > 10")); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); @@ -1105,12 +1133,14 @@ void tst_QQmlDebugJS::setBreakpointInScriptWithCondition() void tst_QQmlDebugJS::setBreakpointInScriptThatQuits() { QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); init(qmlscene, QUIT_QMLFILE); int sourceLine = 36; client->setBreakpoint(QLatin1String(QUIT_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); QString jsonString(client->response); @@ -1147,12 +1177,14 @@ void tst_QQmlDebugJS::clearBreakpoint() { //void clearBreakpoint(int breakpoint); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine1 = 37; int sourceLine2 = 38; init(qmlscene, CHANGEBREAKPOINT_QMLFILE); - client->connect(); + client->connect(redundantRefs, namesAsObjects); //The breakpoints are in a timer loop so we can set them after connect(). //Furthermore the breakpoints should be hit in the right order because setting of breakpoints //can only occur in the QML event loop. (see QCOMPARE for sourceLine2 below) @@ -1195,10 +1227,12 @@ void tst_QQmlDebugJS::setExceptionBreak() { //void setExceptionBreak(QString type, bool enabled = false); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); init(qmlscene, EXCEPTION_QMLFILE); client->setExceptionBreak(QJSDebugClient::All,true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); } @@ -1206,12 +1240,14 @@ void tst_QQmlDebugJS::stepNext() { //void continueDebugging(StepAction stepAction, int stepCount = 1); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 37; init(qmlscene, STEPACTION_QMLFILE); client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->continueDebugging(QJSDebugClient::Next); @@ -1230,13 +1266,15 @@ void tst_QQmlDebugJS::stepIn() { //void continueDebugging(StepAction stepAction, int stepCount = 1); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 41; int actualLine = 37; init(qmlscene, STEPACTION_QMLFILE); client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, 1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->continueDebugging(QJSDebugClient::In); @@ -1255,13 +1293,15 @@ void tst_QQmlDebugJS::stepOut() { //void continueDebugging(StepAction stepAction, int stepCount = 1); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 37; int actualLine = 41; init(qmlscene, STEPACTION_QMLFILE); client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->continueDebugging(QJSDebugClient::Out); @@ -1280,6 +1320,8 @@ void tst_QQmlDebugJS::continueDebugging() { //void continueDebugging(StepAction stepAction, int stepCount = 1); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine1 = 41; int sourceLine2 = 38; @@ -1287,7 +1329,7 @@ void tst_QQmlDebugJS::continueDebugging() client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine1, -1, true); client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine2, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->continueDebugging(QJSDebugClient::Continue); @@ -1306,12 +1348,14 @@ void tst_QQmlDebugJS::backtrace() { //void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 34; init(qmlscene, ONCOMPLETED_QMLFILE); client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->backtrace(); @@ -1322,12 +1366,14 @@ void tst_QQmlDebugJS::getFrameDetails() { //void frame(int number = -1); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 34; init(qmlscene, ONCOMPLETED_QMLFILE); client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->frame(); @@ -1338,12 +1384,14 @@ void tst_QQmlDebugJS::getScopeDetails() { //void scope(int number = -1, int frameNumber = -1); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 34; init(qmlscene, ONCOMPLETED_QMLFILE); client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->scope(); @@ -1374,11 +1422,13 @@ void tst_QQmlDebugJS::evaluateInLocalScope() //void evaluate(QString expr, bool global = false, bool disableBreak = false, int frame = -1, const QVariantMap &addContext = QVariantMap()); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 34; init(qmlscene, ONCOMPLETED_QMLFILE); client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->frame(); @@ -1461,10 +1511,12 @@ void tst_QQmlDebugJS::getScripts() //void scripts(int types = -1, QList<int> ids = QList<int>(), bool includeSource = false, QVariant filter = QVariant()); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); init(qmlscene); client->setBreakpoint(QString(TEST_QMLFILE), 35, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->scripts(); @@ -1482,8 +1534,16 @@ void tst_QQmlDebugJS::getScripts() void tst_QQmlDebugJS::targetData() { QTest::addColumn<bool>("qmlscene"); - QTest::newRow("custom") << false; - QTest::newRow("qmlscene") << true; + QTest::addColumn<bool>("redundantRefs"); + QTest::addColumn<bool>("namesAsObjects"); + QTest::newRow("custom / redundant / objects") << false << true << true; + QTest::newRow("qmlscene / redundant / objects") << true << true << true; + QTest::newRow("custom / redundant / strings") << false << true << false; + QTest::newRow("qmlscene / redundant / strings") << true << true << false; + QTest::newRow("custom / sparse / objects") << false << false << true; + QTest::newRow("qmlscene / sparse / objects") << true << false << true; + QTest::newRow("custom / sparse / strings") << false << false << false; + QTest::newRow("qmlscene / sparse / strings") << true << false << false; } QTEST_MAIN(tst_QQmlDebugJS) diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index 56320b8365..6d0d884ed7 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -120,7 +120,6 @@ public: typedef QV4DataCollector::Refs Refs; typedef QV4DataCollector::Ref Ref; struct NamedRefs { - QJsonArray refs; QJsonObject scope; int size() const { @@ -131,15 +130,6 @@ public: return scope.contains(name); } - QJsonObject resolveRef(int ref) const { - foreach (const QJsonValue &val, refs) { - QJsonObject obj = val.toObject(); - if (obj.value(QLatin1String("handle")).toInt() == ref) - return obj; - } - return QJsonObject(); - } - #define DUMP_JSON(x) {\ QJsonDocument doc(x);\ qDebug() << #x << "=" << doc.toJson(QJsonDocument::Indented);\ @@ -209,7 +199,6 @@ public slots: request.expression, &collector); debugger->runInEngine(&job); m_expressionResults << job.returnValue(); - m_expressionRefs << job.refs(); } if (m_captureContextInfo) @@ -235,24 +224,17 @@ public: ScopeJob job(&collector, i, 0); debugger->runInEngine(&job); NamedRefs &refs = m_capturedScope.last(); - refs.refs = job.refs(); QJsonObject object = job.returnValue(); object = object.value(QLatin1String("object")).toObject(); - int ref = object.value(QLatin1String("ref")).toInt(); - object = refs.resolveRef(ref); + if (object.contains("ref") && !object.contains("properties")) { + QVERIFY(collector.redundantRefs()); + object = collector.lookupRef(object.value("ref").toInt(), true); + QVERIFY(object.contains("properties")); + } foreach (const QJsonValue &value, object.value(QLatin1String("properties")).toArray()) { QJsonObject property = value.toObject(); QString name = property.value(QLatin1String("name")).toString(); property.remove(QLatin1String("name")); - if (property.contains(QLatin1String("ref"))) { - int childRef = property.value(QLatin1String("ref")).toInt(); - if (childRef >= 0 && refs.refs.size() > childRef) { - property.remove(QLatin1String("ref")); - property.insert(QLatin1String("properties"), - refs.resolveRef(childRef).value( - QLatin1String("properties")).toArray()); - } - } refs.scope.insert(name, property); } } @@ -283,7 +265,6 @@ public: QVector<ExpressionRequest> m_expressionRequests; QV4Debugger::Speed m_resumeSpeed; QList<QJsonObject> m_expressionResults; - QList<QJsonArray> m_expressionRefs; QV4Debugger *m_debugger; // Utility methods: @@ -315,9 +296,13 @@ private slots: void conditionalBreakPointInQml(); // context access: + void readArguments_data() { redundancy_data(); } void readArguments(); + void readLocals_data() { redundancy_data(); } void readLocals(); + void readObject_data() { redundancy_data(); } void readObject(); + void readContextInAllFrames_data() { redundancy_data(); } void readContextInAllFrames(); // exceptions: @@ -325,7 +310,9 @@ private slots: void breakInCatch(); void breakInWith(); + void evaluateExpression_data() { redundancy_data(); } void evaluateExpression(); + void stepToEndOfScript_data() { redundancy_data(); } void stepToEndOfScript(); void lastLineOfLoop_data(); @@ -343,6 +330,8 @@ private: waitForSignal(m_engine, SIGNAL(evaluateFinished()), /*timeout*/0); } + void redundancy_data(); + TestEngine *m_engine; QV4::ExecutionEngine *m_v4; TestAgent *m_debuggerAgent; @@ -537,6 +526,9 @@ void tst_qv4debugger::conditionalBreakPointInQml() void tst_qv4debugger::readArguments() { + QFETCH(bool, redundantRefs); + m_debuggerAgent->collector.setRedundantRefs(redundantRefs); + m_debuggerAgent->m_captureContextInfo = true; QString script = "function f(a, b, c, d) {\n" @@ -560,6 +552,9 @@ void tst_qv4debugger::readArguments() void tst_qv4debugger::readLocals() { + QFETCH(bool, redundantRefs); + m_debuggerAgent->collector.setRedundantRefs(redundantRefs); + m_debuggerAgent->m_captureContextInfo = true; QString script = "function f(a, b) {\n" @@ -583,6 +578,9 @@ void tst_qv4debugger::readLocals() void tst_qv4debugger::readObject() { + QFETCH(bool, redundantRefs); + m_debuggerAgent->collector.setRedundantRefs(redundantRefs); + m_debuggerAgent->m_captureContextInfo = true; QString script = "function f(a) {\n" @@ -599,6 +597,12 @@ void tst_qv4debugger::readObject() QVERIFY(frame0.contains("b")); QCOMPARE(frame0.type("b"), QStringLiteral("object")); QJsonObject b = frame0.rawValue("b"); + QVERIFY(b.contains(QStringLiteral("ref"))); + QVERIFY(b.contains(QStringLiteral("value"))); + QVERIFY(!b.contains(QStringLiteral("properties"))); + QVERIFY(b.value("value").isDouble()); + QCOMPARE(b.value("value").toInt(), 2); + b = m_debuggerAgent->collector.lookupRef(b.value("ref").toInt(), true); QVERIFY(b.contains(QStringLiteral("properties"))); QVERIFY(b.value("properties").isArray()); QJsonArray b_props = b.value("properties").toArray(); @@ -614,7 +618,8 @@ void tst_qv4debugger::readObject() QCOMPARE(b_tail.value("name").toString(), QStringLiteral("tail")); QVERIFY(b_tail.contains("ref")); - QJsonObject b_tail_value = m_debuggerAgent->collector.lookupRef(b_tail.value("ref").toInt()); + QJsonObject b_tail_value = m_debuggerAgent->collector.lookupRef(b_tail.value("ref").toInt(), + true); 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(); @@ -631,6 +636,9 @@ void tst_qv4debugger::readObject() void tst_qv4debugger::readContextInAllFrames() { + QFETCH(bool, redundantRefs); + m_debuggerAgent->collector.setRedundantRefs(redundantRefs); + m_debuggerAgent->m_captureContextInfo = true; QString script = "function fact(n) {\n" @@ -678,7 +686,8 @@ void tst_qv4debugger::pauseOnThrow() QCOMPARE(m_debuggerAgent->m_pauseReason, QV4Debugger::Throwing); QCOMPARE(m_debuggerAgent->m_stackTrace.size(), 2); QVERIFY(m_debuggerAgent->m_thrownValue >= qint64(0)); - QJsonObject exception = m_debuggerAgent->collector.lookupRef(m_debuggerAgent->m_thrownValue); + QJsonObject exception = m_debuggerAgent->collector.lookupRef(m_debuggerAgent->m_thrownValue, + true); // DUMP_JSON(exception); QCOMPARE(exception.value("type").toString(), QStringLiteral("string")); QCOMPARE(exception.value("value").toString(), QStringLiteral("hard")); @@ -722,6 +731,9 @@ void tst_qv4debugger::breakInWith() void tst_qv4debugger::evaluateExpression() { + QFETCH(bool, redundantRefs); + m_debuggerAgent->collector.setRedundantRefs(redundantRefs); + QString script = "function testFunction() {\n" " var x = 10\n" @@ -750,14 +762,12 @@ void tst_qv4debugger::evaluateExpression() evaluateJavaScript(script, "evaluateExpression"); - QCOMPARE(m_debuggerAgent->m_expressionRefs.count(), 4); - QCOMPARE(m_debuggerAgent->m_expressionRefs[0].size(), 1); - QJsonObject result0 = m_debuggerAgent->m_expressionRefs[0].first().toObject(); + QCOMPARE(m_debuggerAgent->m_expressionResults.count(), 4); + QJsonObject result0 = m_debuggerAgent->m_expressionResults[0]; QCOMPARE(result0.value("type").toString(), QStringLiteral("number")); QCOMPARE(result0.value("value").toInt(), 10); for (int i = 1; i < 4; ++i) { - QCOMPARE(m_debuggerAgent->m_expressionRefs[i].size(), 1); - QJsonObject result1 = m_debuggerAgent->m_expressionRefs[1].first().toObject(); + QJsonObject result1 = m_debuggerAgent->m_expressionResults[i]; QCOMPARE(result1.value("type").toString(), QStringLiteral("number")); QCOMPARE(result1.value("value").toInt(), 20); } @@ -765,6 +775,9 @@ void tst_qv4debugger::evaluateExpression() void tst_qv4debugger::stepToEndOfScript() { + QFETCH(bool, redundantRefs); + m_debuggerAgent->collector.setRedundantRefs(redundantRefs); + QString script = "var ret = 0;\n" "ret += 4;\n" @@ -827,6 +840,13 @@ void tst_qv4debugger::lastLineOfLoop() QCOMPARE(secondState.lineNumber, 7); } +void tst_qv4debugger::redundancy_data() +{ + QTest::addColumn<bool>("redundantRefs"); + QTest::addRow("redundant") << true; + QTest::addRow("sparse") << false; +} + QTEST_MAIN(tst_qv4debugger) #include "tst_qv4debugger.moc" |