QmlCompiler: Use actual type of enums, rather than int

Now that the type is available from qmltypes we can just use it.

Task-number: QTBUG-112180
Change-Id: I315372da0925f19c209f676226f450863b0d3ea5
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Ulf Hermann 2023-03-27 18:01:07 +02:00
parent 9088125f14
commit f10630aa03
11 changed files with 124 additions and 37 deletions

View File

@ -145,6 +145,7 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
Heap::InternalClass *ic;
quintptr unused;
ReturnedValue encodedEnumValue;
const QtPrivate::QMetaTypeInterface *metaType;
} qmlEnumValueLookup;
struct {
Heap::InternalClass *ic;

View File

@ -1020,9 +1020,41 @@ static bool isTypeCompatible(QMetaType lookupType, QMetaType propertyType)
if (!foundMetaObject)
return false;
} else if (lookupType == QMetaType::fromType<int>()
&& propertyType.flags() & QMetaType::IsEnumeration) {
return true;
} else if (propertyType.flags() & QMetaType::IsEnumeration) {
if (propertyType == lookupType)
return true;
// You can pass the underlying type of an enum.
// We don't want to check for the actual underlying type because
// moc and qmltyperegistrar are not very precise about it. Especially
// the long and longlong types can be ambiguous.
const bool isUnsigned = propertyType.flags() & QMetaType::IsUnsignedEnumeration;
switch (propertyType.sizeOf()) {
case 1:
return isUnsigned
? lookupType == QMetaType::fromType<quint8>()
: lookupType == QMetaType::fromType<qint8>();
case 2:
return isUnsigned
? lookupType == QMetaType::fromType<ushort>()
: lookupType == QMetaType::fromType<short>();
case 4:
// The default type, if moc doesn't know the actual enum type, is int.
// However, the compiler can still decide to encode the enum in uint.
// Therefore, we also accept int for uint enums.
// TODO: This is technically UB.
return isUnsigned
? (lookupType == QMetaType::fromType<int>()
|| lookupType == QMetaType::fromType<uint>())
: lookupType == QMetaType::fromType<int>();
case 8:
return isUnsigned
? lookupType == QMetaType::fromType<qulonglong>()
: lookupType == QMetaType::fromType<qlonglong>();
}
return false;
} else if (propertyType != lookupType) {
return false;
}
@ -1776,13 +1808,44 @@ void AOTCompiledContext::initGetValueLookup(
engine->handle()->throwTypeError();
}
bool AOTCompiledContext::getEnumLookup(uint index, int *target) const
bool AOTCompiledContext::getEnumLookup(uint index, void *target) const
{
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
if (l->getter != QV4::QQmlTypeWrapper::lookupEnumValue)
return false;
*target = l->qmlEnumValueLookup.encodedEnumValue;
return true;
const bool isUnsigned
= l->qmlEnumValueLookup.metaType->flags & QMetaType::IsUnsignedEnumeration;
const QV4::ReturnedValue encoded = l->qmlEnumValueLookup.encodedEnumValue;
switch (l->qmlEnumValueLookup.metaType->size) {
case 1:
if (isUnsigned)
*static_cast<quint8 *>(target) = encoded;
else
*static_cast<qint8 *>(target) = encoded;
return true;
case 2:
if (isUnsigned)
*static_cast<quint16 *>(target) = encoded;
else
*static_cast<qint16 *>(target) = encoded;
return true;
case 4:
if (isUnsigned)
*static_cast<quint32 *>(target) = encoded;
else
*static_cast<qint32 *>(target) = encoded;
return true;
case 8:
if (isUnsigned)
*static_cast<quint64 *>(target) = encoded;
else
*static_cast<qint64 *>(target) = encoded;
return true;
default:
break;
}
return false;
}
void AOTCompiledContext::initGetEnumLookup(
@ -1798,8 +1861,9 @@ void AOTCompiledContext::initGetEnumLookup(
return;
}
const int enumIndex = metaObject->indexOfEnumerator(enumerator);
const int value = metaObject->enumerator(enumIndex).keyToValue(enumValue);
l->qmlEnumValueLookup.encodedEnumValue = value;
const QMetaEnum metaEnum = metaObject->enumerator(enumIndex);
l->qmlEnumValueLookup.encodedEnumValue = metaEnum.keyToValue(enumValue);
l->qmlEnumValueLookup.metaType = metaEnum.metaType().iface();
l->getter = QV4::QQmlTypeWrapper::lookupEnumValue;
}

View File

@ -703,7 +703,7 @@ namespace QQmlPrivate
bool getValueLookup(uint index, void *value, void *target) const;
void initGetValueLookup(uint index, const QMetaObject *metaObject, QMetaType type) const;
bool getEnumLookup(uint index, int *target) const;
bool getEnumLookup(uint index, void *target) const;
void initGetEnumLookup(uint index, const QMetaObject *metaObject,
const char *enumerator, const char *enumValue) const;

View File

@ -478,11 +478,10 @@ void QQmlJSImporter::processImport(const QQmlJSScope::Import &importDescription,
// only happen when enumerations are involved, thus the strategy is to
// resolve enumerations (which can potentially create new child scopes)
// before resolving the type fully
const QQmlJSScope::ConstPtr intType = tempTypes.cppNames.type(u"int"_s).scope;
const QQmlJSScope::ConstPtr arrayType = tempTypes.cppNames.type(u"Array"_s).scope;
for (auto it = import.objects.begin(); it != import.objects.end(); ++it) {
if (!it->scope.factory()) {
QQmlJSScope::resolveEnums(it->scope, intType);
QQmlJSScope::resolveEnums(it->scope, tempTypes.cppNames);
QQmlJSScope::resolveList(it->scope, arrayType);
}
}
@ -522,7 +521,7 @@ QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper()
if (m_builtins)
return *m_builtins;
AvailableTypes builtins(ImportedTypes(ImportedTypes::INTERNAL, {}, {}, {}));
AvailableTypes builtins(ImportedTypes(ImportedTypes::INTERNAL, {}, {}));
Import result;
result.name = QStringLiteral("QML");
@ -579,12 +578,10 @@ QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper()
Q_ASSERT(intType);
Q_ASSERT(arrayType);
m_builtins = AvailableTypes(ImportedTypes(
ImportedTypes::INTERNAL, builtins.cppNames.types(),
intType, arrayType));
m_builtins->qmlNames =
ImportedTypes(ImportedTypes::QML, builtins.qmlNames.types(),
intType, arrayType);
m_builtins = AvailableTypes(
ImportedTypes(ImportedTypes::INTERNAL, builtins.cppNames.types(), arrayType));
m_builtins->qmlNames
= ImportedTypes(ImportedTypes::QML, builtins.qmlNames.types(), arrayType);
processImport(builtinImport, result, &(*m_builtins));
@ -703,9 +700,7 @@ bool QQmlJSImporter::importHelper(const QString &module, AvailableTypes *types,
auto cacheTypes = QSharedPointer<QQmlJSImporter::AvailableTypes>(
new QQmlJSImporter::AvailableTypes(
ImportedTypes(
ImportedTypes::INTERNAL, {},
types->cppNames.intType(), types->cppNames.arrayType())));
ImportedTypes(ImportedTypes::INTERNAL, {}, types->cppNames.arrayType())));
m_cachedImportTypes[cacheKey] = cacheTypes;
const QPair<QString, QTypeRevision> importId { module, version };
@ -822,8 +817,7 @@ QQmlJSImporter::ImportedTypes QQmlJSImporter::importDirectory(
const AvailableTypes builtins = builtinImportHelper();
QQmlJSImporter::AvailableTypes types(
ImportedTypes(
ImportedTypes::INTERNAL, {},
builtins.cppNames.intType(), builtins.cppNames.arrayType()));
ImportedTypes::INTERNAL, {}, builtins.cppNames.arrayType()));
importHelper(directory, &types, prefix, QTypeRevision(), false, true);
return types.qmlNames;
}

View File

@ -92,8 +92,7 @@ private:
{
AvailableTypes(ImportedTypes builtins)
: cppNames(std::move(builtins))
, qmlNames(QQmlJSScope::ContextualTypes::QML, {},
cppNames.intType(), cppNames.arrayType())
, qmlNames(QQmlJSScope::ContextualTypes::QML, {}, cppNames.arrayType())
{
}

View File

@ -77,7 +77,6 @@ QQmlJSImportVisitor::QQmlJSImportVisitor(
m_logger(logger),
m_rootScopeImports(
QQmlJSImporter::ImportedTypes::QML, {},
importer->builtinInternalNames().intType(),
importer->builtinInternalNames().arrayType())
{
m_currentScope->setScopeType(QQmlJSScope::JSFunctionScope);

View File

@ -576,7 +576,7 @@ QTypeRevision QQmlJSScope::resolveTypes(
const auto resolveAll = [](const QQmlJSScope::Ptr &self,
const QQmlJSScope::ContextualTypes &contextualTypes,
QSet<QString> *usedTypes) {
resolveEnums(self, contextualTypes.intType());
resolveEnums(self, contextualTypes, usedTypes);
resolveList(self, contextualTypes.arrayType());
return resolveType(self, contextualTypes, usedTypes);
};
@ -601,7 +601,9 @@ void QQmlJSScope::resolveNonEnumTypes(
resolveEnums() will create a QQmlJSMetaEnum copy for the alias in case the 'self'-scope already
does not have an enum called like the alias.
*/
void QQmlJSScope::resolveEnums(const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &intType)
void QQmlJSScope::resolveEnums(
const QQmlJSScope::Ptr &self, const QQmlJSScope::ContextualTypes &contextualTypes,
QSet<QString> *usedTypes)
{
// temporary hash to avoid messing up m_enumerations while iterators are active on it
QHash<QString, QQmlJSMetaEnum> toBeAppended;
@ -609,12 +611,17 @@ void QQmlJSScope::resolveEnums(const QQmlJSScope::Ptr &self, const QQmlJSScope::
++it) {
if (it->type())
continue;
Q_ASSERT(intType); // We need an "int" type to resolve enums
QQmlJSScope::Ptr enumScope = QQmlJSScope::create();
reparent(self, enumScope);
enumScope->m_scopeType = EnumScope;
enumScope->setBaseTypeName(QStringLiteral("int"));
enumScope->m_baseType.scope = intType;
QString typeName = it->typeName();
if (typeName.isEmpty())
typeName = QStringLiteral("int");
enumScope->setBaseTypeName(typeName);
const auto type = findType(typeName, contextualTypes, usedTypes);
enumScope->m_baseType = { type.scope, type.revision };
enumScope->m_semantics = AccessSemantics::Value;
enumScope->m_internalName = self->internalName() + QStringLiteral("::") + it->name();
if (QString alias = it->alias(); !alias.isEmpty()
@ -658,7 +665,7 @@ void QQmlJSScope::resolveList(const QQmlJSScope::Ptr &self, const QQmlJSScope::C
const QQmlJSImportedScope array = {arrayType, QTypeRevision()};
QQmlJSScope::ContextualTypes contextualTypes(
QQmlJSScope::ContextualTypes::INTERNAL, { { self->internalName(), element }, },
QQmlJSScope::ConstPtr(), arrayType);
arrayType);
QQmlJSScope::resolveTypes(listType, contextualTypes);
Q_ASSERT(listType->valueType() == self);
@ -1097,7 +1104,7 @@ void QDeferredFactory<QQmlJSScope>::populate(const QSharedPointer<QQmlJSScope> &
typeReader(scope);
m_importer->m_globalWarnings.append(typeReader.errors());
scope->setInternalName(internalName());
QQmlJSScope::resolveEnums(scope, m_importer->builtinInternalNames().intType());
QQmlJSScope::resolveEnums(scope, m_importer->builtinInternalNames());
QQmlJSScope::resolveList(scope, m_importer->builtinInternalNames().arrayType());
if (m_isSingleton && !scope->isSingleton()) {

View File

@ -216,16 +216,13 @@ public:
ContextualTypes(
CompileContext context,
const QHash<QString, ImportedScope<ConstPtr>> types,
const QQmlJSScope::ConstPtr &intType,
const QQmlJSScope::ConstPtr &arrayType)
: m_types(types)
, m_context(context)
, m_intType(intType)
, m_arrayType(arrayType)
{}
CompileContext context() const { return m_context; }
ConstPtr intType() const { return m_intType; }
ConstPtr arrayType() const { return m_arrayType; }
bool hasType(const QString &name) const { return m_types.contains(name); }
@ -606,7 +603,8 @@ QT_WARNING_POP
const QQmlJSScope::Ptr &self, const QQmlJSScope::ContextualTypes &contextualTypes,
QSet<QString> *usedTypes = nullptr);
static void resolveEnums(
const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &intType);
const QQmlJSScope::Ptr &self, const QQmlJSScope::ContextualTypes &contextualTypes,
QSet<QString> *usedTypes = nullptr);
static void resolveList(
const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &arrayType);
static void resolveGeneralizedGroup(

View File

@ -11,4 +11,9 @@ QtObject {
readonly property FooThing fighter: root.f.get(Foo.Fighter)
readonly property FooThing bar: root.f.get(Foo.Component)
}
property int a: FooFactory.B
property int b: f.t8
property int c: FooFactory.D
property int d: f.t16
}

View File

@ -45,8 +45,23 @@ class FooThingWrapper {
class FooFactory : public QObject {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(T8 t8 READ t8 CONSTANT FINAL)
Q_PROPERTY(T16 t16 READ t16 CONSTANT FINAL)
public:
enum T8: qint8 {
A, B, C
};
Q_ENUM(T8)
enum T16: qint16 {
D = 500, E, F
};
Q_ENUM(T16)
T8 t8() const { return C; }
T16 t16() const { return E; }
Q_INVOKABLE Foo* get(Foo::Type type) const { return new Foo(type); }
};

View File

@ -2981,6 +2981,11 @@ void tst_QmlCppCodegen::enumProblems()
Foo *fighter = inner->property("fighter").value<Foo *>();
QVERIFY(fighter);
QCOMPARE(fighter->type(), Foo::Fighter);
QCOMPARE(outer->property("a").toInt(), FooFactory::B);
QCOMPARE(outer->property("b").toInt(), FooFactory::C);
QCOMPARE(outer->property("c").toInt(), FooFactory::D);
QCOMPARE(outer->property("d").toInt(), FooFactory::E);
}
void tst_QmlCppCodegen::enumConversion()