diff options
Diffstat (limited to 'src/qml')
-rw-r--r-- | src/qml/jsruntime/qv4dateobject.cpp | 38 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4generatorobject_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4sequenceobject.cpp | 11 | ||||
-rw-r--r-- | src/qml/qml/qqmlbinding.cpp | 9 | ||||
-rw-r--r-- | src/qml/qml/qqmlmetaobject.cpp | 23 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertycache.cpp | 118 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertycache_p.h | 10 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertycachecreator_p.h | 4 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertycachemethodarguments_p.h | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertydata_p.h | 105 |
10 files changed, 209 insertions, 112 deletions
diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 4e445a73fa..a14c7d9257 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -78,10 +78,21 @@ QTBUG-75585 for an explanation and possible workarounds. */ #define USE_QTZ_SYSTEM_ZONE +#elif defined(Q_OS_WASM) +/* + TODO: evaluate using this version of the code more generally, rather than + the #else branches of the various USE_QTZ_SYSTEM_ZONE choices. It might even + work better than the timezone variant; experiments needed. +*/ +// Kludge around the lack of time-zone info using QDateTime. +// It uses localtime() and friends to determine offsets from UTC. +#define USE_QDT_LOCAL_TIME #endif #ifdef USE_QTZ_SYSTEM_ZONE #include <QtCore/QTimeZone> +#elif defined(USE_QDT_LOCAL_TIME) +// QDateTime already included above #else # ifdef Q_OS_WIN # include <windows.h> @@ -356,6 +367,7 @@ static inline double MakeDate(double day, double time) mean a whole day of DST offset for some zones, that have crossed the international date line. This shall confuse client code.) The bug report against the ECMAScript spec is https://github.com/tc39/ecma262/issues/725 + and they've now changed the spec so that the following conforms to it ;^> */ static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC time @@ -363,6 +375,12 @@ static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC t return QTimeZone::systemTimeZone().offsetFromUtc( QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC)) * 1e3 - localTZA; } +#elif defined(USE_QDT_LOCAL_TIME) +static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC time +{ + return QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC + ).toLocalTime().offsetFromUtc() * 1e3 - localTZA; +} #else // This implementation fails to take account of past changes in standard offset. static inline double DaylightSavingTA(double t, double /*localTZA*/) @@ -721,6 +739,26 @@ static double getLocalTZA() // TODO: QTimeZone::resetSystemTimeZone(), see QTBUG-56899 and comment above. // Standard offset, with no daylight-savings adjustment, in ms: return QTimeZone::systemTimeZone().standardTimeOffset(QDateTime::currentDateTime()) * 1e3; +#elif defined(USE_QDT_LOCAL_TIME) + QDate today = QDate::currentDate(); + QDateTime near = today.startOfDay(Qt::LocalTime); + // Early out if we're in standard time anyway: + if (!near.isDaylightTime()) + return near.offsetFromUtc() * 1000; + int year, month; + today.getDate(&year, &month, nullptr); + // One of the solstices is probably in standard time: + QDate summer(year, 6, 21), winter(year - (month < 7 ? 1 : 0), 12, 21); + // But check the one closest to the present by preference, in case there's a + // standard time offset change between them: + QDateTime far = summer.startOfDay(Qt::LocalTime); + near = winter.startOfDay(Qt::LocalTime); + if (month > 3 && month < 10) + near.swap(far); + bool isDst = near.isDaylightTime(); + if (isDst && far.isDaylightTime()) // Permanent DST, probably an hour west: + return (qMin(near.offsetFromUtc(), far.offsetFromUtc()) - 3600) * 1000; + return (isDst ? far : near).offsetFromUtc() * 1000; #else # ifdef Q_OS_WIN TIME_ZONE_INFORMATION tzInfo; diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h index 10eea5e46b..14368f5416 100644 --- a/src/qml/jsruntime/qv4generatorobject_p.h +++ b/src/qml/jsruntime/qv4generatorobject_p.h @@ -87,7 +87,6 @@ struct GeneratorPrototype : FunctionObject { #define GeneratorObjectMembers(class, Member) \ Member(class, Pointer, ExecutionContext *, context) \ - Member(class, Pointer, GeneratorFunction *, function) \ Member(class, NoMark, GeneratorState, state) \ Member(class, NoMark, CppStackFrame, cppFrame) \ Member(class, Pointer, ArrayObject *, values) \ diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index aff8844bb0..f84718b48f 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -720,13 +720,20 @@ DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlRealList); } #define REGISTER_QML_SEQUENCE_METATYPE(unused, unused2, SequenceType, unused3) qRegisterMetaType<SequenceType>(#SequenceType); -void SequencePrototype::init() +static bool registerAllSequenceTypes() { FOREACH_QML_SEQUENCE_TYPE(REGISTER_QML_SEQUENCE_METATYPE) + return true; +} +#undef REGISTER_QML_SEQUENCE_METATYPE + +void SequencePrototype::init() +{ + static const bool registered = registerAllSequenceTypes(); + Q_UNUSED(registered); defineDefaultProperty(QStringLiteral("sort"), method_sort, 1); defineDefaultProperty(engine()->id_valueOf(), method_valueOf, 0); } -#undef REGISTER_QML_SEQUENCE_METATYPE ReturnedValue SequencePrototype::method_valueOf(const FunctionObject *f, const Value *thisObject, const Value *, int) { diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index b9566d5862..194f7b4cf7 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -645,7 +645,10 @@ void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyD Q_ASSERT(valueTypeMetaObject); QMetaProperty vtProp = valueTypeMetaObject->property(m_targetIndex.valueTypeIndex()); valueTypeData->setFlags(QQmlPropertyData::flagsForProperty(vtProp)); + + // valueTypeData is expected to be local here. It must not be shared with other threads. valueTypeData->setPropType(vtProp.userType()); + valueTypeData->setCoreIndex(m_targetIndex.valueTypeIndex()); } } @@ -748,7 +751,11 @@ QQmlBinding *QQmlBinding::newBinding(QQmlEnginePrivate *engine, const QQmlProper if (property && property->isQObject()) return new QObjectPointerBinding(engine, property->propType()); - const int type = (property && property->isFullyResolved()) ? property->propType() : QMetaType::UnknownType; + // If the property is not resolved at this point, you get a binding of unknown type. + // This has been the case for a long time and we keep it like this in Qt5 to be bug-compatible. + const int type = (property && property->isResolved()) + ? property->propType() + : QMetaType::UnknownType; if (type == qMetaTypeId<QQmlBinding *>()) { return new QQmlBindingBinding; diff --git a/src/qml/qml/qqmlmetaobject.cpp b/src/qml/qml/qqmlmetaobject.cpp index a967f46b12..d273849ccb 100644 --- a/src/qml/qml/qqmlmetaobject.cpp +++ b/src/qml/qml/qqmlmetaobject.cpp @@ -232,8 +232,6 @@ int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, Q_ASSERT(!_m.isNull() && index >= 0); if (_m.isT1()) { - typedef QQmlPropertyCacheMethodArguments A; - QQmlPropertyCache *c = _m.asT1(); Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count()); @@ -242,19 +240,16 @@ int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart)); - if (rv->arguments() && static_cast<A *>(rv->arguments())->argumentsValid) - return static_cast<A *>(rv->arguments())->arguments; + if (QQmlPropertyCacheMethodArguments *args = rv->arguments()) + return args->arguments; const QMetaObject *metaObject = c->createMetaObject(); Q_ASSERT(metaObject); QMetaMethod m = metaObject->method(index); int argc = m.parameterCount(); - if (!rv->arguments()) { - A *args = c->createArgumentsObject(argc, m.parameterNames()); - rv->setArguments(args); - } - A *args = static_cast<A *>(rv->arguments()); + + QQmlPropertyCacheMethodArguments *args = c->createArgumentsObject(argc, m.parameterNames()); QList<QByteArray> argTypeNames; // Only loaded if needed @@ -280,8 +275,14 @@ int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, } args->arguments[ii + 1] = type; } - args->argumentsValid = true; - return static_cast<A *>(rv->arguments())->arguments; + + // If we cannot set it, then another thread has set it in the mean time. + // Just return that one, then. We don't have to delete the arguments object + // we've created as it's tracked in a linked list. + if (rv->setArguments(args)) + return args->arguments; + else + return rv->arguments()->arguments; } else { QMetaMethod m = _m.asT2()->method(index); diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 0911c06cd0..6b68a2a288 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -138,17 +138,17 @@ void QQmlPropertyData::lazyLoad(const QMetaProperty &p) { populate(this, p); int type = static_cast<int>(p.userType()); - if (type == QMetaType::QObjectStar) { - setPropType(type); + + if (type >= QMetaType::User || type == 0) + return; // Resolve later + + if (type == QMetaType::QObjectStar) m_flags.type = Flags::QObjectDerivedType; - } else if (type == QMetaType::QVariant) { - setPropType(type); + else if (type == QMetaType::QVariant) m_flags.type = Flags::QVariantType; - } else if (type >= QMetaType::User || type == 0) { - m_flags.notFullyResolved = true; - } else { - setPropType(type); - } + + // This is OK because lazyLoad is done before exposing the property data. + setPropType(type); } void QQmlPropertyData::load(const QMetaProperty &p) @@ -158,45 +158,48 @@ void QQmlPropertyData::load(const QMetaProperty &p) flagsForPropertyType(propType(), m_flags); } -void QQmlPropertyData::load(const QMetaMethod &m) +static void populate(QQmlPropertyData *data, const QMetaMethod &m) { - setCoreIndex(m.methodIndex()); - setArguments(nullptr); + data->setCoreIndex(m.methodIndex()); - setPropType(m.returnType()); - - m_flags.type = Flags::FunctionType; - if (m.methodType() == QMetaMethod::Signal) { - m_flags.setIsSignal(true); - } else if (m.methodType() == QMetaMethod::Constructor) { - m_flags.setIsConstructor(true); - setPropType(QMetaType::QObjectStar); - } + QQmlPropertyData::Flags flags = data->flags(); + flags.type = QQmlPropertyData::Flags::FunctionType; + if (m.methodType() == QMetaMethod::Signal) + flags.setIsSignal(true); + else if (m.methodType() == QMetaMethod::Constructor) + flags.setIsConstructor(true); const int paramCount = m.parameterCount(); if (paramCount) { - m_flags.setHasArguments(true); + flags.setHasArguments(true); if ((paramCount == 1) && (m.parameterTypes().constFirst() == "QQmlV4Function*")) - m_flags.setIsV4Function(true); + flags.setIsV4Function(true); } if (m.attributes() & QMetaMethod::Cloned) - m_flags.setIsCloned(true); + flags.setIsCloned(true); + + data->setFlags(flags); Q_ASSERT(m.revision() <= Q_INT16_MAX); - setRevision(m.revision()); + data->setRevision(m.revision()); } -void QQmlPropertyData::lazyLoad(const QMetaMethod &m) +void QQmlPropertyData::load(const QMetaMethod &m) { - load(m); + populate(this, m); + setPropType(m.methodType() == QMetaMethod::Constructor + ? QMetaType::QObjectStar + : m.returnType()); +} +void QQmlPropertyData::lazyLoad(const QMetaMethod &m) +{ const char *returnType = m.typeName(); - if (!returnType) - returnType = "\0"; - if ((*returnType != 'v') || (qstrcmp(returnType+1, "oid") != 0)) { - m_flags.notFullyResolved = true; - } + if (!returnType || *returnType != 'v' || qstrcmp(returnType + 1, "oid") != 0) + populate(this, m); + else + load(m); // If it's void, resolve it right away } /*! @@ -318,7 +321,6 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag data.setPropType(QMetaType::UnknownType); data.setCoreIndex(coreIndex); data.setFlags(flags); - data.setArguments(nullptr); QQmlPropertyData handler = data; handler.m_flags.setIsSignalHandler(true); @@ -327,7 +329,6 @@ void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flag int argumentCount = *types; QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names); ::memcpy(args->arguments, types, (argumentCount + 1) * sizeof(int)); - args->argumentsValid = true; data.setArguments(args); } @@ -361,7 +362,6 @@ void QQmlPropertyCache::appendMethod(const QString &name, QQmlPropertyData::Flag QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names); for (int ii = 0; ii < argumentCount; ++ii) args->arguments[ii + 1] = parameterTypes.at(ii); - args->argumentsValid = true; data.setArguments(args); data.setFlags(flags); @@ -650,25 +650,23 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject, } } -void QQmlPropertyCache::resolve(QQmlPropertyData *data) const +int QQmlPropertyCache::findPropType(const QQmlPropertyData *data) const { - Q_ASSERT(data->notFullyResolved()); - data->m_flags.notFullyResolved = false; - + int type = QMetaType::UnknownType; const QMetaObject *mo = firstCppMetaObject(); if (data->isFunction()) { auto metaMethod = mo->method(data->coreIndex()); const char *retTy = metaMethod.typeName(); if (!retTy) retTy = "\0"; - data->setPropType(QMetaType::type(retTy)); + type = QMetaType::type(retTy); } else { auto metaProperty = mo->property(data->coreIndex()); - data->setPropType(QMetaType::type(metaProperty.typeName())); + type = QMetaType::type(metaProperty.typeName()); } if (!data->isFunction()) { - if (data->propType() == QMetaType::UnknownType) { + if (type == QMetaType::UnknownType) { QQmlPropertyCache *p = _parent; while (p && (!mo || _ownMetaObject)) { mo = p->_metaObject; @@ -684,11 +682,33 @@ void QQmlPropertyCache::resolve(QQmlPropertyData *data) const int registerResult = -1; void *argv[] = { ®isterResult }; - mo->static_metacall(QMetaObject::RegisterPropertyMetaType, data->coreIndex() - propOffset, argv); - data->setPropType(registerResult == -1 ? QMetaType::UnknownType : registerResult); + mo->static_metacall(QMetaObject::RegisterPropertyMetaType, + data->coreIndex() - propOffset, argv); + type = registerResult == -1 ? QMetaType::UnknownType : registerResult; } } - flagsForPropertyType(data->propType(), data->m_flags); + } + + return type; +} + +void QQmlPropertyCache::resolve(QQmlPropertyData *data) const +{ + const int type = findPropType(data); + + // Setting the flags unsynchronized is somewhat dirty but unlikely to cause trouble + // in practice. We have to do this before setting the property type because otherwise + // a consumer of the flags might see outdated flags even after the property type has + // become valid. The flags should only depend on the property type and the property + // type should be the same across different invocations. So, setting this concurrently + // should be a noop. + if (!data->isFunction()) + flagsForPropertyType(type, data->m_flags); + + // This is the one place where we can update the property type after exposing the data. + if (!data->m_propTypeAndRelativePropIndex.testAndSetOrdered( + 0, type > 0 ? quint32(type) : quint32(QQmlPropertyData::PropTypeUnknown))) { + return; // Someone else is resolving it already } } @@ -873,12 +893,11 @@ QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int a typedef QQmlPropertyCacheMethodArguments A; A *args = static_cast<A *>(malloc(sizeof(A) + (argc) * sizeof(int))); args->arguments[0] = argc; - args->argumentsValid = false; args->signalParameterStringForJS = nullptr; - args->parameterError = false; args->names = argc ? new QList<QByteArray>(names) : nullptr; - args->next = argumentsCache; - argumentsCache = args; + do { + args->next = argumentsCache; + } while (!argumentsCache.testAndSetRelease(args->next, args)); return args; } @@ -1172,7 +1191,6 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) QQmlPropertyCacheMethodArguments *arguments = nullptr; if (data->hasArguments()) { arguments = (QQmlPropertyCacheMethodArguments *)data->arguments(); - Q_ASSERT(arguments->argumentsValid); for (int ii = 0; ii < arguments->arguments[0]; ++ii) { if (ii != 0) signature.append(','); signature.append(QMetaType::typeName(arguments->arguments[1 + ii])); diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index a5340cec37..1563bc0a41 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -220,6 +220,8 @@ private: _hasPropertyOverrides |= isOverride; } + int findPropType(const QQmlPropertyData *data) const; + private: QQmlPropertyCache *_parent; int propertyIndexCacheStart; @@ -239,14 +241,18 @@ private: QByteArray _dynamicClassName; QByteArray _dynamicStringData; QString _defaultPropertyName; - QQmlPropertyCacheMethodArguments *argumentsCache; + QAtomicPointer<QQmlPropertyCacheMethodArguments> argumentsCache; int _jsFactoryMethodIndex; QByteArray _checksum; }; inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const { - if (p && Q_UNLIKELY(p->notFullyResolved())) + // Avoid resolve() in the common case where it's already initialized and we don't + // run into a data race. resolve() checks again, with an atomic operation. + // If there is no coreIndex, there is no point in trying to resolve anything. In that + // case it's a default-constructed instance that never got load()'ed or lazyLoad()'ed. + if (p && p->coreIndex() != -1 && Q_UNLIKELY(p->m_propTypeAndRelativePropIndex == 0)) resolve(p); return p; diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index 77e3763a49..3fd6c8b4da 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -865,6 +865,10 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor Q_ASSERT(targetCache); targetProperty = targetCache->property(valueTypeIndex); + if (targetProperty == nullptr) { + return qQmlCompileError(alias.referenceLocation, + QQmlPropertyCacheCreatorBase::tr("Invalid alias target")); + } *type = targetProperty->propType(); writable = targetProperty->isWritable(); diff --git a/src/qml/qml/qqmlpropertycachemethodarguments_p.h b/src/qml/qml/qqmlpropertycachemethodarguments_p.h index 62f09bdfff..32affe6d9c 100644 --- a/src/qml/qml/qqmlpropertycachemethodarguments_p.h +++ b/src/qml/qml/qqmlpropertycachemethodarguments_p.h @@ -64,8 +64,6 @@ public: //for signal handler rewrites QString *signalParameterStringForJS; - int parameterError:1; - int argumentsValid:1; QList<QByteArray> *names; diff --git a/src/qml/qml/qqmlpropertydata_p.h b/src/qml/qml/qqmlpropertydata_p.h index d9855797cd..2f1b6f62f1 100644 --- a/src/qml/qml/qqmlpropertydata_p.h +++ b/src/qml/qml/qqmlpropertydata_p.h @@ -84,14 +84,6 @@ public: QVariantType = 9 // Property is a QVariant }; - // The _otherBits (which "pad" the Flags struct to align it nicely) are used - // to store the relative property index. It will only get used when said index fits. See - // trySetStaticMetaCallFunction for details. - // (Note: this padding is done here, because certain compilers have surprising behavior - // when an enum is declared in-between two bit fields.) - enum { BitsLeftInFlags = 15 }; - unsigned otherBits : BitsLeftInFlags; // align to 32 bits - // Members of the form aORb can only be a when type is not FunctionType, and only be // b when type equals FunctionType. For that reason, the semantic meaning of the bit is // overloaded, and the accessor functions are used to get the correct value @@ -102,25 +94,24 @@ public: // // Lastly, isDirect and isOverridden apply to both functions and non-functions private: - unsigned isConstantORisVMEFunction : 1; // Has CONST flag OR Function was added by QML - unsigned isWritableORhasArguments : 1; // Has WRITE function OR Function takes arguments - unsigned isResettableORisSignal : 1; // Has RESET function OR Function is a signal - unsigned isAliasORisVMESignal : 1; // Is a QML alias to another property OR Signal was added by QML - unsigned isFinalORisV4Function : 1; // Has FINAL flag OR Function takes QQmlV4Function* args - unsigned isSignalHandler : 1; // Function is a signal handler - unsigned isOverload : 1; // Function is an overload of another function - unsigned isRequiredORisCloned : 1; // Has REQUIRED flag OR The function was marked as cloned - unsigned isConstructor : 1; // The function was marked is a constructor - unsigned isDirect : 1; // Exists on a C++ QMetaObject - unsigned isOverridden : 1; // Is overridden by a extension property + quint16 isConstantORisVMEFunction : 1; // Has CONST flag OR Function was added by QML + quint16 isWritableORhasArguments : 1; // Has WRITE function OR Function takes arguments + quint16 isResettableORisSignal : 1; // Has RESET function OR Function is a signal + quint16 isAliasORisVMESignal : 1; // Is a QML alias to another property OR Signal was added by QML + quint16 isFinalORisV4Function : 1; // Has FINAL flag OR Function takes QQmlV4Function* args + quint16 isSignalHandler : 1; // Function is a signal handler + quint16 isOverload : 1; // Function is an overload of another function + quint16 isRequiredORisCloned : 1; // Has REQUIRED flag OR The function was marked as cloned + quint16 isConstructor : 1; // The function was marked is a constructor + quint16 isDirect : 1; // Exists on a C++ QMetaObject + quint16 isOverridden : 1; // Is overridden by a extension property public: - unsigned type : 4; // stores an entry of Types + quint16 type : 4; // stores an entry of Types // Apply only to IsFunctions // Internal QQmlPropertyCache flags - unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved - unsigned overrideIndexIsProperty: 1; + quint16 overrideIndexIsProperty: 1; inline Flags(); inline bool operator==(const Flags &other) const; @@ -208,16 +199,12 @@ public: }; + Q_STATIC_ASSERT(sizeof(Flags) == sizeof(quint16)); inline bool operator==(const QQmlPropertyData &) const; Flags flags() const { return m_flags; } - void setFlags(Flags f) - { - unsigned otherBits = m_flags.otherBits; - m_flags = f; - m_flags.otherBits = otherBits; - } + void setFlags(Flags f) { m_flags = f; } bool isValid() const { return coreIndex() != -1; } @@ -253,14 +240,26 @@ public: bool hasOverride() const { return overrideIndex() >= 0; } bool hasRevision() const { return revision() != 0; } - bool isFullyResolved() const { return !m_flags.notFullyResolved; } + // This is unsafe in the general case. The property might be in the process of getting + // resolved. Only use it if this case has been taken into account. + bool isResolved() const { return m_propTypeAndRelativePropIndex != 0; } + + int propType() const + { + const quint32 type = m_propTypeAndRelativePropIndex & PropTypeMask; + Q_ASSERT(type > 0); // Property has to be fully resolved. + return type == PropTypeUnknown ? 0 : type; + } - int propType() const { Q_ASSERT(isFullyResolved()); return m_propType; } void setPropType(int pt) { + // You can only directly set the property type if you own the QQmlPropertyData. + // It must not be exposed to other threads before setting the type! Q_ASSERT(pt >= 0); - Q_ASSERT(pt <= std::numeric_limits<qint16>::max()); - m_propType = quint16(pt); + Q_ASSERT(uint(pt) < PropTypeUnknown); + m_propTypeAndRelativePropIndex + = (m_propTypeAndRelativePropIndex & RelativePropIndexMask) + | (pt == 0 ? PropTypeUnknown : quint32(pt)); } int notifyIndex() const { return m_notifyIndex; } @@ -323,7 +322,10 @@ public: } QQmlPropertyCacheMethodArguments *arguments() const { return m_arguments; } - void setArguments(QQmlPropertyCacheMethodArguments *args) { m_arguments = args; } + bool setArguments(QQmlPropertyCacheMethodArguments *args) + { + return m_arguments.testAndSetRelease(nullptr, args); + } int metaObjectOffset() const { return m_metaObjectOffset; } void setMetaObjectOffset(int off) @@ -336,12 +338,26 @@ public: StaticMetaCallFunction staticMetaCallFunction() const { return m_staticMetaCallFunction; } void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex) { - if (relativePropertyIndex < (1 << Flags::BitsLeftInFlags) - 1) { - m_flags.otherBits = relativePropertyIndex; + if (relativePropertyIndex > std::numeric_limits<quint16>::max()) + return; + + const quint16 propType = m_propTypeAndRelativePropIndex & PropTypeMask; + if (propType > 0) { + // We can do this because we know that resolve() has run at this point + // and we don't need to synchronize anymore. If we get a 0, that means it hasn't + // run or is currently in progress. We don't want to interfer and just go through + // the meta object. + m_propTypeAndRelativePropIndex + = propType | (relativePropertyIndex << RelativePropIndexShift); m_staticMetaCallFunction = f; } } - quint16 relativePropertyIndex() const { Q_ASSERT(hasStaticMetaCallFunction()); return m_flags.otherBits; } + + quint16 relativePropertyIndex() const + { + Q_ASSERT(hasStaticMetaCallFunction()); + return m_propTypeAndRelativePropIndex >> 16; + } static Flags flagsForProperty(const QMetaProperty &); void load(const QMetaProperty &); @@ -401,11 +417,17 @@ private: friend class QQmlPropertyCache; void lazyLoad(const QMetaProperty &); void lazyLoad(const QMetaMethod &); - bool notFullyResolved() const { return m_flags.notFullyResolved; } + + enum { + PropTypeMask = 0x0000ffff, + RelativePropIndexMask = 0xffff0000, + RelativePropIndexShift = 16, + PropTypeUnknown = std::numeric_limits<quint16>::max(), + }; + QAtomicInteger<quint32> m_propTypeAndRelativePropIndex; Flags m_flags; qint16 m_coreIndex = -1; - quint16 m_propType = 0; // The notify index is in the range returned by QObjectPrivate::signalIndex(). // This is different from QMetaMethod::methodIndex(). @@ -416,7 +438,7 @@ private: quint8 m_typeMinorVersion = 0; qint16 m_metaObjectOffset = -1; - QQmlPropertyCacheMethodArguments *m_arguments = nullptr; + QAtomicPointer<QQmlPropertyCacheMethodArguments> m_arguments; StaticMetaCallFunction m_staticMetaCallFunction = nullptr; }; @@ -436,8 +458,7 @@ bool QQmlPropertyData::operator==(const QQmlPropertyData &other) const } QQmlPropertyData::Flags::Flags() - : otherBits(0) - , isConstantORisVMEFunction(false) + : isConstantORisVMEFunction(false) , isWritableORhasArguments(false) , isResettableORisSignal(false) , isAliasORisVMESignal(false) @@ -449,7 +470,6 @@ QQmlPropertyData::Flags::Flags() , isDirect(false) , isOverridden(false) , type(OtherType) - , notFullyResolved(false) , overrideIndexIsProperty(false) {} @@ -465,7 +485,6 @@ bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) c isRequiredORisCloned == other.isRequiredORisCloned && type == other.type && isConstructor == other.isConstructor && - notFullyResolved == other.notFullyResolved && overrideIndexIsProperty == other.overrideIndexIsProperty; } |