From 41248f259005aa3052d569def9f873afee4ac8f6 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 31 Jan 2023 13:53:44 +0100 Subject: [PATCH] moc: Allow calling ctors on pre-allocated data When calling a ctor this way, an object is created in the memory pointed to by _a[0] using the other arguments for the ctor. This allows separate allocation and initialization of an object through the metaobject system. Fixes: QTBUG-108879 Change-Id: Ifb154373ee42faab281cfb62aa14334980ec6b7d Reviewed-by: Fabian Kosmale --- src/corelib/kernel/qmetaobject.cpp | 1 + src/corelib/kernel/qobjectdefs.h | 3 +- src/tools/moc/generator.cpp | 44 +++++++++++++++++++----------- tests/auto/tools/moc/tst_moc.cpp | 37 +++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 17 deletions(-) diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 4475d6be8d..73c62da896 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -107,6 +107,7 @@ using namespace Qt::StringLiterals; \value RegisterMethodArgumentMetaType \value BindableProperty \value CustomCall + \value ConstructInPlace */ /*! diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 673d588e32..ed5561b923 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -521,7 +521,8 @@ struct Q_CORE_EXPORT QMetaObject RegisterPropertyMetaType, RegisterMethodArgumentMetaType, BindableProperty, - CustomCall + CustomCall, + ConstructInPlace, }; int static_metacall(Call, int, void **) const; diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index 1214600a7e..ca6a4181b6 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -1073,33 +1073,45 @@ void Generator::generateStaticMetacall() bool needElse = false; bool isUsed_a = false; + const auto generateCtorArguments = [&](int ctorindex) { + const FunctionDef &f = cdef->constructorList.at(ctorindex); + Q_ASSERT(!f.isPrivateSignal); // That would be a strange ctor indeed + int offset = 1; + + int argsCount = f.arguments.size(); + for (int j = 0; j < argsCount; ++j) { + const ArgumentDef &a = f.arguments.at(j); + if (j) + fprintf(out, ","); + fprintf(out, "(*reinterpret_cast<%s>(_a[%d]))", + a.typeNameForCast.constData(), offset++); + } + }; + if (!cdef->constructorList.isEmpty()) { fprintf(out, " if (_c == QMetaObject::CreateInstance) {\n"); fprintf(out, " switch (_id) {\n"); - for (int ctorindex = 0; ctorindex < cdef->constructorList.size(); ++ctorindex) { + const int ctorend = cdef->constructorList.size(); + for (int ctorindex = 0; ctorindex < ctorend; ++ctorindex) { fprintf(out, " case %d: { %s *_r = new %s(", ctorindex, cdef->classname.constData(), cdef->classname.constData()); - const FunctionDef &f = cdef->constructorList.at(ctorindex); - int offset = 1; - - int argsCount = f.arguments.size(); - for (int j = 0; j < argsCount; ++j) { - const ArgumentDef &a = f.arguments.at(j); - if (j) - fprintf(out, ","); - fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))", a.typeNameForCast.constData(), offset++); - } - if (f.isPrivateSignal) { - if (argsCount > 0) - fprintf(out, ", "); - fprintf(out, "%s", QByteArray("QPrivateSignal()").constData()); - } + generateCtorArguments(ctorindex); fprintf(out, ");\n"); fprintf(out, " if (_a[0]) *reinterpret_cast<%s**>(_a[0]) = _r; } break;\n", (cdef->hasQGadget || cdef->hasQNamespace) ? "void" : "QObject"); } fprintf(out, " default: break;\n"); fprintf(out, " }\n"); + fprintf(out, " } else if (_c == QMetaObject::ConstructInPlace) {\n"); + fprintf(out, " switch (_id) {\n"); + for (int ctorindex = 0; ctorindex < ctorend; ++ctorindex) { + fprintf(out, " case %d: { new (_a[0]) %s(", + ctorindex, cdef->classname.constData()); + generateCtorArguments(ctorindex); + fprintf(out, "); } break;\n"); + } + fprintf(out, " default: break;\n"); + fprintf(out, " }\n"); fprintf(out, " }"); needElse = true; isUsed_a = true; diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index 35c31b6b87..abe3b0abbb 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -758,6 +758,7 @@ private slots: void setQPRopertyBinding(); void privateQPropertyShim(); void readWriteThroughBindable(); + void invokableCtors(); signals: void sigWithUnsignedArg(unsigned foo); @@ -4424,6 +4425,42 @@ void tst_Moc::readWriteThroughBindable() } } +struct WithInvokableCtor +{ + Q_GADGET + Q_PROPERTY(int thing MEMBER m_thing CONSTANT FINAL) +public: + WithInvokableCtor() = default; + Q_INVOKABLE WithInvokableCtor(int theThing) : m_thing(theThing) {} + + int m_thing = 10; +}; + +void tst_Moc::invokableCtors() +{ + const QMetaType metaType = QMetaType::fromType(); + Q_ASSERT(metaType.sizeOf() > 0); + const QMetaObject *metaObject = metaType.metaObject(); + QVERIFY(metaObject); + + QCOMPARE(metaObject->constructorCount(), 1); + WithInvokableCtor *result = nullptr; + const auto guard = qScopeGuard([&]() { delete result; }); + int argument = 17; + void *a[] = { &result, &argument }; + metaObject->static_metacall(QMetaObject::CreateInstance, 0, a); + QVERIFY(result); + QCOMPARE(result->m_thing, 17); + + // Call dtor so that we're left with "uninitialized" memory. + WithInvokableCtor result2; + result2.~WithInvokableCtor(); + + void *b[] = { &result2, &argument }; + metaObject->static_metacall(QMetaObject::ConstructInPlace, 0, b); + QCOMPARE(result2.m_thing, 17); +} + QTEST_MAIN(tst_Moc) // the generated code must compile with QT_NO_KEYWORDS