setInternalClass: Correctly handle deleted properties

When an IC was rebuilt, we must not set members that were deleted,
otherwise we'll trigger an assertion. Since the nameMap has to match the
data we still allocate memory for such members and fill it with
undefined. This could be avoided with some deeper refactoring, but a
simple solution is to be preferred because this needs to be picked back
all the way to 6.2.

Pick-to: 6.5 6.2
Fixes: QTBUG-111729
Change-Id: I730d6b4634d989191434225600a08cf0208e72f8
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
(cherry picked from commit 642d531e42)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
(cherry picked from commit 308f785c78)
This commit is contained in:
Fabian Kosmale 2023-03-06 09:24:39 +01:00 committed by Ulf Hermann
parent 1b0a03639c
commit f17168ef87
2 changed files with 27 additions and 2 deletions

View File

@ -41,8 +41,11 @@ void Object::setInternalClass(Heap::InternalClass *ic)
// Pick the members of the old IC that are still valid in the new IC.
// Order them by index in memberData (or inline data).
Scoped<MemberData> newMembers(scope, MemberData::allocate(scope.engine, ic->size));
for (uint i = 0; i < ic->size; ++i)
newMembers->set(scope.engine, i, get(ic->nameMap.at(i)));
for (uint i = 0; i < ic->size; ++i) {
// Note that some members might have been deleted. The key may be invalid.
const PropertyKey key = ic->nameMap.at(i);
newMembers->set(scope.engine, i, key.isValid() ? get(key) : Encode::undefined());
}
p->internalClass.set(scope.engine, ic);
const uint nInline = p->vtable()->nInlineProperties;

View File

@ -310,6 +310,9 @@ private slots:
void symbolToVariant();
void garbageCollectedObjectMethodBase();
void deleteDefineCycle();
public:
Q_INVOKABLE QJSValue throwingCppMethod1();
Q_INVOKABLE void throwingCppMethod2();
@ -6302,6 +6305,25 @@ void tst_QJSEngine::garbageCollectedObjectMethodBase()
}
}
void tst_QJSEngine::deleteDefineCycle()
{
QJSEngine engine;
QStringList stackTrace;
QJSValue result = engine.evaluate(QString::fromLatin1(R"(
let global = ({})
for (let j = 0; j < 1000; j++) {
for (let i = 0; i < 2; i++) {
const name = "test" + i
delete global[name]
Object.defineProperty(global, name, { get() { return 0 }, configurable: true })
}
}
)"), {}, 1, &stackTrace);
QVERIFY(stackTrace.isEmpty());
}
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"