From 5a7aa7881fa2c7abffb3d34a6b642fe4efcadbf4 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Thu, 17 Dec 2020 11:22:34 +0100 Subject: [PATCH 21/28] QML: Fix proxy iteration If the target of a proxy was extensible, we did not set the iteratorTarget to its correct value, and thus the ForInIteratorObject would not be usable. Fixes: QTBUG-86323 Change-Id: Id1924ac4087bab38c006b8eba92b619b79d36b7a Reviewed-by: Ulf Hermann (cherry picked from commit dd740d6b3469448dc1fd31c1742781e923e9f274) --- src/qml/jsruntime/qv4proxy.cpp | 8 +++-- .../qqmlecmascript/data/proxyIteration.qml | 29 +++++++++++++++++++ .../qml/qqmlecmascript/tst_qqmlecmascript.cpp | 10 +++++++ 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/proxyIteration.qml diff --git a/src/qml/jsruntime/qv4proxy.cpp b/src/qml/jsruntime/qv4proxy.cpp index 1505eae426..8bfc9fc3ba 100644 --- a/src/qml/jsruntime/qv4proxy.cpp +++ b/src/qml/jsruntime/qv4proxy.cpp @@ -624,8 +624,10 @@ OwnPropertyKeyIterator *ProxyObject::virtualOwnPropertyKeys(const Object *m, Val else targetNonConfigurableKeys->push_back(keyAsValue); } - if (target->isExtensible() && targetNonConfigurableKeys->getLength() == 0) + if (target->isExtensible() && targetNonConfigurableKeys->getLength() == 0) { + *iteratorTarget = *m; return new ProxyObjectOwnPropertyKeyIterator(trapKeys); + } ScopedArrayObject uncheckedResultKeys(scope, scope.engine->newArrayObject()); uncheckedResultKeys->copyArrayData(trapKeys); @@ -639,8 +641,10 @@ OwnPropertyKeyIterator *ProxyObject::virtualOwnPropertyKeys(const Object *m, Val } } - if (target->isExtensible()) + if (target->isExtensible()) { + *iteratorTarget = *m; return new ProxyObjectOwnPropertyKeyIterator(trapKeys); + } len = targetConfigurableKeys->getLength(); for (uint i = 0; i < len; ++i) { diff --git a/tests/auto/qml/qqmlecmascript/data/proxyIteration.qml b/tests/auto/qml/qqmlecmascript/data/proxyIteration.qml new file mode 100644 index 0000000000..affba7d9f1 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/proxyIteration.qml @@ -0,0 +1,29 @@ +import QtQml 2 + +QtObject { + id: root + property int sum + Component.onCompleted: { + const target = { prop1: 1, prop2: 2, prop3: 3 }; + const handler = { + get: function(target, key) { + return target[key]+1; + }, + ownKeys: function() { + return ["prop1", "prop3"]; + }, + getOwnPropertyDescriptor: function(target, key) { + return { + value: this.get(target, key), + enumerable: true, + configurable: true + }; + } + }; + const proxy = new Proxy(target, handler); + for (var prop in proxy) { + root.sum += proxy[prop] // prop2 gets skipped, the values of 1 and 3 get incremented + } + // so root.sum should be 6 now + } +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 3a9d1bfb4c..9198d3bebf 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -382,6 +382,7 @@ private slots: void semicolonAfterProperty(); void hugeStack(); void variantConversionMethod(); + void proxyIteration(); void proxyHandlerTraps(); void gcCrashRegressionTest(); @@ -9306,6 +9307,15 @@ void tst_qqmlecmascript::variantConversionMethod() QCOMPARE(obj.funcCalled, QLatin1String("QModelIndex")); } +void tst_qqmlecmascript::proxyIteration() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("proxyIteration.qml")); + QScopedPointer root(component.create()); + QVERIFY2(root != nullptr, qPrintable(component.errorString())); + QCOMPARE(root->property("sum").toInt(), 6); +} + void tst_qqmlecmascript::proxyHandlerTraps() { const QString expression = QStringLiteral(R"SNIPPET( -- 2.31.1