From 8e400a48285e58c938fe24367251cc04c1d00985 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 3 May 2021 19:23:49 +0200 Subject: V4 Debugger: Collect locals also from block scopes Block scopes can contain "const" and "let" members. Fixes: QTBUG-92224 Change-Id: Ie13d7d573e2759c510e1ea48c6edc68a095f40a0 Reviewed-by: Fabian Kosmale (cherry picked from commit 051dd3178bc4c9214af60c69cecfc2c28f13174d) --- .../qmldbg_debugger/qv4datacollector.cpp | 3 +- .../debugger/qqmldebugjs/data/letConstLocals.qml | 16 ++++++ .../qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp | 59 ++++++++++++++++++++++ .../qml/debugger/qv4debugger/tst_qv4debugger.cpp | 12 ++++- 4 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 tests/auto/qml/debugger/qqmldebugjs/data/letConstLocals.qml diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp index d3134e0727..8758e3f004 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -216,7 +216,8 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr) return false; QV4::ScopedObject scopeObject(scope, engine()->newObject()); - if (ctxt->d()->type == QV4::Heap::ExecutionContext::Type_CallContext) { + if (ctxt->d()->type == QV4::Heap::ExecutionContext::Type_CallContext || + ctxt->d()->type == QV4::Heap::ExecutionContext::Type_BlockContext) { QStringList names; Refs collectedRefs; diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/letConstLocals.qml b/tests/auto/qml/debugger/qqmldebugjs/data/letConstLocals.qml new file mode 100644 index 0000000000..1715992490 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/letConstLocals.qml @@ -0,0 +1,16 @@ +import QtQml 2.15 + +Timer { + Component.onCompleted: { + var a = 97 + var b = 98 + var c = 99 + let d = 100 + const e = 101 + console.log("onClicked") // Set breakpoint + running = true + } + + interval: 0 + onTriggered: Qt.quit() +} diff --git a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp index a07567aa7d..66c5f0dbf1 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp +++ b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp @@ -66,6 +66,7 @@ const char *BREAKPOINTRELOCATION_QMLFILE = "breakpointRelocation.qml"; const char *ENCODEQMLSCOPE_QMLFILE = "encodeQmlScope.qml"; const char *BREAKONANCHOR_QMLFILE = "breakOnAnchor.qml"; const char *BREAKPOINTIDS_QMLFILE = "breakPointIds.qml"; +const char *LETCONSTLOCALS_QMLFILE = "letConstLocals.qml"; #undef QVERIFY #define QVERIFY(statement) \ @@ -158,6 +159,7 @@ private slots: void breakOnAnchor(); void breakPointIds(); + void letConstLocals(); private: ConnectResult init(bool qmlscene, const QString &qmlFile = QString(TEST_QMLFILE), @@ -1050,6 +1052,63 @@ void tst_QQmlDebugJS::breakPointIds() QCOMPARE(breaks, 6); } +void tst_QQmlDebugJS::letConstLocals() +{ + QString file(LETCONSTLOCALS_QMLFILE); + QCOMPARE(init(true, file), ConnectSuccess); + + QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { + m_client->frame(); + }); + + int numScopes = 0; + QString expectedMembers = QStringLiteral("abcde"); + QObject::connect(m_client.data(), &QV4DebugClient::result, this, [&]() { + const auto value = m_client->response(); + if (value.command == QStringLiteral("frame")) { + const auto scopes = value.body.toObject().value(QStringLiteral("scopes")).toArray(); + for (const auto &scope : scopes) { + const auto scopeObject = scope.toObject(); + const int type = scopeObject.value("type").toInt(); + if (type == 1 || type == 4) { + m_client->scope(scopeObject.value("index").toInt()); + ++numScopes; + } + } + QVERIFY(numScopes > 0); + } else if (value.command == QStringLiteral("scope")) { + const auto props = value.body.toObject().value(QStringLiteral("object")).toObject() + .value(QStringLiteral("properties")).toArray(); + for (const auto &prop : props) { + const auto propObj = prop.toObject(); + const QString name = propObj.value(QStringLiteral("name")).toString(); + if (name == QStringLiteral("onCompleted")) + continue; + QVERIFY(name.length() == 1); + auto i = expectedMembers.indexOf(name.at(0)); + QVERIFY(i != -1); + expectedMembers.remove(i, 1); + QCOMPARE(propObj.value(QStringLiteral("type")).toString(), + QStringLiteral("number")); + QCOMPARE(propObj.value(QStringLiteral("value")).toInt(), + int(name.at(0).toLatin1())); + } + if (--numScopes == 0) { + QVERIFY(expectedMembers.isEmpty()); + m_client->continueDebugging(QV4DebugClient::Continue); + } + } + }); + + setBreakPoint(file, 10, true); + + QTRY_COMPARE(m_process->state(), QProcess::Running); + m_client->connect(); + + QTRY_COMPARE(m_process->state(), QProcess::NotRunning); + QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); +} + QList tst_QQmlDebugJS::createClients() { m_client = new QV4DebugClient(m_connection); diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index 8419b6a31c..33e644d6f2 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -575,21 +575,29 @@ void tst_qv4debugger::readLocals() QString script = "var f = function(a, b) {\n" " var c = a + b\n" + " let e = 'jaja'\n" + " const ff = 'nenene'\n" " var d = a - b\n" // breakpoint, c should be set, d should be undefined " return c === d\n" "}\n" "f(1, 2, 3);\n"; - debugger()->addBreakPoint("readLocals", 3); + debugger()->addBreakPoint("readLocals", 5); evaluateJavaScript(script, "readLocals"); QVERIFY(m_debuggerAgent->m_wasPaused); QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1); const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0); - QCOMPARE(frame0.size(), 5); // locals and parameters + QCOMPARE(frame0.size(), 7); // locals and parameters 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")); + QVERIFY(frame0.contains("e")); + QCOMPARE(frame0.type("e"), QStringLiteral("string")); + QCOMPARE(frame0.value("e").toString(), QStringLiteral("jaja")); + QVERIFY(frame0.contains("ff")); + QCOMPARE(frame0.type("ff"), QStringLiteral("string")); + QCOMPARE(frame0.value("ff").toString(), QStringLiteral("nenene")); } void tst_qv4debugger::readObject() -- cgit v1.2.3