413 lines
14 KiB
C++
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
|