aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brasser <mbrasser@ford.com>2017-04-13 17:10:45 -0500
committerMichael Brasser <michael.brasser@live.com>2017-05-03 11:38:21 +0000
commitaae18aa05719c4b750905f467f42002f4cea1516 (patch)
tree167e75ae3aa157b47fccd75266ba40aa0325b63a
parentf7656d06c0d2095a8d8dd3b930e48dc98996634f (diff)
C++11 scoped enum support for QML
[ChangeLog][QtQml] Support C++11 scoped enums in QML. These can now be accessed as <TypeName>.<EnumName>.<EnumValue> Change-Id: I29bd3f16e980f3e6f1b2390b5a8e9e8e999952a3 Task-number: QTBUG-54961 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp37
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp45
-rw-r--r--src/qml/compiler/qqmltypecompiler_p.h2
-rw-r--r--src/qml/qml/qqmlcustomparser.cpp22
-rw-r--r--src/qml/qml/qqmlmetatype.cpp191
-rw-r--r--src/qml/qml/qqmlmetatype_p.h14
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp29
-rw-r--r--src/qml/qml/qqmltypewrapper_p.h15
-rw-r--r--tests/auto/qml/qqmllanguage/data/cppnamespace.qml1
-rw-r--r--tests/auto/qml/qqmllanguage/data/registeredCompositeTypeWithEnum.qml1
-rw-r--r--tests/auto/qml/qqmllanguage/data/scopedEnum.qml21
-rw-r--r--tests/auto/qml/qqmllanguage/data/scopedEnumList.errors.txt1
-rw-r--r--tests/auto/qml/qqmllanguage/data/scopedEnumList.qml10
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.h18
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp24
15 files changed, 412 insertions, 19 deletions
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index 218f5675dc..de04116a6b 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -1687,6 +1687,7 @@ enum MetaObjectResolverFlags {
};
static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject);
+static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlType *qmlType, int index);
static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
const QV4::IR::MemberExpressionResolver *resolver,
@@ -1702,6 +1703,14 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
if (ok) {
member->setEnumValue(value);
return QV4::IR::SInt32Type;
+ } else {
+ int index = type->scopedEnumIndex(qmlEngine, *member->name, &ok);
+ if (ok) {
+ auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
+ newResolver->owner = resolver->owner;
+ initScopedEnumResolver(newResolver, type, index);
+ return QV4::IR::DiscoveredType(newResolver);
+ }
}
}
@@ -1885,6 +1894,34 @@ static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver,
resolver->flags = 0;
}
+static QV4::IR::DiscoveredType resolveScopedEnum(QQmlEnginePrivate *qmlEngine,
+ const QV4::IR::MemberExpressionResolver *resolver,
+ QV4::IR::Member *member)
+{
+ if (!member->name->constData()->isUpper())
+ return QV4::IR::VarType;
+
+ QQmlType *type = static_cast<QQmlType*>(resolver->data);
+ int index = resolver->flags;
+
+ bool ok = false;
+ int value = type->scopedEnumValue(qmlEngine, index, *member->name, &ok);
+ if (!ok)
+ return QV4::IR::VarType;
+ member->setEnumValue(value);
+ return QV4::IR::SInt32Type;
+}
+
+static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlType *qmlType, int index)
+{
+ Q_ASSERT(resolver);
+
+ resolver->resolveMember = &resolveScopedEnum;
+ resolver->data = qmlType;
+ resolver->extraData = 0;
+ resolver->flags = index;
+}
+
#endif // V4_BOOTSTRAP
void JSCodeGen::beginFunctionBodyHook()
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
index d1d22be0ac..c09fde86f1 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -591,21 +591,32 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj,
if (!string.constData()->isUpper())
return true;
+ // we support one or two '.' in the enum phrase:
+ // * <TypeName>.<EnumValue>
+ // * <TypeName>.<ScopedEnumName>.<EnumValue>
+
int dot = string.indexOf(QLatin1Char('.'));
if (dot == -1 || dot == string.length()-1)
return true;
- if (string.indexOf(QLatin1Char('.'), dot+1) != -1)
- return true;
+ int dot2 = string.indexOf(QLatin1Char('.'), dot+1);
+ if (dot2 != -1 && dot2 != string.length()-1) {
+ if (!string.at(dot+1).isUpper())
+ return true;
+ if (string.indexOf(QLatin1Char('.'), dot2+1) != -1)
+ return true;
+ }
QHashedStringRef typeName(string.constData(), dot);
const bool isQtObject = (typeName == QLatin1String("Qt"));
- const QStringRef enumValue = string.midRef(dot + 1);
+ const QStringRef scopedEnumName = (dot2 != -1 ? string.midRef(dot + 1, dot2 - dot - 1) : QStringRef());
+ // ### consider supporting scoped enums in Qt namespace
+ const QStringRef enumValue = string.midRef(!isQtObject && dot2 != -1 ? dot2 + 1 : dot + 1);
- if (isIntProp) {
+ if (isIntProp) { // ### C++11 allows enums to be other integral types. Should we support other integral types here?
// Allow enum assignment to ints.
bool ok;
- int enumval = evaluateEnum(typeName.toString(), enumValue.toUtf8(), &ok);
+ int enumval = evaluateEnum(typeName.toString(), scopedEnumName, enumValue, &ok);
if (ok) {
if (!assignEnumToBinding(binding, enumValue, enumval, isQtObject))
return false;
@@ -623,18 +634,25 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj,
auto *tr = resolvedTypes->value(obj->inheritedTypeNameIndex);
if (type && tr && tr->type == type) {
+ // When these two match, we can short cut the search
QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex());
+ QMetaEnum menum = mprop.enumerator();
+ QByteArray enumName = enumValue.toUtf8();
+ if (menum.isScoped() && !scopedEnumName.isEmpty() && enumName != scopedEnumName.toUtf8())
+ return true;
- // When these two match, we can short cut the search
if (mprop.isFlagType()) {
- value = mprop.enumerator().keysToValue(enumValue.toUtf8().constData(), &ok);
+ value = menum.keysToValue(enumName.constData(), &ok);
} else {
- value = mprop.enumerator().keyToValue(enumValue.toUtf8().constData(), &ok);
+ value = menum.keyToValue(enumName.constData(), &ok);
}
} else {
// Otherwise we have to search the whole type
if (type) {
- value = type->enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue), &ok);
+ if (!scopedEnumName.isEmpty())
+ value = type->scopedEnumValue(compiler->enginePrivate(), scopedEnumName, enumValue, &ok);
+ else
+ value = type->enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue), &ok);
} else {
QByteArray enumName = enumValue.toUtf8();
const QMetaObject *metaObject = StaticQtMetaObject::get();
@@ -651,7 +669,7 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj,
return assignEnumToBinding(binding, enumValue, value, isQtObject);
}
-int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QByteArray &enumValue, bool *ok) const
+int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QStringRef &enumName, const QStringRef &enumValue, bool *ok) const
{
Q_ASSERT_X(ok, "QQmlEnumTypeResolver::evaluateEnum", "ok must not be a null pointer");
*ok = false;
@@ -661,13 +679,16 @@ int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QByteArray &e
imports->resolveType(scope, &type, 0, 0, 0);
if (!type)
return -1;
- return type->enumValue(compiler->enginePrivate(), QHashedCStringRef(enumValue.constData(), enumValue.length()), ok);
+ if (!enumName.isEmpty())
+ return type->scopedEnumValue(compiler->enginePrivate(), enumName, enumValue, ok);
+ return type->enumValue(compiler->enginePrivate(), QHashedStringRef(enumValue.constData(), enumValue.length()), ok);
}
const QMetaObject *mo = StaticQtMetaObject::get();
int i = mo->enumeratorCount();
+ const QByteArray ba = enumValue.toUtf8();
while (i--) {
- int v = mo->enumerator(i).keyToValue(enumValue.constData(), ok);
+ int v = mo->enumerator(i).keyToValue(ba.constData(), ok);
if (*ok)
return v;
}
diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h
index 79fc073d8b..76aa422fc5 100644
--- a/src/qml/compiler/qqmltypecompiler_p.h
+++ b/src/qml/compiler/qqmltypecompiler_p.h
@@ -209,7 +209,7 @@ private:
bool tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache,
const QQmlPropertyData *prop,
QmlIR::Binding *binding);
- int evaluateEnum(const QString &scope, const QByteArray &enumValue, bool *ok) const;
+ int evaluateEnum(const QString &scope, const QStringRef &enumName, const QStringRef &enumValue, bool *ok) const;
const QVector<QmlIR::Object*> &qmlObjects;
diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp
index 0b0bbef795..6ad641b8b1 100644
--- a/src/qml/qml/qqmlcustomparser.cpp
+++ b/src/qml/qml/qqmlcustomparser.cpp
@@ -121,13 +121,16 @@ int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const
{
Q_ASSERT_X(ok, "QQmlCustomParser::evaluateEnum", "ok must not be a null pointer");
*ok = false;
+
+ // we support one or two '.' in the enum phrase:
+ // * <TypeName>.<EnumValue>
+ // * <TypeName>.<ScopedEnumName>.<EnumValue>
+
int dot = script.indexOf('.');
- if (dot == -1)
+ if (dot == -1 || dot == script.length()-1)
return -1;
-
QString scope = QString::fromUtf8(script.left(dot));
- QByteArray enumValue = script.mid(dot+1);
if (scope != QLatin1String("Qt")) {
if (imports.isNull())
@@ -142,9 +145,20 @@ int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const
type = result.type;
}
- return type ? type->enumValue(engine, QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1;
+ if (!type)
+ return -1;
+
+ int dot2 = script.indexOf('.', dot+1);
+ const bool dot2Valid = dot2 != -1 && dot2 != script.length()-1;
+ QByteArray enumValue = script.mid(dot2Valid ? dot2 + 1 : dot + 1);
+ QByteArray scopedEnumName = (dot2Valid ? script.mid(dot + 1, dot2 - dot - 1) : QByteArray());
+ if (!scopedEnumName.isEmpty())
+ return type->scopedEnumValue(engine, scopedEnumName, enumValue, ok);
+ else
+ return type->enumValue(engine, QHashedCStringRef(enumValue.constData(), enumValue.length()), ok);
}
+ QByteArray enumValue = script.mid(dot + 1);
const QMetaObject *mo = StaticQtMetaObject::get();
int i = mo->enumeratorCount();
while (i--) {
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index bb9b69c479..0672618225 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -216,6 +216,8 @@ public:
mutable bool haveSuperType:1;
mutable QList<QQmlProxyMetaObject::ProxyData> metaObjects;
mutable QStringHash<int> enums;
+ mutable QStringHash<int> scopedEnumIndex; // maps from enum name to index in scopedEnums
+ mutable QList<QStringHash<int>*> scopedEnums;
static QHash<const QMetaObject *, int> attachedPropertyIds;
};
@@ -313,6 +315,7 @@ QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type)
QQmlTypePrivate::~QQmlTypePrivate()
{
+ qDeleteAll(scopedEnums);
switch (regType) {
case QQmlType::CppType:
delete extraData.cd->customParser;
@@ -507,6 +510,67 @@ int QQmlType::resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString
return type->enumValue(engine, name, ok);
}
+int QQmlType::resolveCompositeScopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const
+{
+ Q_ASSERT(isComposite());
+ *ok = false;
+ QQmlType *type = resolveCompositeBaseType(engine);
+ if (!type)
+ return -1;
+ return type->scopedEnumIndex(engine, name, ok);
+}
+
+int QQmlType::resolveCompositeScopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bool *ok) const
+{
+ Q_ASSERT(isComposite());
+ *ok = false;
+ QQmlType *type = resolveCompositeBaseType(engine);
+ if (!type)
+ return -1;
+ return type->scopedEnumIndex(engine, name, ok);
+}
+
+
+int QQmlType::resolveCompositeScopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *name, bool *ok) const
+{
+ Q_ASSERT(isComposite());
+ *ok = false;
+ QQmlType *type = resolveCompositeBaseType(engine);
+ if (!type)
+ return -1;
+ return type->scopedEnumValue(engine, index, name, ok);
+}
+
+int QQmlType::resolveCompositeScopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &name, bool *ok) const
+{
+ Q_ASSERT(isComposite());
+ *ok = false;
+ QQmlType *type = resolveCompositeBaseType(engine);
+ if (!type)
+ return -1;
+ return type->scopedEnumValue(engine, index, name, ok);
+}
+
+int QQmlType::resolveCompositeScopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scopedName, const QByteArray &name, bool *ok) const
+{
+ Q_ASSERT(isComposite());
+ *ok = false;
+ QQmlType *type = resolveCompositeBaseType(engine);
+ if (!type)
+ return -1;
+ return type->scopedEnumValue(engine, scopedName, name, ok);
+}
+
+int QQmlType::resolveCompositeScopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scopedName, const QStringRef &name, bool *ok) const
+{
+ Q_ASSERT(isComposite());
+ *ok = false;
+ QQmlType *type = resolveCompositeBaseType(engine);
+ if (!type)
+ return -1;
+ return type->scopedEnumValue(engine, scopedName, name, ok);
+}
+
static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd)
{
@@ -702,8 +766,21 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const
// Add any enum values defined by this class, overwriting any inherited values
for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) {
QMetaEnum e = metaObject->enumerator(ii);
- for (int jj = 0; jj < e.keyCount(); ++jj)
- enums.insert(QString::fromUtf8(e.key(jj)), e.value(jj));
+ const bool isScoped = e.isScoped();
+ QStringHash<int> *scoped = isScoped ? new QStringHash<int>() : 0;
+
+ for (int jj = 0; jj < e.keyCount(); ++jj) {
+ const QString key = QString::fromUtf8(e.key(jj));
+ const int value = e.value(jj);
+ enums.insert(key, value);
+ if (isScoped)
+ scoped->insert(key, value);
+ }
+
+ if (isScoped) {
+ scopedEnums << scoped;
+ scopedEnumIndex.insert(QString::fromUtf8(e.name()), scopedEnums.count()-1);
+ }
}
}
@@ -1004,6 +1081,116 @@ int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool
return -1;
}
+int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *name, bool *ok) const
+{
+ Q_ASSERT(ok);
+ if (isComposite())
+ return resolveCompositeScopedEnumIndex(engine, name, ok);
+ *ok = true;
+
+ d->initEnums();
+
+ int *rv = d->scopedEnumIndex.value(name);
+ if (rv)
+ return *rv;
+
+ *ok = false;
+ return -1;
+}
+
+int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bool *ok) const
+{
+ Q_ASSERT(ok);
+ if (isComposite())
+ return resolveCompositeScopedEnumIndex(engine, name, ok);
+ *ok = true;
+
+ d->initEnums();
+
+ int *rv = d->scopedEnumIndex.value(name);
+ if (rv)
+ return *rv;
+
+ *ok = false;
+ return -1;
+}
+
+int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *name, bool *ok) const
+{
+ Q_ASSERT(ok);
+ if (isComposite())
+ return resolveCompositeScopedEnumValue(engine, index, name, ok);
+ *ok = true;
+
+ Q_ASSERT(index > -1 && index < d->scopedEnums.count());
+ int *rv = d->scopedEnums.at(index)->value(name);
+ if (rv)
+ return *rv;
+
+ *ok = false;
+ return -1;
+}
+
+int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &name, bool *ok) const
+{
+ Q_ASSERT(ok);
+ if (isComposite())
+ return resolveCompositeScopedEnumValue(engine, index, name, ok);
+ *ok = true;
+
+ Q_ASSERT(index > -1 && index < d->scopedEnums.count());
+ int *rv = d->scopedEnums.at(index)->value(name);
+ if (rv)
+ return *rv;
+
+ *ok = false;
+ return -1;
+}
+
+int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scopedEnumName, const QByteArray &name, bool *ok) const
+{
+ Q_ASSERT(ok);
+ if (isComposite())
+ return resolveCompositeScopedEnumValue(engine, scopedEnumName, name, ok);
+ *ok = true;
+
+ d->initEnums();
+
+ int *rv = d->scopedEnumIndex.value(QHashedCStringRef(scopedEnumName.constData(), scopedEnumName.length()));
+ if (rv) {
+ int index = *rv;
+ Q_ASSERT(index > -1 && index < d->scopedEnums.count());
+ rv = d->scopedEnums.at(index)->value(QHashedCStringRef(name.constData(), name.length()));
+ if (rv)
+ return *rv;
+ }
+
+ *ok = false;
+ return -1;
+}
+
+int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scopedEnumName, const QStringRef &name, bool *ok) const
+{
+ Q_ASSERT(ok);
+ if (isComposite())
+ return resolveCompositeScopedEnumValue(engine, scopedEnumName, name, ok);
+ *ok = true;
+
+ d->initEnums();
+
+ int *rv = d->scopedEnumIndex.value(QHashedStringRef(scopedEnumName));
+ if (rv) {
+ int index = *rv;
+ Q_ASSERT(index > -1 && index < d->scopedEnums.count());
+ rv = d->scopedEnums.at(index)->value(QHashedStringRef(name));
+ if (rv)
+ return *rv;
+ }
+
+ *ok = false;
+ return -1;
+}
+
QQmlTypeModule::QQmlTypeModule()
: d(new QQmlTypeModulePrivate)
{
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index 2b615e645a..4dd28bbd36 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -216,10 +216,24 @@ public:
int enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &, bool *ok) const;
int enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &, bool *ok) const;
int enumValue(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const;
+
+ int scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const;
+ int scopedEnumIndex(QQmlEnginePrivate *engine, const QString &, bool *ok) const;
+ int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *, bool *ok) const;
+ int scopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &, bool *ok) const;
+ int scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &, const QByteArray &, bool *ok) const;
+ int scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &, const QStringRef &, bool *ok) const;
+
private:
QQmlType *superType() const;
QQmlType *resolveCompositeBaseType(QQmlEnginePrivate *engine) const;
int resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const;
+ int resolveCompositeScopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *, bool *ok) const;
+ int resolveCompositeScopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bool *ok) const;
+ int resolveCompositeScopedEnumValue(QQmlEnginePrivate *engine, int index, const QV4::String *, bool *ok) const;
+ int resolveCompositeScopedEnumValue(QQmlEnginePrivate *engine, int index, const QString &name, bool *ok) const;
+ int resolveCompositeScopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scopedName, const QByteArray &name, bool *ok) const;
+ int resolveCompositeScopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scopedName, const QStringRef &name, bool *ok) const;
friend class QQmlTypePrivate;
friend struct QQmlMetaTypeData;
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp
index 7b98096a7f..bf68b1ed4b 100644
--- a/src/qml/qml/qqmltypewrapper.cpp
+++ b/src/qml/qml/qqmltypewrapper.cpp
@@ -53,6 +53,7 @@ QT_BEGIN_NAMESPACE
using namespace QV4;
DEFINE_OBJECT_VTABLE(QmlTypeWrapper);
+DEFINE_OBJECT_VTABLE(QQmlScopedEnumWrapper);
void Heap::QmlTypeWrapper::init()
{
@@ -224,6 +225,14 @@ ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasPrope
if (ok)
return QV4::Primitive::fromInt32(value).asReturnedValue();
+ value = type->scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok);
+ if (ok) {
+ Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocObject<QQmlScopedEnumWrapper>());
+ enumWrapper->d()->type = type;
+ enumWrapper->d()->scopeEnumIndex = value;
+ return enumWrapper.asReturnedValue();
+ }
+
// Fall through to base implementation
} else if (w->d()->object) {
@@ -382,4 +391,24 @@ ReturnedValue QmlTypeWrapper::instanceOf(const Object *typeObject, const Value &
return QV4::Encode(QQmlMetaObject::canConvert(theirType, myQmlType));
}
+ReturnedValue QQmlScopedEnumWrapper::get(const Managed *m, String *name, bool *hasProperty)
+{
+ Q_ASSERT(m->as<QQmlScopedEnumWrapper>());
+ const QQmlScopedEnumWrapper *resource = static_cast<const QQmlScopedEnumWrapper *>(m);
+ QV4::ExecutionEngine *v4 = resource->engine();
+ QV4::Scope scope(v4);
+
+ QQmlType *type = resource->d()->type;
+ int index = resource->d()->scopeEnumIndex;
+
+ bool ok = false;
+ int value = type->scopedEnumValue(QQmlEnginePrivate::get(v4->qmlEngine()), index, name, &ok);
+ if (hasProperty)
+ *hasProperty = ok;
+ if (ok)
+ return QV4::Primitive::fromInt32(value).asReturnedValue();
+
+ return Encode::undefined();
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h
index c584458ed4..86b2e47f29 100644
--- a/src/qml/qml/qqmltypewrapper_p.h
+++ b/src/qml/qml/qqmltypewrapper_p.h
@@ -81,6 +81,13 @@ struct QmlTypeWrapper : Object {
const void *importNamespace;
};
+struct QQmlScopedEnumWrapper : Object {
+ void init() { Object::init(); }
+ void destroy() { Object::destroy(); }
+ int scopeEnumIndex;
+ QQmlType *type;
+};
+
}
struct Q_QML_EXPORT QmlTypeWrapper : Object
@@ -106,6 +113,14 @@ struct Q_QML_EXPORT QmlTypeWrapper : Object
static ReturnedValue instanceOf(const Object *typeObject, const Value &var);
};
+struct Q_QML_EXPORT QQmlScopedEnumWrapper : Object
+{
+ V4_OBJECT2(QQmlScopedEnumWrapper, Object)
+ V4_NEEDS_DESTROY
+
+ static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
+};
+
}
QT_END_NAMESPACE
diff --git a/tests/auto/qml/qqmllanguage/data/cppnamespace.qml b/tests/auto/qml/qqmllanguage/data/cppnamespace.qml
index efedf2b14a..48f7eb6715 100644
--- a/tests/auto/qml/qqmllanguage/data/cppnamespace.qml
+++ b/tests/auto/qml/qqmllanguage/data/cppnamespace.qml
@@ -2,4 +2,5 @@ import Test 1.0
MyNamespacedType {
myEnum: MyNamespace.Key5
+ property int intProperty: MyNamespace.MyOtherNSEnum.OtherKey2
}
diff --git a/tests/auto/qml/qqmllanguage/data/registeredCompositeTypeWithEnum.qml b/tests/auto/qml/qqmllanguage/data/registeredCompositeTypeWithEnum.qml
index 5f8c11e5f6..b6a07693f2 100644
--- a/tests/auto/qml/qqmllanguage/data/registeredCompositeTypeWithEnum.qml
+++ b/tests/auto/qml/qqmllanguage/data/registeredCompositeTypeWithEnum.qml
@@ -3,4 +3,5 @@ import Test 1.0
RegisteredCompositeTypeWithEnum {
property int enumValue0: RegisteredCompositeTypeWithEnum.EnumValue0
property int enumValue42: RegisteredCompositeTypeWithEnum.EnumValue42
+ property int enumValue15: RegisteredCompositeTypeWithEnum.ScopedCompositeEnum.EnumValue15
}
diff --git a/tests/auto/qml/qqmllanguage/data/scopedEnum.qml b/tests/auto/qml/qqmllanguage/data/scopedEnum.qml
new file mode 100644
index 0000000000..7f4177af76
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/scopedEnum.qml
@@ -0,0 +1,21 @@
+import QtQuick 2.0
+import Test 1.0
+
+MyTypeObject {
+ id: obj
+ scopedEnum: MyTypeObject.MyScopedEnum.ScopedVal1
+ intProperty: MyTypeObject.MyScopedEnum.ScopedVal2
+ property int listValue: myModel.get(0).myData
+ property int noScope: MyTypeObject.ScopedVal1
+
+ function assignNewValue() {
+ scopedEnum = MyTypeObject.MyScopedEnum.ScopedVal2
+ noScope = MyTypeObject.ScopedVal2
+ }
+
+ property ListModel myModel: ListModel {
+ ListElement {
+ myData: MyTypeObject.MyScopedEnum.ScopedVal3
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/scopedEnumList.errors.txt b/tests/auto/qml/qqmllanguage/data/scopedEnumList.errors.txt
new file mode 100644
index 0000000000..67576dfd8d
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/scopedEnumList.errors.txt
@@ -0,0 +1 @@
+7:13:ListElement: cannot use script for property value
diff --git a/tests/auto/qml/qqmllanguage/data/scopedEnumList.qml b/tests/auto/qml/qqmllanguage/data/scopedEnumList.qml
new file mode 100644
index 0000000000..8655139683
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/scopedEnumList.qml
@@ -0,0 +1,10 @@
+import QtQuick 2.0
+import Test 1.0
+
+MyTypeObject {
+ property ListModel myModel: ListModel {
+ ListElement {
+ myData: MyTypeObject.MyScopedEnum
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h
index 7d7a8ac6d3..e4a76b4324 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.h
+++ b/tests/auto/qml/qqmllanguage/testtypes.h
@@ -223,6 +223,7 @@ class MyTypeObject : public QObject
Q_PROPERTY(Qt::TextFormat qtEnumProperty READ qtEnumProperty WRITE setQtEnumProperty NOTIFY qtEnumPropertyChanged)
Q_PROPERTY(MyMirroredEnum mirroredEnumProperty READ mirroredEnumProperty WRITE setMirroredEnumProperty NOTIFY mirroredEnumPropertyChanged)
Q_PROPERTY(MyEnumContainer::RelatedEnum relatedEnumProperty READ relatedEnumProperty WRITE setRelatedEnumProperty)
+ Q_PROPERTY(MyScopedEnum scopedEnum READ scopedEnum WRITE setScopedEnum)
Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty NOTIFY stringPropertyChanged)
Q_PROPERTY(QByteArray byteArrayProperty READ byteArrayProperty WRITE setByteArrayProperty NOTIFY byteArrayPropertyChanged)
Q_PROPERTY(uint uintProperty READ uintProperty WRITE setUintProperty NOTIFY uintPropertyChanged)
@@ -339,6 +340,14 @@ public:
relatedEnumPropertyValue = v;
}
+ enum class MyScopedEnum : int { ScopedVal1, ScopedVal2, ScopedVal3 };
+ Q_ENUM(MyScopedEnum)
+ MyScopedEnum scopedEnumPropertyValue;
+ MyScopedEnum scopedEnum() const { return scopedEnumPropertyValue; }
+ void setScopedEnum(MyScopedEnum v) {
+ scopedEnumPropertyValue = v;
+ }
+
QString stringPropertyValue;
QString stringProperty() const {
return stringPropertyValue;
@@ -738,6 +747,13 @@ namespace MyNamespace {
};
Q_ENUM_NS(MyNSEnum);
+ enum class MyOtherNSEnum {
+ OtherKey1 = 1,
+ OtherKey2
+ };
+ Q_ENUM_NS(MyOtherNSEnum);
+
+
class MyNamespacedType : public QObject
{
Q_OBJECT
@@ -1171,9 +1187,11 @@ class MyCompositeBaseType : public QObject
{
Q_OBJECT
Q_ENUMS(CompositeEnum)
+ Q_ENUMS(ScopedCompositeEnum)
public:
enum CompositeEnum { EnumValue0, EnumValue42 = 42 };
+ enum class ScopedCompositeEnum : int { EnumValue15 = 15 };
static QObject *qmlAttachedProperties(QObject *parent) { return new QObject(parent); }
};
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index e67fa18309..002c27c49d 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -208,6 +208,7 @@ private slots:
void lowercaseEnumRuntime();
void lowercaseEnumCompileTime_data();
void lowercaseEnumCompileTime();
+ void scopedEnum();
void literals_data();
void literals();
@@ -541,6 +542,8 @@ void tst_qqmllanguage::errors_data()
QTest::newRow("singularProperty") << "singularProperty.qml" << "singularProperty.errors.txt" << false;
QTest::newRow("singularProperty.2") << "singularProperty.2.qml" << "singularProperty.2.errors.txt" << false;
+ QTest::newRow("scopedEnumList") << "scopedEnumList.qml" << "scopedEnumList.errors.txt" << false;
+
const QString expectedError = isCaseSensitiveFileSystem(dataDirectory()) ?
QStringLiteral("incorrectCase.errors.sensitive.txt") :
QStringLiteral("incorrectCase.errors.insensitive.txt");
@@ -1601,6 +1604,9 @@ void tst_qqmllanguage::cppnamespace()
VERIFY_ERRORS(0);
QObject *object = component.create();
QVERIFY(object != 0);
+
+ QCOMPARE(object->property("intProperty").toInt(), (int)MyNamespace::MyOtherNSEnum::OtherKey2);
+
delete object;
}
@@ -3501,6 +3507,7 @@ void tst_qqmllanguage::registeredCompositeTypeWithEnum()
QCOMPARE(o->property("enumValue0").toInt(), static_cast<int>(MyCompositeBaseType::EnumValue0));
QCOMPARE(o->property("enumValue42").toInt(), static_cast<int>(MyCompositeBaseType::EnumValue42));
+ QCOMPARE(o->property("enumValue15").toInt(), static_cast<int>(MyCompositeBaseType::ScopedCompositeEnum::EnumValue15));
delete o;
}
@@ -3681,6 +3688,23 @@ void tst_qqmllanguage::lowercaseEnumCompileTime()
VERIFY_ERRORS(qPrintable(errorFile));
}
+void tst_qqmllanguage::scopedEnum()
+{
+ QQmlComponent component(&engine, testFileUrl("scopedEnum.qml"));
+
+ MyTypeObject *o = qobject_cast<MyTypeObject *>(component.create());
+ QVERIFY(o != 0);
+
+ QCOMPARE(o->scopedEnum(), MyTypeObject::MyScopedEnum::ScopedVal1);
+ QCOMPARE(o->intProperty(), (int)MyTypeObject::MyScopedEnum::ScopedVal2);
+ QCOMPARE(o->property("listValue").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal3);
+ QCOMPARE(o->property("noScope").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal1);
+
+ QMetaObject::invokeMethod(o, "assignNewValue");
+ QCOMPARE(o->scopedEnum(), MyTypeObject::MyScopedEnum::ScopedVal2);
+ QCOMPARE(o->property("noScope").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal2);
+}
+
void tst_qqmllanguage::literals_data()
{
QTest::addColumn<QString>("property");