/****************************************************************************
**
** 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 QQMLPROPERTYCACHE_P_H
#define QQMLPROPERTYCACHE_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/qqmlrefcount_p.h>
#include <private/qflagpointer_p.h>
#include "qqmlcleanup_p.h"
#include "qqmlnotifier_p.h"
#include <private/qqmlpropertyindex_p.h>

#include <private/qhashedstring_p.h>
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qvector.h>

#include <private/qv4value_p.h>

#include <limits>

QT_BEGIN_NAMESPACE

class QCryptographicHash;
class QMetaProperty;
class QQmlEngine;
class QJSEngine;
class QQmlPropertyData;
class QMetaObjectBuilder;
class QQmlPropertyCacheMethodArguments;
class QQmlVMEMetaObject;
template <typename T> class QQmlPropertyCacheCreator;
template <typename T> class QQmlPropertyCacheAliasCreator;

// We have this somewhat awful split between RawData and Data so that RawData can be
// used in unions.  In normal code, you should always use Data which initializes RawData
// to an invalid state on construction.
// ### We should be able to remove this split nowadays
class QQmlPropertyRawData
{
public:
    typedef QObjectPrivate::StaticMetaCallFunction StaticMetaCallFunction;

    struct Flags {
        enum Types {
            OtherType            = 0,
            FunctionType         = 1, // Is an invokable
            QObjectDerivedType   = 2, // Property type is a QObject* derived type
            EnumType             = 3, // Property type is an enum
            QListType            = 4, // Property type is a QML list
            QmlBindingType       = 5, // Property type is a QQmlBinding*
            QJSValueType         = 6, // Property type is a QScriptValue
            V4HandleType         = 7, // Property type is a QQmlV4Handle
            VarPropertyType      = 8, // Property type is a "var" property of VMEMO
            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 = 10 };
        unsigned _otherBits       : BitsLeftInFlags; // align to 32 bits

        // Can apply to all properties, except IsFunction
        unsigned isConstant       : 1; // Has CONST flag
        unsigned isWritable       : 1; // Has WRITE function
        unsigned isResettable     : 1; // Has RESET function
        unsigned isAlias          : 1; // Is a QML alias to another property
        unsigned isFinal          : 1; // Has FINAL flag
        unsigned isOverridden     : 1; // Is overridden by a extension property
        unsigned isDirect         : 1; // Exists on a C++ QMetaObject

        unsigned type             : 4; // stores an entry of Types

        // Apply only to IsFunctions
        unsigned isVMEFunction    : 1; // Function was added by QML
        unsigned hasArguments     : 1; // Function takes arguments
        unsigned isSignal         : 1; // Function is a signal
        unsigned isVMESignal      : 1; // Signal was added by QML
        unsigned isV4Function     : 1; // Function takes QQmlV4Function* args
        unsigned isSignalHandler  : 1; // Function is a signal handler
        unsigned isOverload       : 1; // Function is an overload of another function
        unsigned isCloned         : 1; // The function was marked as cloned
        unsigned isConstructor    : 1; // The function was marked is a constructor

        // Internal QQmlPropertyCache flags
        unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved
        unsigned overrideIndexIsProperty: 1;

        inline Flags();
        inline bool operator==(const Flags &other) const;
        inline void copyPropertyTypeFlags(Flags from);
    };

    Flags flags() const { return _flags; }
    void setFlags(Flags f)
    {
        unsigned otherBits = _flags._otherBits;
        _flags = f;
        _flags._otherBits = otherBits;
    }

    bool isValid() const { return coreIndex() != -1; }

    bool isConstant() const { return _flags.isConstant; }
    bool isWritable() const { return _flags.isWritable; }
    void setWritable(bool onoff) { _flags.isWritable = onoff; }
    bool isResettable() const { return _flags.isResettable; }
    bool isAlias() const { return _flags.isAlias; }
    bool isFinal() const { return _flags.isFinal; }
    bool isOverridden() const { return _flags.isOverridden; }
    bool isDirect() const { return _flags.isDirect; }
    bool hasStaticMetaCallFunction() const { return staticMetaCallFunction() != nullptr; }
    bool isFunction() const { return _flags.type == Flags::FunctionType; }
    bool isQObject() const { return _flags.type == Flags::QObjectDerivedType; }
    bool isEnum() const { return _flags.type == Flags::EnumType; }
    bool isQList() const { return _flags.type == Flags::QListType; }
    bool isQmlBinding() const { return _flags.type == Flags::QmlBindingType; }
    bool isQJSValue() const { return _flags.type == Flags::QJSValueType; }
    bool isV4Handle() const { return _flags.type == Flags::V4HandleType; }
    bool isVarProperty() const { return _flags.type == Flags::VarPropertyType; }
    bool isQVariant() const { return _flags.type == Flags::QVariantType; }
    bool isVMEFunction() const { return _flags.isVMEFunction; }
    bool hasArguments() const { return _flags.hasArguments; }
    bool isSignal() const { return _flags.isSignal; }
    bool isVMESignal() const { return _flags.isVMESignal; }
    bool isV4Function() const { return _flags.isV4Function; }
    bool isSignalHandler() const { return _flags.isSignalHandler; }
    bool isOverload() const { return _flags.isOverload; }
    void setOverload(bool onoff) { _flags.isOverload = onoff; }
    bool isCloned() const { return _flags.isCloned; }
    bool isConstructor() const { return _flags.isConstructor; }

    bool hasOverride() const { return overrideIndex() >= 0; }
    bool hasRevision() const { return revision() != 0; }

    bool isFullyResolved() const { return !_flags.notFullyResolved; }

    int propType() const { Q_ASSERT(isFullyResolved()); return _propType; }
    void setPropType(int pt)
    {
        Q_ASSERT(pt >= 0);
        Q_ASSERT(pt <= std::numeric_limits<qint16>::max());
        _propType = quint16(pt);
    }

    int notifyIndex() const { return _notifyIndex; }
    void setNotifyIndex(int idx)
    {
        Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
        Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
        _notifyIndex = qint16(idx);
    }

    bool overrideIndexIsProperty() const { return _flags.overrideIndexIsProperty; }
    void setOverrideIndexIsProperty(bool onoff) { _flags.overrideIndexIsProperty = onoff; }

    int overrideIndex() const { return _overrideIndex; }
    void setOverrideIndex(int idx)
    {
        Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
        Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
        _overrideIndex = qint16(idx);
    }

    int coreIndex() const { return _coreIndex; }
    void setCoreIndex(int idx)
    {
        Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
        Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
        _coreIndex = qint16(idx);
    }

    quint8 revision() const { return _revision; }
    void setRevision(quint8 rev)
    {
        Q_ASSERT(rev <= std::numeric_limits<quint8>::max());
        _revision = quint8(rev);
    }

    /* If a property is a C++ type, then we store the minor
     * version of this type.
     * This is required to resolve property or signal revisions
     * if this property is used as a grouped property.
     *
     * Test.qml
     * property TextEdit someTextEdit: TextEdit {}
     *
     * Test {
     *   someTextEdit.preeditText: "test" //revision 7
     *   someTextEdit.onEditingFinished: console.log("test") //revision 6
     * }
     *
     * To determine if these properties with revisions are available we need
     * the minor version of TextEdit as imported in Test.qml.
     *
     */

    quint8 typeMinorVersion() const { return _typeMinorVersion; }
    void setTypeMinorVersion(quint8 rev)
    {
        Q_ASSERT(rev <= std::numeric_limits<quint8>::max());
        _typeMinorVersion = quint8(rev);
    }

    QQmlPropertyCacheMethodArguments *arguments() const { return _arguments; }
    void setArguments(QQmlPropertyCacheMethodArguments *args) { _arguments = args; }

    int metaObjectOffset() const { return _metaObjectOffset; }
    void setMetaObjectOffset(int off)
    {
        Q_ASSERT(off >= std::numeric_limits<qint16>::min());
        Q_ASSERT(off <= std::numeric_limits<qint16>::max());
        _metaObjectOffset = qint16(off);
    }

    StaticMetaCallFunction staticMetaCallFunction() const { return _staticMetaCallFunction; }
    void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex)
    {
        if (relativePropertyIndex < (1 << Flags::BitsLeftInFlags) - 1) {
            _flags._otherBits = relativePropertyIndex;
            _staticMetaCallFunction = f;
        }
    }
    quint16 relativePropertyIndex() const { Q_ASSERT(hasStaticMetaCallFunction()); return _flags._otherBits; }

private:
    Flags _flags;
    qint16 _coreIndex = 0;
    quint16 _propType = 0;

    // The notify index is in the range returned by QObjectPrivate::signalIndex().
    // This is different from QMetaMethod::methodIndex().
    qint16 _notifyIndex = 0;
    qint16 _overrideIndex = 0;

    quint8 _revision = 0;
    quint8 _typeMinorVersion = 0;
    qint16 _metaObjectOffset = 0;

    QQmlPropertyCacheMethodArguments *_arguments = nullptr;
    StaticMetaCallFunction _staticMetaCallFunction = nullptr;

    friend class QQmlPropertyData;
    friend class QQmlPropertyCache;
};

#if QT_POINTER_SIZE == 4
Q_STATIC_ASSERT(sizeof(QQmlPropertyRawData) == 24);
#else // QT_POINTER_SIZE == 8
Q_STATIC_ASSERT(sizeof(QQmlPropertyRawData) == 32);
#endif

class QQmlPropertyData : public QQmlPropertyRawData
{
public:
    enum WriteFlag {
        BypassInterceptor = 0x01,
        DontRemoveBinding = 0x02,
        RemoveBindingOnAliasWrite = 0x04
    };
    Q_DECLARE_FLAGS(WriteFlags, WriteFlag)

    inline QQmlPropertyData();
    inline QQmlPropertyData(const QQmlPropertyRawData &);

    inline bool operator==(const QQmlPropertyRawData &);

    static Flags flagsForProperty(const QMetaProperty &);
    void load(const QMetaProperty &);
    void load(const QMetaMethod &);
    QString name(QObject *) const;
    QString name(const QMetaObject *) const;

    void markAsOverrideOf(QQmlPropertyData *predecessor);

    inline void readProperty(QObject *target, void *property) const
    {
        void *args[] = { property, nullptr };
        readPropertyWithArgs(target, args);
    }

    inline void readPropertyWithArgs(QObject *target, void *args[]) const
    {
        if (hasStaticMetaCallFunction())
            staticMetaCallFunction()(target, QMetaObject::ReadProperty, relativePropertyIndex(), args);
        else if (isDirect())
            target->qt_metacall(QMetaObject::ReadProperty, coreIndex(), args);
        else
            QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex(), args);
    }

    bool writeProperty(QObject *target, void *value, WriteFlags flags) const
    {
        int status = -1;
        void *argv[] = { value, nullptr, &status, &flags };
        if (flags.testFlag(BypassInterceptor) && hasStaticMetaCallFunction())
            staticMetaCallFunction()(target, QMetaObject::WriteProperty, relativePropertyIndex(), argv);
        else if (flags.testFlag(BypassInterceptor) && isDirect())
            target->qt_metacall(QMetaObject::WriteProperty, coreIndex(), argv);
        else
            QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex(), argv);
        return true;
    }

    static Flags defaultSignalFlags()
    {
        Flags f;
        f.isSignal = true;
        f.type = Flags::FunctionType;
        f.isVMESignal = true;
        return f;
    }

    static Flags defaultSlotFlags()
    {
        Flags f;
        f.type = Flags::FunctionType;
        f.isVMEFunction = true;
        return f;
    }

private:
    friend class QQmlPropertyCache;
    void lazyLoad(const QMetaProperty &);
    void lazyLoad(const QMetaMethod &);
    bool notFullyResolved() const { return _flags.notFullyResolved; }
};

struct QQmlEnumValue
{
    QQmlEnumValue() {}
    QQmlEnumValue(const QString &n, int v) : namedValue(n), value(v) {}
    QString namedValue;
    int value = -1;
};

struct QQmlEnumData
{
    QString name;
    QVector<QQmlEnumValue> values;
};

class QQmlPropertyCacheMethodArguments;
class Q_QML_PRIVATE_EXPORT QQmlPropertyCache : public QQmlRefCount
{
public:
    QQmlPropertyCache();
    QQmlPropertyCache(const QMetaObject *, int metaObjectRevision = 0);
    ~QQmlPropertyCache() override;

    void update(const QMetaObject *);
    void invalidate(const QMetaObject *);
    // Used by qmlpuppet. Remove as soon Creator requires Qt 5.5.
    void invalidate(void *, const QMetaObject *mo) { invalidate(mo); }

    QQmlPropertyCache *copy();

    QQmlPropertyCache *copyAndAppend(const QMetaObject *,
                QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyData::Flags(),
                QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(),
                QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags());
    QQmlPropertyCache *copyAndAppend(const QMetaObject *, int typeMinorVersion,
                QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyData::Flags(),
                QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(),
                QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags());

    QQmlPropertyCache *copyAndReserve(int propertyCount,
                                      int methodCount, int signalCount, int enumCount);
    void appendProperty(const QString &, QQmlPropertyRawData::Flags flags, int coreIndex,
                        int propType, int revision, int notifyIndex);
    void appendSignal(const QString &, QQmlPropertyRawData::Flags, int coreIndex,
                      const int *types = nullptr, const QList<QByteArray> &names = QList<QByteArray>());
    void appendMethod(const QString &, QQmlPropertyData::Flags flags, int coreIndex,
                      const QList<QByteArray> &names = QList<QByteArray>());
    void appendEnum(const QString &, const QVector<QQmlEnumValue> &);

    const QMetaObject *metaObject() const;
    const QMetaObject *createMetaObject();
    const QMetaObject *firstCppMetaObject() const;

    template<typename K>
    QQmlPropertyData *property(const K &key, QObject *object, QQmlContextData *context) const
    {
        return findProperty(stringCache.find(key), object, context);
    }

    QQmlPropertyData *property(int) const;
    QQmlPropertyData *method(int) const;
    QQmlPropertyData *signal(int index) const;
    QQmlEnumData *qmlEnum(int) const;
    int methodIndexToSignalIndex(int) const;

    QString defaultPropertyName() const;
    QQmlPropertyData *defaultProperty() const;
    QQmlPropertyCache *parent() const;
    // is used by the Qml Designer
    void setParent(QQmlPropertyCache *newParent);

    inline QQmlPropertyData *overrideData(QQmlPropertyData *) const;
    inline bool isAllowedInRevision(QQmlPropertyData *) const;

    static QQmlPropertyData *property(QJSEngine *, QObject *, const QStringRef &,
                                              QQmlContextData *, QQmlPropertyData &);
    static QQmlPropertyData *property(QJSEngine *, QObject *, const QLatin1String &,
                                      QQmlContextData *, QQmlPropertyData &);
    static QQmlPropertyData *property(QJSEngine *, QObject *, const QV4::String *,
                                              QQmlContextData *, QQmlPropertyData &);

    static QQmlPropertyData *property(QJSEngine *engine, QObject *obj, const QString &name,
                                      QQmlContextData *context, QQmlPropertyData &local)
    {
        return property(engine, obj, QStringRef(&name), context, local);
    }

    //see QMetaObjectPrivate::originalClone
    int originalClone(int index);
    static int originalClone(QObject *, int index);

    QList<QByteArray> signalParameterNames(int index) const;
    static QString signalParameterStringForJS(QV4::ExecutionEngine *engine, const QList<QByteArray> &parameterNameList, QString *errorString = nullptr);

    const char *className() const;

    inline int propertyCount() const;
    inline int propertyOffset() const;
    inline int methodCount() const;
    inline int methodOffset() const;
    inline int signalCount() const;
    inline int signalOffset() const;
    inline int qmlEnumCount() const;

    static bool isDynamicMetaObject(const QMetaObject *);

    void toMetaObjectBuilder(QMetaObjectBuilder &);

    inline bool callJSFactoryMethod(QObject *object, void **args) const;

    static bool determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount, int *stringCount);
    static bool addToHash(QCryptographicHash &hash, const QMetaObject &mo);

    QByteArray checksum(bool *ok);
private:
    friend class QQmlEnginePrivate;
    friend class QQmlCompiler;
    template <typename T> friend class QQmlPropertyCacheCreator;
    template <typename T> friend class QQmlPropertyCacheAliasCreator;
    friend class QQmlComponentAndAliasResolver;
    friend class QQmlMetaObject;
    friend struct QQmlMetaTypeData;

    inline QQmlPropertyCache *copy(int reserve);

    void append(const QMetaObject *, int typeMinorVersion,
                QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyRawData::Flags(),
                QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(),
                QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags());

    QQmlPropertyCacheMethodArguments *createArgumentsObject(int count, const QList<QByteArray> &names);

    typedef QVector<QQmlPropertyData> IndexCache;
    typedef QStringMultiHash<QPair<int, QQmlPropertyData *> > StringCache;
    typedef QVector<int> AllowedRevisionCache;

    QQmlPropertyData *findProperty(StringCache::ConstIterator it, QObject *, QQmlContextData *) const;
    QQmlPropertyData *findProperty(StringCache::ConstIterator it, const QQmlVMEMetaObject *, QQmlContextData *) const;

    QQmlPropertyData *ensureResolved(QQmlPropertyData*) const;

    Q_NEVER_INLINE void resolve(QQmlPropertyData *) const;
    void updateRecur(const QMetaObject *);

    template<typename K>
    QQmlPropertyData *findNamedProperty(const K &key) const
    {
        StringCache::mapped_type *it = stringCache.value(key);
        return it ? it->second : 0;
    }

    template<typename K>
    void setNamedProperty(const K &key, int index, QQmlPropertyData *data, bool isOverride)
    {
        stringCache.insert(key, qMakePair(index, data));
        _hasPropertyOverrides |= isOverride;
    }

private:
    QQmlPropertyCache *_parent;
    int propertyIndexCacheStart;
    int methodIndexCacheStart;
    int signalHandlerIndexCacheStart;

    IndexCache propertyIndexCache;
    IndexCache methodIndexCache;
    IndexCache signalHandlerIndexCache;
    StringCache stringCache;
    AllowedRevisionCache allowedRevisionCache;
    QVector<QQmlEnumData> enumCache;

    bool _hasPropertyOverrides : 1;
    bool _ownMetaObject : 1;
    const QMetaObject *_metaObject;
    QByteArray _dynamicClassName;
    QByteArray _dynamicStringData;
    QString _defaultPropertyName;
    QQmlPropertyCacheMethodArguments *argumentsCache;
    int _jsFactoryMethodIndex;
    QByteArray _checksum;
};

typedef QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCachePtr;

// QQmlMetaObject serves as a wrapper around either QMetaObject or QQmlPropertyCache.
// This is necessary as we delay creation of QMetaObject for synthesized QObjects, but
// we don't want to needlessly generate QQmlPropertyCaches every time we encounter a
// QObject type used in assignment or when we don't have a QQmlEngine etc.
//
// This class does NOT reference the propertycache.
class QQmlEnginePrivate;
class Q_QML_EXPORT QQmlMetaObject
{
public:
    typedef QVarLengthArray<int, 9> ArgTypeStorage;

    inline QQmlMetaObject();
    inline QQmlMetaObject(QObject *);
    inline QQmlMetaObject(const QMetaObject *);
    inline QQmlMetaObject(QQmlPropertyCache *);
    inline QQmlMetaObject(const QQmlMetaObject &);

    inline QQmlMetaObject &operator=(const QQmlMetaObject &);

    inline bool isNull() const;

    inline const char *className() const;
    inline int propertyCount() const;

    inline bool hasMetaObject() const;
    inline const QMetaObject *metaObject() const;

    QQmlPropertyCache *propertyCache(QQmlEnginePrivate *) const;

    int methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const;
    int *methodParameterTypes(int index, ArgTypeStorage *argStorage,
                              QByteArray *unknownTypeError) const;

    static bool canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to);

    // static_metacall (on Gadgets) doesn't call the base implementation and therefore
    // we need a helper to find the correct meta object and property/method index.
    static void resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index);

protected:
    QBiPointer<QQmlPropertyCache, const QMetaObject> _m;
    int *methodParameterTypes(const QMetaMethod &method, ArgTypeStorage *argStorage,
                              QByteArray *unknownTypeError) const;

};

class QQmlObjectOrGadget: public QQmlMetaObject
{
public:
    QQmlObjectOrGadget(QObject *obj)
        : QQmlMetaObject(obj),
          ptr(obj)
    {}
    QQmlObjectOrGadget(QQmlPropertyCache *propertyCache, void *gadget)
        : QQmlMetaObject(propertyCache)
        , ptr(gadget)
    {}

    void metacall(QMetaObject::Call type, int index, void **argv) const;

private:
    QBiPointer<QObject, void> ptr;

protected:
    QQmlObjectOrGadget(const QMetaObject* metaObject)
        : QQmlMetaObject(metaObject)
    {}

};

class QQmlStaticMetaObject : public QQmlObjectOrGadget {
public:
    QQmlStaticMetaObject(const QMetaObject* metaObject)
        : QQmlObjectOrGadget(metaObject)
    {}
    int *constructorParameterTypes(int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const;
};

QQmlPropertyRawData::Flags::Flags()
    : _otherBits(0)
    , isConstant(false)
    , isWritable(false)
    , isResettable(false)
    , isAlias(false)
    , isFinal(false)
    , isOverridden(false)
    , isDirect(false)
    , type(OtherType)
    , isVMEFunction(false)
    , hasArguments(false)
    , isSignal(false)
    , isVMESignal(false)
    , isV4Function(false)
    , isSignalHandler(false)
    , isOverload(false)
    , isCloned(false)
    , isConstructor(false)
    , notFullyResolved(false)
    , overrideIndexIsProperty(false)
{}

bool QQmlPropertyRawData::Flags::operator==(const QQmlPropertyRawData::Flags &other) const
{
    return isConstant == other.isConstant &&
            isWritable == other.isWritable &&
            isResettable == other.isResettable &&
            isAlias == other.isAlias &&
            isFinal == other.isFinal &&
            isOverridden == other.isOverridden &&
            type == other.type &&
            isVMEFunction == other.isVMEFunction &&
            hasArguments == other.hasArguments &&
            isSignal == other.isSignal &&
            isVMESignal == other.isVMESignal &&
            isV4Function == other.isV4Function &&
            isSignalHandler == other.isSignalHandler &&
            isOverload == other.isOverload &&
            isCloned == other.isCloned &&
            isConstructor == other.isConstructor &&
            notFullyResolved == other.notFullyResolved &&
            overrideIndexIsProperty == other.overrideIndexIsProperty;
}

void QQmlPropertyRawData::Flags::copyPropertyTypeFlags(QQmlPropertyRawData::Flags from)
{
    switch (from.type) {
    case QObjectDerivedType:
    case EnumType:
    case QListType:
    case QmlBindingType:
    case QJSValueType:
    case V4HandleType:
    case QVariantType:
        type = from.type;
    }
}

QQmlPropertyData::QQmlPropertyData()
{
    setCoreIndex(-1);
    setPropType(0);
    setNotifyIndex(-1);
    setOverrideIndex(-1);
    setRevision(0);
    setMetaObjectOffset(-1);
    setArguments(nullptr);
    trySetStaticMetaCallFunction(nullptr, 0);
}

QQmlPropertyData::QQmlPropertyData(const QQmlPropertyRawData &d)
{
    *(static_cast<QQmlPropertyRawData *>(this)) = d;
}

bool QQmlPropertyData::operator==(const QQmlPropertyRawData &other)
{
    return flags() == other.flags() &&
           propType() == other.propType() &&
           coreIndex() == other.coreIndex() &&
           notifyIndex() == other.notifyIndex() &&
           revision() == other.revision();
}

inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const
{
    if (p && Q_UNLIKELY(p->notFullyResolved()))
        resolve(p);

    return p;
}

// Returns this property cache's metaObject.  May be null if it hasn't been created yet.
inline const QMetaObject *QQmlPropertyCache::metaObject() const
{
    return _metaObject;
}

// Returns the first C++ type's QMetaObject - that is, the first QMetaObject not created by
// QML
inline const QMetaObject *QQmlPropertyCache::firstCppMetaObject() const
{
    while (_parent && (_metaObject == nullptr || _ownMetaObject))
        return _parent->firstCppMetaObject();
    return _metaObject;
}

inline QQmlPropertyData *QQmlPropertyCache::property(int index) const
{
    if (index < 0 || index >= (propertyIndexCacheStart + propertyIndexCache.count()))
        return nullptr;

    if (index < propertyIndexCacheStart)
        return _parent->property(index);

    QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&propertyIndexCache.at(index - propertyIndexCacheStart));
    return ensureResolved(rv);
}

inline QQmlPropertyData *QQmlPropertyCache::method(int index) const
{
    if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count()))
        return nullptr;

    if (index < methodIndexCacheStart)
        return _parent->method(index);

    QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - methodIndexCacheStart));
    return ensureResolved(rv);
}

/*! \internal
    \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
    This is different from QMetaMethod::methodIndex().
*/
inline QQmlPropertyData *QQmlPropertyCache::signal(int index) const
{
    if (index < 0 || index >= (signalHandlerIndexCacheStart + signalHandlerIndexCache.count()))
        return nullptr;

    if (index < signalHandlerIndexCacheStart)
        return _parent->signal(index);

    QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - signalHandlerIndexCacheStart));
    Q_ASSERT(rv->isSignal() || rv->coreIndex() == -1);
    return ensureResolved(rv);
}

inline QQmlEnumData *QQmlPropertyCache::qmlEnum(int index) const
{
    if (index < 0 || index >= enumCache.count())
        return nullptr;

    return const_cast<QQmlEnumData *>(&enumCache.at(index));
}

inline int QQmlPropertyCache::methodIndexToSignalIndex(int index) const
{
    if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count()))
        return index;

    if (index < methodIndexCacheStart)
        return _parent->methodIndexToSignalIndex(index);

    return index - methodIndexCacheStart + signalHandlerIndexCacheStart;
}

// Returns the name of the default property for this cache
inline QString QQmlPropertyCache::defaultPropertyName() const
{
    return _defaultPropertyName;
}

inline QQmlPropertyCache *QQmlPropertyCache::parent() const
{
    return _parent;
}

QQmlPropertyData *
QQmlPropertyCache::overrideData(QQmlPropertyData *data) const
{
    if (!data->hasOverride())
        return nullptr;

    if (data->overrideIndexIsProperty())
        return property(data->overrideIndex());
    else
        return method(data->overrideIndex());
}

bool QQmlPropertyCache::isAllowedInRevision(QQmlPropertyData *data) const
{
    return (data->metaObjectOffset() == -1 && data->revision() == 0) ||
           (allowedRevisionCache[data->metaObjectOffset()] >= data->revision());
}

int QQmlPropertyCache::propertyCount() const
{
    return propertyIndexCacheStart + propertyIndexCache.count();
}

int QQmlPropertyCache::propertyOffset() const
{
    return propertyIndexCacheStart;
}

int QQmlPropertyCache::methodCount() const
{
    return methodIndexCacheStart + methodIndexCache.count();
}

int QQmlPropertyCache::methodOffset() const
{
    return methodIndexCacheStart;
}

int QQmlPropertyCache::signalCount() const
{
    return signalHandlerIndexCacheStart + signalHandlerIndexCache.count();
}

int QQmlPropertyCache::signalOffset() const
{
    return signalHandlerIndexCacheStart;
}

int QQmlPropertyCache::qmlEnumCount() const
{
    return enumCache.count();
}

bool QQmlPropertyCache::callJSFactoryMethod(QObject *object, void **args) const
{
    if (_jsFactoryMethodIndex != -1) {
        _metaObject->d.static_metacall(object, QMetaObject::InvokeMetaMethod, _jsFactoryMethodIndex, args);
        return true;
    }
    if (_parent)
        return _parent->callJSFactoryMethod(object, args);
    return false;
}

QQmlMetaObject::QQmlMetaObject()
{
}

QQmlMetaObject::QQmlMetaObject(QObject *o)
{
    if (o) {
        QQmlData *ddata = QQmlData::get(o, false);
        if (ddata && ddata->propertyCache) _m = ddata->propertyCache;
        else _m = o->metaObject();
    }
}

QQmlMetaObject::QQmlMetaObject(const QMetaObject *m)
: _m(m)
{
}

QQmlMetaObject::QQmlMetaObject(QQmlPropertyCache *m)
: _m(m)
{
}

QQmlMetaObject::QQmlMetaObject(const QQmlMetaObject &o)
: _m(o._m)
{
}

QQmlMetaObject &QQmlMetaObject::operator=(const QQmlMetaObject &o)
{
    _m = o._m;
    return *this;
}

bool QQmlMetaObject::isNull() const
{
    return _m.isNull();
}

const char *QQmlMetaObject::className() const
{
    if (_m.isNull()) {
        return nullptr;
    } else if (_m.isT1()) {
        return _m.asT1()->className();
    } else {
        return _m.asT2()->className();
    }
}

int QQmlMetaObject::propertyCount() const
{
    if (_m.isNull()) {
        return 0;
    } else if (_m.isT1()) {
        return _m.asT1()->propertyCount();
    } else {
        return _m.asT2()->propertyCount();
    }
}

bool QQmlMetaObject::hasMetaObject() const
{
    return _m.isT2() || (!_m.isNull() && _m.asT1()->metaObject());
}

const QMetaObject *QQmlMetaObject::metaObject() const
{
    if (_m.isNull()) return nullptr;
    if (_m.isT1()) return _m.asT1()->createMetaObject();
    else return _m.asT2();
}

class QQmlPropertyCacheVector
{
public:
    QQmlPropertyCacheVector() {}
    QQmlPropertyCacheVector(QQmlPropertyCacheVector &&other)
        : data(std::move(other.data)) {}
    QQmlPropertyCacheVector &operator=(QQmlPropertyCacheVector &&other) {
        QVector<QFlagPointer<QQmlPropertyCache>> moved(std::move(other.data));
        data.swap(moved);
        return *this;
    }

    ~QQmlPropertyCacheVector() { clear(); }
    void resize(int size) { return data.resize(size); }
    int count() const { return data.count(); }
    void clear()
    {
        for (int i = 0; i < data.count(); ++i) {
            if (QQmlPropertyCache *cache = data.at(i).data())
                cache->release();
        }
        data.clear();
    }

    void append(QQmlPropertyCache *cache) { cache->addref(); data.append(cache); }
    QQmlPropertyCache *at(int index) const { return data.at(index).data(); }
    void set(int index, const QQmlRefPointer<QQmlPropertyCache> &replacement) {
        if (QQmlPropertyCache *oldCache = data.at(index).data()) {
            if (replacement.data() == oldCache)
                return;
            oldCache->release();
        }
        data[index] = replacement.data();
        replacement->addref();
    }

    void setNeedsVMEMetaObject(int index) { data[index].setFlag(); }
    bool needsVMEMetaObject(int index) const { return data.at(index).flag(); }
private:
    Q_DISABLE_COPY(QQmlPropertyCacheVector)
    QVector<QFlagPointer<QQmlPropertyCache>> data;
};

Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyData::WriteFlags)

QT_END_NAMESPACE

#endif // QQMLPROPERTYCACHE_P_H
