V4 Debugger: Collect locals also from block scopes

Block scopes can contain "const" and "let" members.

Fixes: QTBUG-92224
Pick-to: 5.15 6.1
Change-Id: Ie13d7d573e2759c510e1ea48c6edc68a095f40a0
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2021-05-03 19:23:49 +02:00
parent a0d74b122a
commit 051dd3178b
4 changed files with 85 additions and 3 deletions

View File

@ -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;

View File

@ -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()
}

View File

@ -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,61 @@ 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();
QString name = propObj.value(QStringLiteral("name")).toString();
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<QQmlDebugClient *> tst_QQmlDebugJS::createClients()
{
m_client = new QV4DebugClient(m_connection);

View File

@ -580,21 +580,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()