qtdeclarative/src/qml/qml/qqmldata_p.h

413 lines
14 KiB
C++

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QQMLDATA_P_H
#define QQMLDATA_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <private/qtqmlglobal_p.h>
#include <private/qobject_p.h>
#include <private/qqmlpropertyindex_p.h>
#include <private/qv4value_p.h>
#include <private/qv4persistent_p.h>
#include <private/qqmlrefcount_p.h>
#include <qqmlprivate.h>
#include <qjsengine.h>
#include <qvector.h>
QT_BEGIN_NAMESPACE
template <class Key, class T> class QHash;
class QQmlEngine;
class QQmlGuardImpl;
class QQmlAbstractBinding;
class QQmlBoundSignal;
class QQmlContext;
class QQmlPropertyCache;
class QQmlContextData;
class QQmlNotifier;
class QQmlDataExtended;
class QQmlNotifierEndpoint;
class QQmlPropertyObserver;
namespace QV4 {
class ExecutableCompilationUnit;
namespace CompiledData {
struct Binding;
}
}
// This class is structured in such a way, that simply zero'ing it is the
// default state for elemental object allocations. This is crucial in the
// workings of the QQmlInstruction::CreateSimpleObject instruction.
// Don't change anything here without first considering that case!
class Q_QML_PRIVATE_EXPORT QQmlData : public QAbstractDeclarativeData
{
public:
QQmlData();
~QQmlData();
static inline void init() {
static bool initialized = false;
if (!initialized) {
initialized = true;
QAbstractDeclarativeData::destroyed = destroyed;
QAbstractDeclarativeData::parentChanged = nullptr;
QAbstractDeclarativeData::signalEmitted = signalEmitted;
QAbstractDeclarativeData::receivers = receivers;
QAbstractDeclarativeData::isSignalConnected = isSignalConnected;
}
}
static void destroyed(QAbstractDeclarativeData *, QObject *);
static void signalEmitted(QAbstractDeclarativeData *, QObject *, int, void **);
static int receivers(QAbstractDeclarativeData *, const QObject *, int);
static bool isSignalConnected(QAbstractDeclarativeData *, const QObject *, int);
void destroyed(QObject *);
void setImplicitDestructible() {
if (!explicitIndestructibleSet) indestructible = false;
}
// If ownMemomry is true, the QQmlData was normally allocated. Otherwise it was allocated
// with placement new and QQmlData::destroyed is not allowed to free the memory
quint32 ownMemory:1;
// indestructible is set if and only if the object has CppOwnership
// This can be explicitly set with QJSEngine::setObjectOwnership
// Top level objects generally have CppOwnership (see QQmlcCmponentprivate::beginCreate),
// unless created by special methods like the QML component.createObject() function
quint32 indestructible:1;
// indestructible was explicitly set with setObjectOwnership
// or the object is a top-level object
quint32 explicitIndestructibleSet:1;
// set when one QObject has been wrapped into QObjectWrapper in multiple engines
// at the same time - a rather rare case
quint32 hasTaintedV4Object:1;
quint32 isQueuedForDeletion:1;
/*
* rootObjectInCreation should be true only when creating top level CPP and QML objects,
* v8 GC will check this flag, only deletes the objects when rootObjectInCreation is false.
*/
quint32 rootObjectInCreation:1;
// set when at least one of the object's properties is intercepted
quint32 hasInterceptorMetaObject:1;
quint32 hasVMEMetaObject:1;
quint32 dummy:8;
// When bindingBitsSize < sizeof(ptr), we store the binding bit flags inside
// bindingBitsValue. When we need more than sizeof(ptr) bits, we allocated
// sufficient space and use bindingBits to point to it.
quint32 bindingBitsArraySize : 16;
typedef quintptr BindingBitsType;
enum {
BitsPerType = sizeof(BindingBitsType) * 8,
InlineBindingArraySize = 2
};
union {
BindingBitsType *bindingBits;
BindingBitsType bindingBitsValue[InlineBindingArraySize];
};
struct NotifyList {
quint64 connectionMask;
quint16 maximumTodoIndex;
quint16 notifiesSize;
QQmlNotifierEndpoint *todo;
QQmlNotifierEndpoint**notifies;
void layout();
private:
void layout(QQmlNotifierEndpoint*);
};
NotifyList *notifyList;
inline QQmlNotifierEndpoint *notify(int index);
void addNotify(int index, QQmlNotifierEndpoint *);
int endpointCount(int index);
bool signalHasEndpoint(int index) const;
void disconnectNotifiers();
// The context that created the C++ object; not refcounted to prevent cycles
QQmlContextData *context = nullptr;
// The outermost context in which this object lives; not refcounted to prevent cycles
QQmlContextData *outerContext = nullptr;
QQmlRefPointer<QQmlContextData> ownContext;
QQmlAbstractBinding *bindings;
QQmlBoundSignal *signalHandlers;
std::vector<QQmlPropertyObserver> propertyObservers;
// Linked list for QQmlContext::contextObjects
QQmlData *nextContextObject;
QQmlData**prevContextObject;
inline bool hasBindingBit(int) const;
inline void setBindingBit(QObject *obj, int);
inline void clearBindingBit(int);
inline bool hasPendingBindingBit(int index) const;
inline void setPendingBindingBit(QObject *obj, int);
inline void clearPendingBindingBit(int);
quint16 lineNumber;
quint16 columnNumber;
quint32 jsEngineId; // id of the engine that created the jsWrapper
struct DeferredData {
DeferredData();
~DeferredData();
unsigned int deferredIdx;
QMultiHash<int, const QV4::CompiledData::Binding *> bindings;
// Not always the same as the other compilation unit
QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
// Could be either context or outerContext
QQmlRefPointer<QQmlContextData> context;
Q_DISABLE_COPY(DeferredData);
};
QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit;
QVector<DeferredData *> deferredData;
void deferData(int objectIndex, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &,
const QQmlRefPointer<QQmlContextData> &);
void releaseDeferredData();
QV4::WeakValue jsWrapper;
QQmlPropertyCache *propertyCache;
QQmlGuardImpl *guards;
static QQmlData *get(const QObject *object, bool create = false) {
QObjectPrivate *priv = QObjectPrivate::get(const_cast<QObject *>(object));
// If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has
// to be avoided because QObjectPrivate::currentChildBeingDeleted is in use.
if (priv->isDeletingChildren || priv->wasDeleted) {
Q_ASSERT(!create);
return nullptr;
} else if (priv->declarativeData) {
return static_cast<QQmlData *>(priv->declarativeData);
} else if (create) {
return createQQmlData(priv);
} else {
return nullptr;
}
}
static bool keepAliveDuringGarbageCollection(const QObject *object) {
QQmlData *ddata = get(object);
if (!ddata || ddata->indestructible || ddata->rootObjectInCreation)
return true;
return false;
}
bool hasExtendedData() const { return extendedData != nullptr; }
QHash<QQmlAttachedPropertiesFunc, QObject *> *attachedProperties() const;
static inline bool wasDeleted(const QObject *);
static void markAsDeleted(QObject *);
static void setQueuedForDeletion(QObject *);
static inline void flushPendingBinding(QObject *, QQmlPropertyIndex propertyIndex);
static QQmlPropertyCache *ensurePropertyCache(QJSEngine *engine, QObject *object)
{
Q_ASSERT(engine);
QQmlData *ddata = QQmlData::get(object, /*create*/true);
if (Q_LIKELY(ddata->propertyCache))
return ddata->propertyCache;
return createPropertyCache(engine, object);
}
Q_ALWAYS_INLINE static uint offsetForBit(int bit) { return static_cast<uint>(bit) / BitsPerType; }
Q_ALWAYS_INLINE static BindingBitsType bitFlagForBit(int bit) { return BindingBitsType(1) << (static_cast<uint>(bit) & (BitsPerType - 1)); }
private:
// For attachedProperties
mutable QQmlDataExtended *extendedData;
Q_NEVER_INLINE static QQmlData *createQQmlData(QObjectPrivate *priv);
Q_NEVER_INLINE static QQmlPropertyCache *createPropertyCache(QJSEngine *engine, QObject *object);
void flushPendingBindingImpl(QQmlPropertyIndex index);
Q_ALWAYS_INLINE bool hasBitSet(int bit) const
{
uint offset = offsetForBit(bit);
if (bindingBitsArraySize <= offset)
return false;
const BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
return bits[offset] & bitFlagForBit(bit);
}
Q_ALWAYS_INLINE void clearBit(int bit)
{
uint offset = QQmlData::offsetForBit(bit);
if (bindingBitsArraySize > offset) {
BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
bits[offset] &= ~QQmlData::bitFlagForBit(bit);
}
}
Q_ALWAYS_INLINE void setBit(QObject *obj, int bit)
{
uint offset = QQmlData::offsetForBit(bit);
BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits;
if (Q_UNLIKELY(bindingBitsArraySize <= offset))
bits = growBits(obj, bit);
bits[offset] |= QQmlData::bitFlagForBit(bit);
}
Q_NEVER_INLINE BindingBitsType *growBits(QObject *obj, int bit);
Q_DISABLE_COPY(QQmlData);
};
bool QQmlData::wasDeleted(const QObject *object)
{
if (!object)
return true;
const QObjectPrivate *priv = QObjectPrivate::get(object);
if (!priv || priv->wasDeleted || priv->isDeletingChildren)
return true;
const QQmlData *ddata = QQmlData::get(object);
return ddata && ddata->isQueuedForDeletion;
}
QQmlNotifierEndpoint *QQmlData::notify(int index)
{
Q_ASSERT(index <= 0xFFFF);
if (!notifyList || !(notifyList->connectionMask & (1ULL << quint64(index % 64)))) {
return nullptr;
} else if (index < notifyList->notifiesSize) {
return notifyList->notifies[index];
} else if (index <= notifyList->maximumTodoIndex) {
notifyList->layout();
}
if (index < notifyList->notifiesSize) {
return notifyList->notifies[index];
} else {
return nullptr;
}
}
/*
The index MUST be in the range returned by QObjectPrivate::signalIndex()
This is different than the index returned by QMetaMethod::methodIndex()
*/
inline bool QQmlData::signalHasEndpoint(int index) const
{
return notifyList && (notifyList->connectionMask & (1ULL << quint64(index % 64)));
}
bool QQmlData::hasBindingBit(int coreIndex) const
{
Q_ASSERT(coreIndex >= 0);
Q_ASSERT(coreIndex <= 0xffff);
return hasBitSet(coreIndex * 2);
}
void QQmlData::setBindingBit(QObject *obj, int coreIndex)
{
Q_ASSERT(coreIndex >= 0);
Q_ASSERT(coreIndex <= 0xffff);
setBit(obj, coreIndex * 2);
}
void QQmlData::clearBindingBit(int coreIndex)
{
Q_ASSERT(coreIndex >= 0);
Q_ASSERT(coreIndex <= 0xffff);
clearBit(coreIndex * 2);
}
bool QQmlData::hasPendingBindingBit(int coreIndex) const
{
Q_ASSERT(coreIndex >= 0);
Q_ASSERT(coreIndex <= 0xffff);
return hasBitSet(coreIndex * 2 + 1);
}
void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex)
{
Q_ASSERT(coreIndex >= 0);
Q_ASSERT(coreIndex <= 0xffff);
setBit(obj, coreIndex * 2 + 1);
}
void QQmlData::clearPendingBindingBit(int coreIndex)
{
Q_ASSERT(coreIndex >= 0);
Q_ASSERT(coreIndex <= 0xffff);
clearBit(coreIndex * 2 + 1);
}
void QQmlData::flushPendingBinding(QObject *o, QQmlPropertyIndex propertyIndex)
{
QQmlData *data = QQmlData::get(o, false);
if (data && data->hasPendingBindingBit(propertyIndex.coreIndex()))
data->flushPendingBindingImpl(propertyIndex);
}
QT_END_NAMESPACE
#endif // QQMLDATA_P_H