Use QQmlType for looking up enums, even on singletons

QQmlType has more information than the bare QMetaType. The optimizer
already uses it for looking up enums, so some code would behave
differently, depending on whether the optimizer was enabled or not.

In some cases we cannot use QQmlType for lookup of enums because
QQmlType might have been created with only a callback. The object
only shows up later in that case. Then the only thing we can do is
query the metatype.

We can test this by adding an eval() because eval() disables
optimization for the surrounding code.

Task-number: QTBUG-58394
Change-Id: I8c90591b19fe1ed3e5339d877f9e6ec7c6f9aa73
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
Ulf Hermann 2017-02-08 16:53:58 +01:00
parent ade0ed7fa9
commit fedcd26ce8
3 changed files with 55 additions and 5 deletions

View File

@ -129,15 +129,20 @@ ReturnedValue QmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, Q
return w.asReturnedValue();
}
static int enumForSingleton(String *name, QObject *qobjectSingleton)
static int enumForSingleton(QV4::ExecutionEngine *v4, String *name, QObject *qobjectSingleton,
QQmlType *type)
{
bool ok;
int value = type->enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok);
if (ok)
return value;
// ### Optimize
QByteArray enumName = name->toQString().toUtf8();
const QMetaObject *metaObject = qobjectSingleton->metaObject();
for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) {
QMetaEnum e = metaObject->enumerator(ii);
bool ok;
int value = e.keyToValue(enumName.constData(), &ok);
value = e.keyToValue(enumName.constData(), &ok);
if (ok)
return value;
}
@ -183,7 +188,7 @@ ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasPrope
// check for enum value
const bool includeEnums = w->d()->mode == Heap::QmlTypeWrapper::IncludeEnums;
if (includeEnums && name->startsWithUpper()) {
const int value = enumForSingleton(name, qobjectSingleton);
const int value = enumForSingleton(v4, name, qobjectSingleton, type);
if (value != -1)
return QV4::Primitive::fromInt32(value).asReturnedValue();
}
@ -196,7 +201,7 @@ ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasPrope
// Warn when attempting to access a lowercased enum value, singleton case
if (!ok && includeEnums && !name->startsWithUpper()) {
const int value = enumForSingleton(name, qobjectSingleton);
const int value = enumForSingleton(v4, name, qobjectSingleton, type);
if (value != -1)
return throwLowercaseEnumError(v4, name, type);
}

View File

@ -0,0 +1,12 @@
import QtQml 2.2
import x.y.z 1.0
QtObject {
function x() {
eval("1");
return ExternalEnums.DocumentsLocation;
}
property var a: ExternalEnums.DocumentsLocation
property var b: x()
}

View File

@ -26,6 +26,7 @@
**
****************************************************************************/
#include <qstandardpaths.h>
#include <qtest.h>
#include <qqml.h>
#include <qqmlprivate.h>
@ -53,6 +54,7 @@ private slots:
void invalidQmlTypeName();
void registrationType();
void compositeType();
void externalEnums();
void isList();
@ -70,6 +72,20 @@ public:
};
QML_DECLARE_TYPE(TestType);
class ExternalEnums : public QObject
{
Q_OBJECT
Q_ENUMS(QStandardPaths::StandardLocation QStandardPaths::LocateOptions)
public:
ExternalEnums(QObject *parent = nullptr) : QObject(parent) {}
static QObject *create(QQmlEngine *engine, QJSEngine *scriptEngine) {
Q_UNUSED(scriptEngine);
return new ExternalEnums(engine);
}
};
QML_DECLARE_TYPE(ExternalEnums);
QObject *testTypeProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine);
@ -271,6 +287,23 @@ void tst_qqmlmetatype::compositeType()
QCOMPARE(type->sourceUrl(), testFileUrl("ImplicitType.qml"));
}
void tst_qqmlmetatype::externalEnums()
{
QQmlEngine engine;
qmlRegisterSingletonType<ExternalEnums>("x.y.z", 1, 0, "ExternalEnums", ExternalEnums::create);
QQmlComponent c(&engine, testFileUrl("testExternalEnums.qml"));
QObject *obj = c.create();
QVERIFY(obj);
QVariant a = obj->property("a");
QCOMPARE(a.type(), QVariant::Int);
QCOMPARE(a.toInt(), int(QStandardPaths::DocumentsLocation));
QVariant b = obj->property("b");
QCOMPARE(b.type(), QVariant::Int);
QCOMPARE(b.toInt(), int(QStandardPaths::DocumentsLocation));
}
QTEST_MAIN(tst_qqmlmetatype)
#include "tst_qqmlmetatype.moc"