// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default

#include "qmetaobjectbuilder_p.h"

#include "qobject_p.h"
#include "qmetaobject_p.h"

#include <QtCore/qstring.h>

#include <vector>
#include <stdlib.h>

QT_BEGIN_NAMESPACE

using namespace Qt::StringLiterals;

/*!
    \class QMetaObjectBuilder
    \inmodule QtCore
    \internal
    \brief The QMetaObjectBuilder class supports building QMetaObject objects at runtime.

*/

/*!
    \enum QMetaObjectBuilder::AddMember
    This enum defines which members of QMetaObject should be copied by QMetaObjectBuilder::addMetaObject()

    \value ClassName Add the class name.
    \value SuperClass Add the super class.
    \value Methods Add methods that aren't signals or slots.
    \value Signals Add signals.
    \value Slots Add slots.
    \value Constructors Add constructors.
    \value Properties Add properties.
    \value Enumerators Add enumerators.
    \value ClassInfos Add items of class information.
    \value RelatedMetaObjects Add related meta objects.
    \value StaticMetacall Add the static metacall function.
    \value PublicMethods Add public methods (ignored for signals).
    \value ProtectedMethods Add protected methods (ignored for signals).
    \value PrivateMethods All private methods (ignored for signals).
    \value AllMembers Add all members.
    \value AllPrimaryMembers Add everything except the class name, super class, and static metacall function.
*/

static bool isBuiltinType(QByteArrayView type)
{
    int id = QMetaType::fromName(type).rawId();
    if (!id && !type.isEmpty() && type != "void")
        return false;
    return (id < QMetaType::User);
}

// copied from qmetaobject.cpp
[[maybe_unused]] static inline const QMetaObjectPrivate *qmobPriv(const uint* data)
{ return reinterpret_cast<const QMetaObjectPrivate*>(data); }

class QMetaMethodBuilderPrivate
{
public:
    QMetaMethodBuilderPrivate
            (QMetaMethod::MethodType _methodType,
             const QByteArray& _signature,
             const QByteArray& _returnType = QByteArray("void"),
             QMetaMethod::Access _access = QMetaMethod::Public,
             int _revision = 0)
        : signature(QMetaObject::normalizedSignature(_signature.constData())),
          returnType(QMetaObject::normalizedType(_returnType)),
          attributes(((int)_access) | (((int)_methodType) << 2)),
          revision(_revision)
    {
        Q_ASSERT((_methodType == QMetaMethod::Constructor) == returnType.isNull());
    }

    QByteArray signature;
    QByteArray returnType;
    QList<QByteArray> parameterNames;
    QByteArray tag;
    int attributes;
    int revision;

    QMetaMethod::MethodType methodType() const
    {
        return (QMetaMethod::MethodType)((attributes & MethodTypeMask) >> 2);
    }

    QMetaMethod::Access access() const
    {
        return (QMetaMethod::Access)(attributes & AccessMask);
    }

    void setAccess(QMetaMethod::Access value)
    {
        attributes = ((attributes & ~AccessMask) | (int)value);
    }

    QList<QByteArrayView> parameterTypes() const
    {
        QVarLengthArray<QByteArrayView, 10> typeNames;
        QMetaObjectPrivate::parameterTypeNamesFromSignature(signature, typeNames);
        QList<QByteArrayView> list;
        list.reserve(typeNames.size());
        for (auto n : typeNames)
            list.emplace_back(n);
        return list;
    }

    int parameterCount() const
    {
        return parameterTypes().size();
    }

    QByteArray name() const
    {
        return signature.left(qMax(signature.indexOf('('), 0));
    }
};
Q_DECLARE_TYPEINFO(QMetaMethodBuilderPrivate, Q_RELOCATABLE_TYPE);

class QMetaPropertyBuilderPrivate
{
public:
    QMetaPropertyBuilderPrivate
            (const QByteArray& _name, const QByteArray& _type, QMetaType _metaType, int notifierIdx=-1,
             int _revision = 0)
        : name(_name),
          type(QMetaObject::normalizedType(_type.constData())),
          metaType(_metaType),
          flags(Readable | Writable | Scriptable), notifySignal(notifierIdx),
          revision(_revision)
    {

    }

    QByteArray name;
    QByteArray type;
    QMetaType metaType;
    int flags;
    int notifySignal;
    int revision;

    bool flag(int f) const
    {
        return ((flags & f) != 0);
    }

    void setFlag(int f, bool value)
    {
        if (value)
            flags |= f;
        else
            flags &= ~f;
    }
};
Q_DECLARE_TYPEINFO(QMetaPropertyBuilderPrivate, Q_RELOCATABLE_TYPE);

class QMetaEnumBuilderPrivate
{
public:
    QMetaEnumBuilderPrivate(const QByteArray &_name)
        : name(_name), enumName(_name)
    {
    }

    QByteArray name;
    QByteArray enumName;
    QMetaType metaType;
    QList<QByteArray> keys;
    QList<quint64> values;
    QFlags<EnumFlags> flags = {};

    int addKey(const QByteArray &name, quint64 value)
    {
        int index = keys.size();
        keys += name;
        values += value;
        return index;
    }
};
Q_DECLARE_TYPEINFO(QMetaEnumBuilderPrivate, Q_RELOCATABLE_TYPE);

class QMetaObjectBuilderPrivate
{
public:
    QMetaObjectBuilderPrivate()
        : flags(0)
    {
        superClass = &QObject::staticMetaObject;
        staticMetacallFunction = nullptr;
    }

    QByteArray className;
    const QMetaObject *superClass;
    QMetaObjectBuilder::StaticMetacallFunction staticMetacallFunction;
    std::vector<QMetaMethodBuilderPrivate> signalVec; // QMetaMethod::Signal type
    std::vector<QMetaMethodBuilderPrivate> methods; // QMetaMethod::Method and Slot types
    std::vector<QMetaMethodBuilderPrivate> constructors;
    std::vector<QMetaPropertyBuilderPrivate> properties;
    QList<QByteArray> classInfoNames;
    QList<QByteArray> classInfoValues;
    std::vector<QMetaEnumBuilderPrivate> enumerators;
    QList<const QMetaObject *> relatedMetaObjects;
    MetaObjectFlags flags;
};

/*!
    Constructs a new QMetaObjectBuilder.
*/
QMetaObjectBuilder::QMetaObjectBuilder()
{
    d = new QMetaObjectBuilderPrivate();
}

/*!
    Constructs a new QMetaObjectBuilder which is a copy of the
    meta object information in \a prototype.  Note: the super class
    contents for \a prototype are not copied, only the immediate
    class that is defined by \a prototype.

    The \a members parameter indicates which members of \a prototype
    should be added.  The default is AllMembers.

    \sa addMetaObject()
*/
QMetaObjectBuilder::QMetaObjectBuilder(const QMetaObject *prototype,
                                       QMetaObjectBuilder::AddMembers members)
{
    d = new QMetaObjectBuilderPrivate();
    addMetaObject(prototype, members);
}

/*!
    Destroys this meta object builder.
*/
QMetaObjectBuilder::~QMetaObjectBuilder()
{
    delete d;
}

/*!
    Returns the name of the class being constructed by this
    meta object builder.  The default value is an empty QByteArray.

    \sa setClassName(), superClass()
*/
QByteArray QMetaObjectBuilder::className() const
{
    return d->className;
}

/*!
    Sets the \a name of the class being constructed by this
    meta object builder.

    \sa className(), setSuperClass()
*/
void QMetaObjectBuilder::setClassName(const QByteArray &name)
{
    d->className = name;
}

/*!
    Returns the superclass meta object of the class being constructed
    by this meta object builder.  The default value is the meta object
    for QObject.

    \sa setSuperClass(), className()
*/
const QMetaObject *QMetaObjectBuilder::superClass() const
{
    return d->superClass;
}

/*!
    Sets the superclass meta object of the class being constructed
    by this meta object builder to \a meta.  The \a meta parameter
    must not be null.

    \sa superClass(), setClassName()
*/
void QMetaObjectBuilder::setSuperClass(const QMetaObject *meta)
{
    Q_ASSERT(meta);
    d->superClass = meta;
}

/*!
    Returns the flags of the class being constructed by this meta object
    builder.

    \sa setFlags()
*/
MetaObjectFlags QMetaObjectBuilder::flags() const
{
    return d->flags;
}

/*!
    Sets the \a flags of the class being constructed by this meta object
    builder.

    \sa flags()
*/
void QMetaObjectBuilder::setFlags(MetaObjectFlags flags)
{
    d->flags = flags;
}

/*!
    Returns the number of methods in this class, excluding the number
    of methods in the base class.  These include signals and slots
    as well as normal member functions.

    \sa addMethod(), method(), removeMethod(), indexOfMethod()
*/
int QMetaObjectBuilder::methodCount() const
{
    return int(d->signalVec.size() + d->methods.size());
}

/*!
    Returns the number of constructors in this class.

    \sa addConstructor(), constructor(), removeConstructor(), indexOfConstructor()
*/
int QMetaObjectBuilder::constructorCount() const
{
    return int(d->constructors.size());
}

/*!
    Returns the number of properties in this class, excluding the number
    of properties in the base class.

    \sa addProperty(), property(), removeProperty(), indexOfProperty()
*/
int QMetaObjectBuilder::propertyCount() const
{
    return int(d->properties.size());
}

/*!
    Returns the number of enumerators in this class, excluding the
    number of enumerators in the base class.

    \sa addEnumerator(), enumerator(), removeEnumerator()
    \sa indexOfEnumerator()
*/
int QMetaObjectBuilder::enumeratorCount() const
{
    return int(d->enumerators.size());
}

/*!
    Returns the number of items of class information in this class,
    exclusing the number of items of class information in the base class.

    \sa addClassInfo(), classInfoName(), classInfoValue(), removeClassInfo()
    \sa indexOfClassInfo()
*/
int QMetaObjectBuilder::classInfoCount() const
{
    return d->classInfoNames.size();
}

/*!
    Returns the number of related meta objects that are associated
    with this class.

    Related meta objects are used when resolving the enumerated type
    associated with a property, where the enumerated type is in a
    different class from the property.

    \sa addRelatedMetaObject(), relatedMetaObject()
    \sa removeRelatedMetaObject()
*/
int QMetaObjectBuilder::relatedMetaObjectCount() const
{
    return d->relatedMetaObjects.size();
}

/*!
    Adds a new public method to this class with the specified \a signature.
    Returns an object that can be used to adjust the other attributes
    of the method.  The \a signature will be normalized before it is
    added to the class.

    \sa method(), methodCount(), removeMethod(), indexOfMethod()
*/
QMetaMethodBuilder QMetaObjectBuilder::addMethod(const QByteArray &signature)
{
    int index = int(d->methods.size());
    d->methods.push_back(QMetaMethodBuilderPrivate(QMetaMethod::Method, signature));
    return QMetaMethodBuilder(this, index, QMetaMethod::Method);
}

/*!
    Adds a new public method to this class with the specified
    \a signature and \a returnType.  Returns an object that can be
    used to adjust the other attributes of the method.  The \a signature
    and \a returnType will be normalized before they are added to
    the class.

    \sa method(), methodCount(), removeMethod(), indexOfMethod()
*/
QMetaMethodBuilder QMetaObjectBuilder::addMethod(const QByteArray &signature,
                                                 const QByteArray &returnType)
{
    int index = int(d->methods.size());
    d->methods.push_back(QMetaMethodBuilderPrivate(QMetaMethod::Method, signature, returnType));
    return QMetaMethodBuilder(this, index, QMetaMethod::Method);
}

/*!
    Adds a new public method to this class that has the same information as
    \a prototype.  This is used to clone the methods of an existing
    QMetaObject.  Returns an object that can be used to adjust the
    attributes of the method.

    This function will detect if \a prototype is an ordinary method,
    signal, slot, or constructor and act accordingly.

    \sa method(), methodCount(), removeMethod(), indexOfMethod()
*/
QMetaMethodBuilder QMetaObjectBuilder::addMethod(const QMetaMethod &prototype)
{
    QMetaMethodBuilder method;
    switch (prototype.methodType()) {
    case QMetaMethod::Method:
        method = addMethod(prototype.methodSignature());
        break;
    case QMetaMethod::Signal:
        method = addSignal(prototype.methodSignature());
        break;
    case QMetaMethod::Slot:
        method = addSlot(prototype.methodSignature());
        break;
    case QMetaMethod::Constructor:
        method = addConstructor(prototype.methodSignature());
        break;
    }
    method.setReturnType(prototype.typeName());
    method.setParameterNames(prototype.parameterNames());
    method.setTag(prototype.tag());
    method.setAccess(prototype.access());
    method.setAttributes(prototype.attributes());
    method.setRevision(prototype.revision());
    method.setConst(prototype.isConst());
    return method;
}

/*!
    Adds a new public slot to this class with the specified \a signature.
    Returns an object that can be used to adjust the other attributes
    of the slot.  The \a signature will be normalized before it is
    added to the class.

    \sa addMethod(), addSignal(), indexOfSlot()
*/
QMetaMethodBuilder QMetaObjectBuilder::addSlot(const QByteArray &signature)
{
    int index = int(d->methods.size());
    d->methods.push_back(QMetaMethodBuilderPrivate(QMetaMethod::Slot, signature));
    return QMetaMethodBuilder(this, index, QMetaMethod::Slot);
}

/*!
    Adds a new signal to this class with the specified \a signature.
    Returns an object that can be used to adjust the other attributes
    of the signal.  The \a signature will be normalized before it is
    added to the class.

    \sa addMethod(), addSlot(), indexOfSignal()
*/
QMetaMethodBuilder QMetaObjectBuilder::addSignal(const QByteArray &signature)
{
    int index = int(d->signalVec.size());
    d->signalVec.emplace_back(QMetaMethod::Signal, signature, "void"_ba, QMetaMethod::Public);
    return QMetaMethodBuilder(this, index, QMetaMethod::Signal);
}

/*!
    Adds a new constructor to this class with the specified \a signature.
    Returns an object that can be used to adjust the other attributes
    of the constructor.  The \a signature will be normalized before it is
    added to the class.

    \sa constructor(), constructorCount(), removeConstructor()
    \sa indexOfConstructor()
*/
QMetaMethodBuilder QMetaObjectBuilder::addConstructor(const QByteArray &signature)
{
    int index = int(d->constructors.size());
    d->constructors.push_back(QMetaMethodBuilderPrivate(QMetaMethod::Constructor, signature,
                                                        /*returnType=*/QByteArray()));
    return QMetaMethodBuilder(this, index, QMetaMethod::Constructor);
}

/*!
    Adds a new constructor to this class that has the same information as
    \a prototype.  This is used to clone the constructors of an existing
    QMetaObject.  Returns an object that can be used to adjust the
    attributes of the constructor.

    This function requires that \a prototype be a constructor.

    \sa constructor(), constructorCount(), removeConstructor()
    \sa indexOfConstructor()
*/
QMetaMethodBuilder QMetaObjectBuilder::addConstructor(const QMetaMethod &prototype)
{
    Q_ASSERT(prototype.methodType() == QMetaMethod::Constructor);
    QMetaMethodBuilder ctor = addConstructor(prototype.methodSignature());
    ctor.setReturnType(prototype.typeName());
    ctor.setParameterNames(prototype.parameterNames());
    ctor.setTag(prototype.tag());
    ctor.setAccess(prototype.access());
    ctor.setAttributes(prototype.attributes());
    return ctor;
}

/*!
    Adds a new readable/writable property to this class with the
    specified \a name and \a type.  Returns an object that can be used
    to adjust the other attributes of the property.  The \a type will
    be normalized before it is added to the class. \a notifierId will
    be registered as the property's \e notify signal.

    \sa property(), propertyCount(), removeProperty(), indexOfProperty()
*/
QMetaPropertyBuilder QMetaObjectBuilder::addProperty(const QByteArray &name, const QByteArray &type,
                                                     int notifierId)
{
    return addProperty(name, type, QMetaType::fromName(type), notifierId);
}

/*!
    \overload
 */
QMetaPropertyBuilder QMetaObjectBuilder::addProperty(const QByteArray &name, const QByteArray &type, QMetaType metaType, int notifierId)
{
    int index = int(d->properties.size());
    d->properties.push_back(QMetaPropertyBuilderPrivate(name, type, metaType, notifierId));
    return QMetaPropertyBuilder(this, index);
}

/*!
    Adds a new property to this class that has the same information as
    \a prototype.  This is used to clone the properties of an existing
    QMetaObject.  Returns an object that can be used to adjust the
    attributes of the property.

    \sa property(), propertyCount(), removeProperty(), indexOfProperty()
*/
QMetaPropertyBuilder QMetaObjectBuilder::addProperty(const QMetaProperty &prototype)
{
    QMetaPropertyBuilder property = addProperty(prototype.name(), prototype.typeName(), prototype.metaType());
    property.setReadable(prototype.isReadable());
    property.setWritable(prototype.isWritable());
    property.setResettable(prototype.isResettable());
    property.setDesignable(prototype.isDesignable());
    property.setScriptable(prototype.isScriptable());
    property.setStored(prototype.isStored());
    property.setUser(prototype.isUser());
    property.setStdCppSet(prototype.hasStdCppSet());
    property.setEnumOrFlag(prototype.isEnumType());
    property.setConstant(prototype.isConstant());
    property.setFinal(prototype.isFinal());
    property.setVirtual(prototype.isVirtual());
    property.setOverride(prototype.isOverride());
    property.setRevision(prototype.revision());
    if (prototype.hasNotifySignal()) {
        // Find an existing method for the notify signal, or add a new one.
        QMetaMethod method = prototype.notifySignal();
        const QByteArray signature = method.methodSignature();
        int index = indexOfSignal(signature);
        if (index == -1)
            index = addSignal(signature).index();
        d->properties[property._index].notifySignal = index;
    }
    return property;
}

/*!
    Adds a new enumerator to this class with the specified
    \a name.  Returns an object that can be used to adjust
    the other attributes of the enumerator.

    \sa enumerator(), enumeratorCount(), removeEnumerator()
    \sa indexOfEnumerator()
*/
QMetaEnumBuilder QMetaObjectBuilder::addEnumerator(const QByteArray &name)
{
    int index = int(d->enumerators.size());
    d->enumerators.push_back(QMetaEnumBuilderPrivate(name));
    return QMetaEnumBuilder(this, index);
}

/*!
    Adds a new enumerator to this class that has the same information as
    \a prototype.  This is used to clone the enumerators of an existing
    QMetaObject.  Returns an object that can be used to adjust the
    attributes of the enumerator.

    \sa enumerator(), enumeratorCount(), removeEnumerator()
    \sa indexOfEnumerator()
*/
QMetaEnumBuilder QMetaObjectBuilder::addEnumerator(const QMetaEnum &prototype)
{
    QMetaEnumBuilder en = addEnumerator(prototype.name());
    en.setEnumName(prototype.enumName());
    en.setMetaType(prototype.metaType());
    en.setIsFlag(prototype.isFlag());
    en.setIsScoped(prototype.isScoped());
    int count = prototype.keyCount();
    for (int index = 0; index < count; ++index)
        en.addKey(prototype.key(index), prototype.value64(index).value_or(0));
    // reset the is64Bit() flag if necessary
    en.setIs64Bit(prototype.is64Bit());
    return en;
}

/*!
    Adds \a name and \a value as an item of class information to this class.
    Returns the index of the new item of class information.

    \sa classInfoCount(), classInfoName(), classInfoValue(), removeClassInfo()
    \sa indexOfClassInfo()
*/
int QMetaObjectBuilder::addClassInfo(const QByteArray &name, const QByteArray &value)
{
    int index = d->classInfoNames.size();
    d->classInfoNames += name;
    d->classInfoValues += value;
    return index;
}

/*!
    Adds \a meta to this class as a related meta object.  Returns
    the index of the new related meta object entry.

    Related meta objects are used when resolving the enumerated type
    associated with a property, where the enumerated type is in a
    different class from the property.

    \sa relatedMetaObjectCount(), relatedMetaObject()
    \sa removeRelatedMetaObject()
*/
int QMetaObjectBuilder::addRelatedMetaObject(const QMetaObject *meta)
{
    Q_ASSERT(meta);
    int index = d->relatedMetaObjects.size();
    d->relatedMetaObjects.append(meta);
    return index;
}

/*!
    Adds the contents of \a prototype to this meta object builder.
    This function is useful for cloning the contents of an existing QMetaObject.

    The \a members parameter indicates which members of \a prototype
    should be added.  The default is AllMembers.
*/
void QMetaObjectBuilder::addMetaObject(const QMetaObject *prototype,
                                       QMetaObjectBuilder::AddMembers members)
{
    Q_ASSERT(prototype);
    int index;

    if ((members & ClassName) != 0)
        d->className = prototype->className();

    if ((members & SuperClass) != 0)
        d->superClass = prototype->superClass();

    if ((members & (Methods | Signals | Slots)) != 0) {
        for (index = prototype->methodOffset(); index < prototype->methodCount(); ++index) {
            QMetaMethod method = prototype->method(index);
            if (method.methodType() != QMetaMethod::Signal) {
                switch (method.access()) {
                case QMetaMethod::Public:
                    if ((members & PublicMethods) == 0)
                        continue;
                    break;
                case QMetaMethod::Private:
                    if ((members & PrivateMethods) == 0)
                        continue;
                    break;
                case QMetaMethod::Protected:
                    if ((members & ProtectedMethods) == 0)
                        continue;
                    break;
                }
            }
            switch (method.methodType()) {
            case QMetaMethod::Method:
                if (members & Methods)
                    addMethod(method);
                break;
            case QMetaMethod::Signal:
                if (members & Signals)
                    addMethod(method);
                break;
            case QMetaMethod::Slot:
                if (members & Slots)
                    addMethod(method);
                break;
            case QMetaMethod::Constructor:
                ;
            }
        }
    }

    if ((members & Constructors) != 0) {
        for (index = 0; index < prototype->constructorCount(); ++index)
            addConstructor(prototype->constructor(index));
    }

    if ((members & Properties) != 0) {
        for (index = prototype->propertyOffset(); index < prototype->propertyCount(); ++index)
            addProperty(prototype->property(index));
    }

    if ((members & Enumerators) != 0) {
        for (index = prototype->enumeratorOffset(); index < prototype->enumeratorCount(); ++index)
            addEnumerator(prototype->enumerator(index));
    }

    if ((members & ClassInfos) != 0) {
        for (index = prototype->classInfoOffset(); index < prototype->classInfoCount(); ++index) {
            QMetaClassInfo ci = prototype->classInfo(index);
            addClassInfo(ci.name(), ci.value());
        }
    }

    if ((members & RelatedMetaObjects) != 0) {
        Q_ASSERT(qmobPriv(prototype->d.data)->revision >= 2);
        const auto *objects = prototype->d.relatedMetaObjects;
        if (objects) {
            while (*objects != nullptr) {
                addRelatedMetaObject(*objects);
                ++objects;
            }
        }
    }

    if ((members & StaticMetacall) != 0) {
        Q_ASSERT(qmobPriv(prototype->d.data)->revision >= 6);
        if (prototype->d.static_metacall)
            setStaticMetacallFunction(prototype->d.static_metacall);
    }
}

/*!
    Returns the method at \a index in this class. The returned object's type is
    either QMetaMethod::Method or QMetaMethod::Slot. For signals, use signal().

    \note Starting from Qt 6.12, this function can only be used for
    QMetaMethod::Method or QMetaMethod::Slot functions. Before Qt 6.12
    this method could be used for signals too.

    \sa signal(), methodCount(), addMethod(), removeMethod(), indexOfMethod()
*/
QMetaMethodBuilder QMetaObjectBuilder::method(int index) const
{
    if (uint(index) < d->methods.size())
        return QMetaMethodBuilder(this, index, QMetaMethod::Method);
    else
        return QMetaMethodBuilder();
}


/*! \since 6.12

    Returns the signal at \a index in this class. The returned object's
    type is QMetaMethod::Signal.

    \sa method(), slot(), indexOfSignal()
*/
QMetaMethodBuilder QMetaObjectBuilder::signal(int index) const
{
    if (size_t(index) < d->signalVec.size())
        return QMetaMethodBuilder(this, index, QMetaMethod::Signal);
    return QMetaMethodBuilder();
}

/*!
    Returns the constructor at \a index in this class.

    \sa methodCount(), addMethod(), removeMethod(), indexOfConstructor()
*/
QMetaMethodBuilder QMetaObjectBuilder::constructor(int index) const
{
    if (uint(index) < d->constructors.size())
        return QMetaMethodBuilder(this, index, QMetaMethod::Constructor);
    else
        return QMetaMethodBuilder();
}

/*!
    Returns the property at \a index in this class.

    \sa methodCount(), addMethod(), removeMethod(), indexOfProperty()
*/
QMetaPropertyBuilder QMetaObjectBuilder::property(int index) const
{
    if (uint(index) < d->properties.size())
        return QMetaPropertyBuilder(this, index);
    else
        return QMetaPropertyBuilder();
}

/*!
    Returns the enumerator at \a index in this class.

    \sa enumeratorCount(), addEnumerator(), removeEnumerator()
    \sa indexOfEnumerator()
*/
QMetaEnumBuilder QMetaObjectBuilder::enumerator(int index) const
{
    if (uint(index) < d->enumerators.size())
        return QMetaEnumBuilder(this, index);
    else
        return QMetaEnumBuilder();
}

/*!
    Returns the related meta object at \a index in this class.

    Related meta objects are used when resolving the enumerated type
    associated with a property, where the enumerated type is in a
    different class from the property.

    \sa relatedMetaObjectCount(), addRelatedMetaObject()
    \sa removeRelatedMetaObject()
*/
const QMetaObject *QMetaObjectBuilder::relatedMetaObject(int index) const
{
    if (index >= 0 && index < d->relatedMetaObjects.size())
        return d->relatedMetaObjects[index];
    else
        return nullptr;
}

/*!
    Returns the name of the item of class information at \a index
    in this class.

    \sa classInfoCount(), addClassInfo(), classInfoValue(), removeClassInfo()
    \sa indexOfClassInfo()
*/
QByteArray QMetaObjectBuilder::classInfoName(int index) const
{
    if (index >= 0 && index < d->classInfoNames.size())
        return d->classInfoNames[index];
    else
        return QByteArray();
}

/*!
    Returns the value of the item of class information at \a index
    in this class.

    \sa classInfoCount(), addClassInfo(), classInfoName(), removeClassInfo()
    \sa indexOfClassInfo()
*/
QByteArray QMetaObjectBuilder::classInfoValue(int index) const
{
    if (index >= 0 && index < d->classInfoValues.size())
        return d->classInfoValues[index];
    else
        return QByteArray();
}

/*!
    Removes the method at \a index from this class. This method's type is either
    QMetaMethod::Method or QMetaMethod::Slot. The indices of all following
    methods and slots will be decremented by 1. For signals use removeSignal().

    \note Starting from Qt 6.12, this function can only be used for
    QMetaMethod::Method or QMetaMethod::Slot functions. Before Qt 6.12 this
    function could be used to remove signals too.

    \sa removeSignal(), methodCount(), addMethod(), method(), indexOfMethod()
*/
void QMetaObjectBuilder::removeMethod(int index)
{
    if (size_t(index) < d->methods.size())
        d->methods.erase(d->methods.begin() + index);
}

/*! \since 6.12

    Removes the signal at \a index from this class. The indices of all
    following signals will be decremented by 1. If the signal is registered
    as a notify signal on a property, then the notify signal will be removed
    from the property.

    \sa removeMethod()
*/
void QMetaObjectBuilder::removeSignal(int index)
{
    if (size_t(index) < d->signalVec.size()) {
        d->signalVec.erase(d->signalVec.begin() + index);
        for (auto &property : d->properties) {
            // Adjust the indices of property notify signal references.
            if (property.notifySignal == index) {
                property.notifySignal = -1;
            } else if (property.notifySignal > index)
                property.notifySignal--;
        }
    }
}

/*!
    Removes the constructor at \a index from this class.  The indices of
    all following constructors will be adjusted downwards by 1.

    \sa constructorCount(), addConstructor(), constructor()
    \sa indexOfConstructor()
*/
void QMetaObjectBuilder::removeConstructor(int index)
{
    if (uint(index) < d->constructors.size())
        d->constructors.erase(d->constructors.begin() + index);
}

/*!
    Removes the property at \a index from this class.  The indices of
    all following properties will be adjusted downwards by 1.

    \sa propertyCount(), addProperty(), property(), indexOfProperty()
*/
void QMetaObjectBuilder::removeProperty(int index)
{
    if (uint(index) < d->properties.size())
        d->properties.erase(d->properties.begin() + index);
}

/*!
    Removes the enumerator at \a index from this class.  The indices of
    all following enumerators will be adjusted downwards by 1.

    \sa enumertorCount(), addEnumerator(), enumerator()
    \sa indexOfEnumerator()
*/
void QMetaObjectBuilder::removeEnumerator(int index)
{
    if (uint(index) < d->enumerators.size())
        d->enumerators.erase(d->enumerators.begin() + index);
}

/*!
    Removes the item of class information at \a index from this class.
    The indices of all following items will be adjusted downwards by 1.

    \sa classInfoCount(), addClassInfo(), classInfoName(), classInfoValue()
    \sa indexOfClassInfo()
*/
void QMetaObjectBuilder::removeClassInfo(int index)
{
    if (index >= 0 && index < d->classInfoNames.size()) {
        d->classInfoNames.removeAt(index);
        d->classInfoValues.removeAt(index);
    }
}

/*!
    Removes the related meta object at \a index from this class.
    The indices of all following related meta objects will be adjusted
    downwards by 1.

    Related meta objects are used when resolving the enumerated type
    associated with a property, where the enumerated type is in a
    different class from the property.

    \sa relatedMetaObjectCount(), addRelatedMetaObject()
    \sa relatedMetaObject()
*/
void QMetaObjectBuilder::removeRelatedMetaObject(int index)
{
    if (index >= 0 && index < d->relatedMetaObjects.size())
        d->relatedMetaObjects.removeAt(index);
}

/*!
    Finds a method with the specified \a signature and returns its index;
    otherwise returns -1.  The \a signature will be normalized by this method.

    \sa method(), methodCount(), addMethod(), removeMethod()
*/
int QMetaObjectBuilder::indexOfMethod(const QByteArray &signature)
{
    QByteArray sig = QMetaObject::normalizedSignature(signature);
    for (const auto &method : d->methods) {
        if (sig == method.signature)
            return int(&method - &d->methods.front());
    }
    return -1;
}

/*!
    Finds a signal with the specified \a signature and returns its index;
    otherwise returns -1.  The \a signature will be normalized by this method.

    \sa indexOfMethod(), indexOfSlot()
*/
int QMetaObjectBuilder::indexOfSignal(const QByteArray &signature)
{
    QByteArray sig = QMetaObject::normalizedSignature(signature);
    for (size_t i = 0; i < d->signalVec.size(); ++i) {
        if (sig == d->signalVec[i].signature)
            return int(i);
    }
    return -1;
}

/*!
    Finds a slot with the specified \a signature and returns its index;
    otherwise returns -1.  The \a signature will be normalized by this method.

    \sa indexOfMethod(), indexOfSignal()
*/
int QMetaObjectBuilder::indexOfSlot(const QByteArray &signature)
{
    QByteArray sig = QMetaObject::normalizedSignature(signature);
    for (const auto &method : d->methods) {
        if (method.methodType() == QMetaMethod::Slot && sig == method.signature)
            return int(&method - &d->methods.front());
    }
    return -1;
}

/*!
    Finds a constructor with the specified \a signature and returns its index;
    otherwise returns -1.  The \a signature will be normalized by this method.

    \sa constructor(), constructorCount(), addConstructor(), removeConstructor()
*/
int QMetaObjectBuilder::indexOfConstructor(const QByteArray &signature)
{
    QByteArray sig = QMetaObject::normalizedSignature(signature);
    for (const auto &constructor : d->constructors) {
        if (sig == constructor.signature)
            return int(&constructor - &d->constructors.front());
    }
    return -1;
}

/*!
    Finds a property with the specified \a name and returns its index;
    otherwise returns -1.

    \sa property(), propertyCount(), addProperty(), removeProperty()
*/
int QMetaObjectBuilder::indexOfProperty(const QByteArray &name)
{
    for (const auto &property : d->properties) {
        if (name == property.name)
            return int(&property - &d->properties.front());
    }
    return -1;
}

/*!
    Finds an enumerator with the specified \a name and returns its index;
    otherwise returns -1.

    \sa enumertor(), enumeratorCount(), addEnumerator(), removeEnumerator()
*/
int QMetaObjectBuilder::indexOfEnumerator(const QByteArray &name)
{
    for (const auto &enumerator : d->enumerators) {
        if (name == enumerator.name)
            return int(&enumerator - &d->enumerators.front());
    }
    return -1;
}

/*!
    Finds an item of class information with the specified \a name and
    returns its index; otherwise returns -1.

    \sa classInfoName(), classInfoValue(), classInfoCount(), addClassInfo()
    \sa removeClassInfo()
*/
int QMetaObjectBuilder::indexOfClassInfo(const QByteArray &name)
{
    for (int index = 0; index < d->classInfoNames.size(); ++index) {
        if (name == d->classInfoNames[index])
            return index;
    }
    return -1;
}

// Align on a specific type boundary.
#ifdef ALIGN
#  undef ALIGN
#endif
#define ALIGN(size,type)    \
    (size) = ((size) + sizeof(type) - 1) & ~(sizeof(type) - 1)

/*!
    \class QMetaStringTable
    \inmodule QtCore
    \internal
    \brief The QMetaStringTable class can generate a meta-object string table at runtime.
*/

QMetaStringTable::QMetaStringTable(const QByteArray &className)
    : m_index(0)
    , m_className(className)
{
    const int index = enter(m_className);
    Q_ASSERT(index == 0);
    Q_UNUSED(index);
}

// Enters the given value into the string table (if it hasn't already been
// entered). Returns the index of the string.
int QMetaStringTable::enter(const QByteArray &value)
{
    auto [it, inserted] = m_entries.tryInsert(value, m_index);
    if (inserted)
        ++m_index;
    return it.value();
}

int QMetaStringTable::preferredAlignment()
{
    return alignof(uint);
}

// Returns the size (in bytes) required for serializing this string table.
int QMetaStringTable::blobSize() const
{
    int size = int(m_entries.size() * 2 * sizeof(uint));
    Entries::const_iterator it;
    for (it = m_entries.constBegin(); it != m_entries.constEnd(); ++it)
        size += it.key().size() + 1;
    return size;
}

static void writeString(char *out, int i, const QByteArray &str,
                        const int offsetOfStringdataMember, int &stringdataOffset)
{
    int size = str.size();
    int offset = offsetOfStringdataMember + stringdataOffset;
    uint offsetLen[2] = { uint(offset), uint(size) };

    memcpy(out + 2 * i * sizeof(uint), &offsetLen, 2 * sizeof(uint));

    memcpy(out + offset, str.constData(), size);
    out[offset + size] = '\0';

    stringdataOffset += size + 1;
}

// Writes strings to string data struct.
// The struct consists of an array of QByteArrayData, followed by a char array
// containing the actual strings. This format must match the one produced by
// moc (see generator.cpp).
void QMetaStringTable::writeBlob(char *out) const
{
    Q_ASSERT(!(reinterpret_cast<quintptr>(out) & (preferredAlignment() - 1)));

    int offsetOfStringdataMember = int(m_entries.size() * 2 * sizeof(uint));
    int stringdataOffset = 0;

    // qt_metacast expects the first string in the string table to be the class name.
    writeString(out, /*index*/ 0, m_className, offsetOfStringdataMember, stringdataOffset);

    for (Entries::ConstIterator it = m_entries.constBegin(), end = m_entries.constEnd();
         it != end; ++it) {
        const int i = it.value();
        if (i == 0)
            continue;
        const QByteArray &str = it.key();

        writeString(out, i, str, offsetOfStringdataMember, stringdataOffset);
    }
}

// Returns the number of integers needed to store these methods' parameter type
// infos or type names, parameter names and, if present, the method revision.
// This is needed for calculating the size of the methods' parameter type/name
// meta-data.
static int aggregateParameterCount(const std::vector<QMetaMethodBuilderPrivate> &methods)
{
    int sum = 0;
    for (const auto &method : methods) {
        if (method.revision)
            ++sum;

        // type infos or type names; +1 for return type (constructors don't
        // have one, so this stores a link to an empty string)
        sum += method.parameterCount() + 1;

        // parameter names (return type doesn't get one)
        sum += method.parameterCount();
    }
    return sum;
}

enum Mode {
    Prepare, // compute the size of the metaobject
    Construct // construct metaobject in pre-allocated buffer
};
// Build a QMetaObject in "buf" based on the information in "d".
// If the mode is prepare, then return the number of bytes needed to
// build the QMetaObject.
template<Mode mode>
static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
                           int expectedSize)
{
    Q_UNUSED(expectedSize); // Avoid warning in release mode
    Q_UNUSED(buf);
    qsizetype size = 0;
    int dataIndex;
    int paramsIndex;
    int enumIndex;
    int index;

    // Create the main QMetaObject structure at the start of the buffer.
    QMetaObject *meta = reinterpret_cast<QMetaObject *>(buf);
    size += sizeof(QMetaObject);
    ALIGN(size, int);
    if constexpr (mode == Construct) {
        meta->d.superdata = d->superClass;
        meta->d.relatedMetaObjects = nullptr;
        meta->d.extradata = nullptr;
        meta->d.metaTypes = nullptr;
        meta->d.static_metacall = d->staticMetacallFunction;
    }

    // Populate the QMetaObjectPrivate structure.
    QMetaObjectPrivate *pmeta = buf ? reinterpret_cast<QMetaObjectPrivate *>(buf + size)
                                    : nullptr;
    //int pmetaSize = size;
    dataIndex = MetaObjectPrivateFieldCount;
    int methodParametersDataSize = aggregateParameterCount(d->methods)
             + aggregateParameterCount(d->signalVec)
             + aggregateParameterCount(d->constructors);
    if constexpr (mode == Construct) {
        static_assert(QMetaObjectPrivate::OutputRevision == 13, "QMetaObjectBuilder should generate the same version as moc");
        pmeta->revision = QMetaObjectPrivate::OutputRevision;
        pmeta->flags = d->flags.toInt() | AllocatedMetaObject;
        pmeta->className = 0;   // Class name is always the first string.

        pmeta->classInfoCount = d->classInfoNames.size();
        pmeta->classInfoData = dataIndex;
        dataIndex += 2 * d->classInfoNames.size();

        // QMetaObject treats QMetaObjectPrivate::methodData as the beginning of all "methods",
        // including signals
        pmeta->methodData = dataIndex;
        pmeta->signalCount = int(d->signalVec.size());
        pmeta->methodCount = int(d->methods.size()) + pmeta->signalCount;
        dataIndex += QMetaObjectPrivate::IntsPerMethod * pmeta->methodCount;
        paramsIndex = dataIndex;
        dataIndex += methodParametersDataSize;

        pmeta->propertyCount = int(d->properties.size());
        pmeta->propertyData = dataIndex;
        dataIndex += QMetaObjectPrivate::IntsPerProperty * int(d->properties.size());

        pmeta->enumeratorCount = int(d->enumerators.size());
        pmeta->enumeratorData = dataIndex;
        dataIndex += QMetaObjectPrivate::IntsPerEnum * int(d->enumerators.size());

        pmeta->constructorCount = int(d->constructors.size());
        pmeta->constructorData = dataIndex;
        dataIndex += QMetaObjectPrivate::IntsPerMethod * int(d->constructors.size());
    } else {
        dataIndex += 2 * int(d->classInfoNames.size());
        const auto method_count = int(d->signalVec.size() + d->methods.size());
        dataIndex += QMetaObjectPrivate::IntsPerMethod * method_count;
        paramsIndex = dataIndex;
        dataIndex += methodParametersDataSize;
        dataIndex += QMetaObjectPrivate::IntsPerProperty * int(d->properties.size());
        dataIndex += QMetaObjectPrivate::IntsPerEnum * int(d->enumerators.size());
        dataIndex += QMetaObjectPrivate::IntsPerMethod * int(d->constructors.size());
    }

    // Allocate space for the enumerator key names and values.
    enumIndex = dataIndex;
    for (const auto &enumerator : d->enumerators) {
        dataIndex += 2 * enumerator.keys.size();
        if (enumerator.flags & EnumIs64Bit)
            dataIndex += enumerator.keys.size();
    }

    // Zero terminator at the end of the data offset table.
    ++dataIndex;

    // Find the start of the data and string tables.
    int *data = reinterpret_cast<int *>(pmeta);
    size += dataIndex * sizeof(int);
    ALIGN(size, void *);
    [[maybe_unused]] char *str = reinterpret_cast<char *>(buf + size);
    if constexpr (mode == Construct) {
        meta->d.stringdata = reinterpret_cast<const uint *>(str);
        meta->d.data = reinterpret_cast<uint *>(data);
    }

    // Reset the current data position to just past the QMetaObjectPrivate.
    dataIndex = MetaObjectPrivateFieldCount;

    QMetaStringTable strings(d->className);

    // Output the class infos,
    Q_ASSERT(!buf || dataIndex == pmeta->classInfoData);
    for (index = 0; index < d->classInfoNames.size(); ++index) {
        [[maybe_unused]] int name = strings.enter(d->classInfoNames[index]);
        [[maybe_unused]] int value = strings.enter(d->classInfoValues[index]);
        if constexpr (mode == Construct) {
            data[dataIndex] = name;
            data[dataIndex + 1] = value;
        }
        dataIndex += 2;
    }

    // Output all the methods in the class.
    Q_ASSERT(!buf || dataIndex == pmeta->methodData);
    // property count + enum count  + 1 for metatype of this metaobject
    int parameterMetaTypesIndex = int(d->properties.size()) + int(d->enumerators.size()) + 1;

    auto add_method = [&](const auto &method) {
        [[maybe_unused]] int name = strings.enter(method.name());
        int argc = method.parameterCount();
        [[maybe_unused]] int tag = strings.enter(method.tag);
        [[maybe_unused]] int attrs = method.attributes;
        if (method.revision)
            ++paramsIndex;
        if constexpr (mode == Construct) {
            data[dataIndex]     = name;
            data[dataIndex + 1] = argc;
            data[dataIndex + 2] = paramsIndex;
            data[dataIndex + 3] = tag;
            data[dataIndex + 4] = attrs;
            data[dataIndex + 5] = parameterMetaTypesIndex;
        }
        dataIndex += QMetaObjectPrivate::IntsPerMethod;
        paramsIndex += 1 + argc * 2;
        parameterMetaTypesIndex += 1 + argc;
    };

    // first output the signals in the class, to match meta-objects generated by moc
    for (const auto &signal : d->signalVec)
        add_method(signal);

    // Output the rest of the methods (QMetaMethod::{Method,Slot}) in the class
    for (const auto &method : d->methods)
        add_method(method);

    auto getTypeInfo = [&](const auto &typeName) {
        if (isBuiltinType(typeName))
            return QMetaType::fromName(typeName).rawId();
        int index;
        if constexpr (std::is_same_v<decltype(typeName), const QByteArrayView &>)
            index = strings.enter(QByteArray::fromRawData(typeName.constData(), typeName.size()));
        else
            index = strings.enter(typeName);
        return int(IsUnresolvedType | index);
    };

    // Output the method parameters in the class.
    Q_ASSERT(!buf || dataIndex == pmeta->methodData + pmeta->methodCount * QMetaObjectPrivate::IntsPerMethod);
    auto processMethodParameters = [&](const auto &methods) {
        for (const auto &method : methods) {
            if (method.revision) {
                if constexpr (mode == Construct)
                    data[dataIndex] = method.revision;
                ++dataIndex;
            }

            [[maybe_unused]] int typeInfo = getTypeInfo(method.returnType);
            if constexpr (mode == Construct)
                data[dataIndex] = typeInfo;
            ++dataIndex;

            const QList<QByteArrayView> paramTypeNames = method.parameterTypes();
            for (auto typeName : paramTypeNames) {
                [[maybe_unused]] int typeInfo = getTypeInfo(typeName);
                if constexpr (mode == Construct)
                    data[dataIndex] = typeInfo;
                ++dataIndex;
            }

            QList<QByteArray> paramNames = method.parameterNames;
            if (const auto paramCount = paramTypeNames.size(); paramNames.size() < paramCount)
                paramNames.resize(paramCount);
            for (const auto &name : std::as_const(paramNames)) {
                [[maybe_unused]] int stringIndex = strings.enter(name);
                if constexpr (mode == Construct)
                    data[dataIndex] = stringIndex;
                ++dataIndex;
            }
        }
    };
    processMethodParameters(d->signalVec);
    processMethodParameters(d->methods);
    processMethodParameters(d->constructors);

    // Output the properties in the class.
    Q_ASSERT(!buf || dataIndex == pmeta->propertyData);
    for (QMetaPropertyBuilderPrivate &prop : d->properties) {
        [[maybe_unused]] int name = strings.enter(prop.name);

        // try to resolve the metatype again if it was unknown
        if (!prop.metaType.isValid())
            prop.metaType = QMetaType::fromName(prop.type);
        [[maybe_unused]] const int typeInfo = prop.metaType.isValid()
                       ? prop.metaType.id()
                       : IsUnresolvedType | strings.enter(prop.type);

        [[maybe_unused]] int flags = prop.flags;

        if (!isBuiltinType(prop.type))
            flags |= EnumOrFlag;

        if constexpr (mode == Construct) {
            data[dataIndex]     = name;
            data[dataIndex + 1] = typeInfo;
            data[dataIndex + 2] = flags;
            data[dataIndex + 3] = prop.notifySignal;
            data[dataIndex + 4] = prop.revision;
        }
        dataIndex += QMetaObjectPrivate::IntsPerProperty;
    }

    // Output the enumerators in the class.
    Q_ASSERT(!buf || dataIndex == pmeta->enumeratorData);
    for (const auto &enumerator : d->enumerators) {
        [[maybe_unused]] int name = strings.enter(enumerator.name);
        [[maybe_unused]] int enumName = strings.enter(enumerator.enumName);
        int count = enumerator.keys.size();
        if constexpr (mode == Construct) {
            data[dataIndex]     = name;
            data[dataIndex + 1] = enumName;
            data[dataIndex + 2] = enumerator.flags.toInt();
            data[dataIndex + 3] = count;
            data[dataIndex + 4] = enumIndex;
        }
        for (int key = 0; key < count; ++key) {
            [[maybe_unused]] int keyIndex = strings.enter(enumerator.keys[key]);
            if constexpr (mode == Construct) {
                data[enumIndex + 0] = keyIndex;
                data[enumIndex + 1] = uint(enumerator.values[key]);
            }
            enumIndex += 2;
        }
        bool is64Bit = enumerator.flags.testAnyFlags(EnumIs64Bit);
        for (int key = 0; is64Bit && key < count; ++key) {
            if constexpr (mode == Construct) {
                data[enumIndex] = uint(enumerator.values[key] >> 32);
            }
            ++enumIndex;
        }
        dataIndex += QMetaObjectPrivate::IntsPerEnum;
    }

    // Output the constructors in the class.
    Q_ASSERT(!buf || dataIndex == pmeta->constructorData);
    for (const auto &ctor : d->constructors) {
        [[maybe_unused]] int name = strings.enter(ctor.name());
        int argc = ctor.parameterCount();
        [[maybe_unused]] int tag = strings.enter(ctor.tag);
        [[maybe_unused]] int attrs = ctor.attributes;
        if constexpr (mode == Construct) {
            data[dataIndex]     = name;
            data[dataIndex + 1] = argc;
            data[dataIndex + 2] = paramsIndex;
            data[dataIndex + 3] = tag;
            data[dataIndex + 4] = attrs;
            data[dataIndex + 5] = parameterMetaTypesIndex;
        }
        dataIndex += QMetaObjectPrivate::IntsPerMethod;
        paramsIndex += 1 + argc * 2;
        if (ctor.revision)
            ++paramsIndex;
        parameterMetaTypesIndex += argc;
    }

    size += strings.blobSize();

    if constexpr (mode == Construct)
        strings.writeBlob(str);

    // Output the zero terminator in the data array.
    if constexpr (mode == Construct)
        data[enumIndex] = 0;

    // Create the relatedMetaObjects block if we need one.
    if (d->relatedMetaObjects.size() > 0) {
        using SuperData = QMetaObject::SuperData;
        ALIGN(size, SuperData);
        auto objects = reinterpret_cast<SuperData *>(buf + size);
        if constexpr (mode == Construct) {
            meta->d.relatedMetaObjects = objects;
            for (index = 0; index < d->relatedMetaObjects.size(); ++index)
                objects[index] = d->relatedMetaObjects[index];
            objects[index] = nullptr;
        }
        size += sizeof(SuperData) * (d->relatedMetaObjects.size() + 1);
    }

    ALIGN(size, QtPrivate::QMetaTypeInterface *);
    auto types = reinterpret_cast<const QtPrivate::QMetaTypeInterface **>(buf + size);
    if constexpr (mode == Construct) {
        meta->d.metaTypes = types;
        for (const auto &prop : d->properties) {
            QMetaType mt = prop.metaType;
            *types = mt.iface();
            types++;
        }
        // add metatypes for enumerators
        for (const auto &enumerator: d->enumerators) {
            QMetaType mt = enumerator.metaType;
            mt.registerType();
            *types = mt.iface();
            types++;
        }
        // add metatype interface for this metaobject - must be null
        // as we can't know our metatype
        *types = nullptr;
        types++;

        auto addMetaTypes = [&](const auto &method) {
            QMetaType mt(QMetaType::fromName(method.returnType).rawId());
            *types = reinterpret_cast<QtPrivate::QMetaTypeInterface *&>(mt);
            types++;
            for (auto parameterType: method.parameterTypes()) {
                QMetaType mt = QMetaType::fromName(parameterType);
                *types = mt.iface();
                types++;
            }
        };
        for (const auto &method : d->signalVec)
            addMetaTypes(method);
        for (const auto &method : d->methods)
            addMetaTypes(method);

        for (const auto &constructor : d->constructors) {
            for (auto parameterType : constructor.parameterTypes()) {
                QMetaType mt = QMetaType::fromName(parameterType);
                *types = mt.iface();
                types++;
            }
        }
    }
    // parameterMetaTypesIndex is equal to the total number of metatypes
    size += sizeof(QMetaType) * parameterMetaTypesIndex;

    // Align the final size and return it.
    ALIGN(size, void *);
    Q_ASSERT(!buf || size == expectedSize);
    return size;
}

/*!
    Converts this meta object builder into a concrete QMetaObject.
    The return value should be deallocated using free() once it
    is no longer needed.

    The returned meta object is a snapshot of the state of the
    QMetaObjectBuilder.  Any further modifications to the QMetaObjectBuilder
    will not be reflected in previous meta objects returned by
    this method.
*/
QMetaObject *QMetaObjectBuilder::toMetaObject() const
{
    int size = buildMetaObject<Prepare>(d, nullptr, 0);
    char *buf = new (calloc(size, 1)) char[size];
    buildMetaObject<Construct>(d, buf, size);
    return reinterpret_cast<QMetaObject *>(buf);
}

/*!
    \typedef QMetaObjectBuilder::StaticMetacallFunction

    Typedef for static metacall functions.  The three parameters are
    the call type value, the constructor index, and the
    array of parameters.
*/

/*!
    Returns the static metacall function to use to construct objects
    of this class.  The default value is null.

    \sa setStaticMetacallFunction()
*/
QMetaObjectBuilder::StaticMetacallFunction QMetaObjectBuilder::staticMetacallFunction() const
{
    return d->staticMetacallFunction;
}

/*!
    Sets the static metacall function to use to construct objects
    of this class to \a value.  The default value is null.

    \sa staticMetacallFunction()
*/
void QMetaObjectBuilder::setStaticMetacallFunction
        (QMetaObjectBuilder::StaticMetacallFunction value)
{
    d->staticMetacallFunction = value;
}

/*!
    \class QMetaMethodBuilder
    \inmodule QtCore
    \internal
    \brief The QMetaMethodBuilder class enables modifications to a method definition on a meta object builder.
*/

QMetaMethodBuilderPrivate *QMetaMethodBuilder::d_func() const
{
    if (!_mobj)
        return nullptr;
    switch (_type) {
    case QMetaMethod::Signal:
        if (_index < int(_mobj->d->signalVec.size()))
            return &(_mobj->d->signalVec[_index]);
        break;
    case QMetaMethod::Method:
    case QMetaMethod::Slot:
        if (_index < int(_mobj->d->methods.size()))
            return &(_mobj->d->methods[_index]);
        break;
    case QMetaMethod::Constructor:
        if (_index < int(_mobj->d->constructors.size()))
            return &(_mobj->d->constructors[_index]);
        break;
    }
    return nullptr;
}

/*!
    \fn QMetaMethodBuilder::QMetaMethodBuilder()
    \internal
*/

/*!
    Returns the index of this method within its QMetaObjectBuilder.
*/
int QMetaMethodBuilder::index() const
{
    return _index;
}

/*!
    Returns the type of this method (signal, slot, method, or constructor).
*/
QMetaMethod::MethodType QMetaMethodBuilder::methodType() const
{
    QMetaMethodBuilderPrivate *d = d_func();
    if (d)
        return d->methodType();
    else
        return QMetaMethod::Method;
}

/*!
    Returns the signature of this method.

    \sa parameterNames(), returnType()
*/
QByteArray QMetaMethodBuilder::signature() const
{
    QMetaMethodBuilderPrivate *d = d_func();
    if (d)
        return d->signature;
    else
        return QByteArray();
}

/*!
    Returns the return type for this method; empty if the method's
    return type is \c{void}.

    \sa setReturnType(), signature()
*/
QByteArray QMetaMethodBuilder::returnType() const
{
    QMetaMethodBuilderPrivate *d = d_func();
    if (d)
        return d->returnType;
    else
        return QByteArray();
}

/*!
    Sets the return type for this method to \a value.  If \a value
    is empty, then the method's return type is \c{void}.  The \a value
    will be normalized before it is added to the method.

    \sa returnType(), parameterTypes(), signature()
*/
void QMetaMethodBuilder::setReturnType(const QByteArray &value)
{
    QMetaMethodBuilderPrivate *d = d_func();
    if (d)
        d->returnType = QMetaObject::normalizedType(value);
}

/*!
    Returns the list of parameter types for this method.

    \sa returnType(), parameterNames()
*/
QList<QByteArrayView> QMetaMethodBuilder::parameterTypes() const
{
    QMetaMethodBuilderPrivate *d = d_func();
    if (d)
        return d->parameterTypes();
    else
        return {};
}

/*!
    Returns the list of parameter names for this method.

    \sa setParameterNames()
*/
QList<QByteArray> QMetaMethodBuilder::parameterNames() const
{
    QMetaMethodBuilderPrivate *d = d_func();
    if (d)
        return d->parameterNames;
    else
        return QList<QByteArray>();
}

/*!
    Sets the list of parameter names for this method to \a value.

    \sa parameterNames()
*/
void QMetaMethodBuilder::setParameterNames(const QList<QByteArray> &value)
{
    QMetaMethodBuilderPrivate *d = d_func();
    Q_ASSERT(d->parameterCount() >= value.size());
    if (d)
        d->parameterNames = value;
}

/*!
    Returns the tag associated with this method.

    \sa setTag()
*/
QByteArray QMetaMethodBuilder::tag() const
{
    QMetaMethodBuilderPrivate *d = d_func();
    if (d)
        return d->tag;
    else
        return QByteArray();
}

/*!
    Sets the tag associated with this method to \a value.

    \sa setTag()
*/
void QMetaMethodBuilder::setTag(const QByteArray &value)
{
    QMetaMethodBuilderPrivate *d = d_func();
    if (d)
        d->tag = value;
}

/*!
    Returns the access specification of this method (private, protected,
    or public).  The default value is QMetaMethod::Public for methods,
    slots, signals and constructors.

    \sa setAccess()
*/
QMetaMethod::Access QMetaMethodBuilder::access() const
{
    QMetaMethodBuilderPrivate *d = d_func();
    if (d)
        return d->access();
    else
        return QMetaMethod::Public;
}

/*!
    Sets the access specification of this method (private, protected,
    or public) to \a value.  If the method is a signal, this function
    will be ignored.

    \sa access()
*/
void QMetaMethodBuilder::setAccess(QMetaMethod::Access value)
{
    QMetaMethodBuilderPrivate *d = d_func();
    if (d && d->methodType() != QMetaMethod::Signal)
        d->setAccess(value);
}

/*!
    Returns the additional attributes for this method.

    \sa setAttributes()
*/
int QMetaMethodBuilder::attributes() const
{
    QMetaMethodBuilderPrivate *d = d_func();
    if (d)
        return (d->attributes >> 4) & 0x7;
    else
        return 0;
}

/*!
    Sets the additional attributes for this method to \a value.

    \sa attributes()
*/
void QMetaMethodBuilder::setAttributes(int value)
{
    QMetaMethodBuilderPrivate *d = d_func();
    if (d) {
        d->attributes &= ~0x70;
        d->attributes |= (value & 0x7) << 4;
    }
}

/*!
    Returns true if the method is const qualified.
 */
bool QMetaMethodBuilder::isConst() const
{
    QMetaMethodBuilderPrivate *d = d_func();
    if (!d)
        return false;
    return (d->attributes & MethodIsConst);
}

void QMetaMethodBuilder::setConst(bool methodIsConst)
{
    QMetaMethodBuilderPrivate *d = d_func();
    if (!d)
        return;
    if (methodIsConst)
        d->attributes |= MethodIsConst;
    else
        d->attributes &= ~MethodIsConst;
}

/*!
    Returns the revision of this method.

    \sa setRevision()
*/
int QMetaMethodBuilder::revision() const
{
    QMetaMethodBuilderPrivate *d = d_func();
    if (d)
        return d->revision;
    return 0;
}

/*!
    Sets the \a revision of this method.

    \sa revision()
*/
void QMetaMethodBuilder::setRevision(int revision)
{
    QMetaMethodBuilderPrivate *d = d_func();
    if (d) {
        d->revision = revision;
        if (revision)
            d->attributes |= MethodRevisioned;
        else
            d->attributes &= ~MethodRevisioned;
    }
}

/*!
    \class QMetaPropertyBuilder
    \inmodule QtCore
    \internal
    \brief The QMetaPropertyBuilder class enables modifications to a property definition on a meta object builder.
*/

QMetaPropertyBuilderPrivate *QMetaPropertyBuilder::d_func() const
{
    if (_mobj && _index >= 0 && _index < int(_mobj->d->properties.size()))
        return &(_mobj->d->properties[_index]);
    else
        return nullptr;
}

/*!
    \fn QMetaPropertyBuilder::QMetaPropertyBuilder()
    \internal
*/

/*!
    \fn int QMetaPropertyBuilder::index() const

    Returns the index of this property within its QMetaObjectBuilder.
*/

/*!
    Returns the name associated with this property.

    \sa type()
*/
QByteArray QMetaPropertyBuilder::name() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->name;
    else
        return QByteArray();
}

/*!
    Returns the type associated with this property.

    \sa name()
*/
QByteArray QMetaPropertyBuilder::type() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->type;
    else
        return QByteArray();
}

/*!
    Returns \c true if this property has a notify signal; false otherwise.

    \sa notifySignal(), setNotifySignal(), removeNotifySignal()
*/
bool QMetaPropertyBuilder::hasNotifySignal() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->notifySignal != -1;
    else
        return false;
}

/*!
    Returns the notify signal associated with this property.

    \sa hasNotifySignal(), setNotifySignal(), removeNotifySignal()
*/
QMetaMethodBuilder QMetaPropertyBuilder::notifySignal() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d && d->notifySignal >= 0)
        return QMetaMethodBuilder(_mobj, d->notifySignal, QMetaMethod::Signal);
    else
        return QMetaMethodBuilder();
}

/*!
    Sets the notify signal associated with this property to \a value.

    \sa hasNotifySignal(), notifySignal(), removeNotifySignal()
*/
void QMetaPropertyBuilder::setNotifySignal(const QMetaMethodBuilder &value)
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d) {
        if (value._mobj) {
            d->notifySignal = value._index;
        } else {
            d->notifySignal = -1;
        }
    }
}

/*!
    Removes the notify signal from this property.

    \sa hasNotifySignal(), notifySignal(), setNotifySignal()
*/
void QMetaPropertyBuilder::removeNotifySignal()
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        d->notifySignal = -1;
}

/*!
    Returns \c true if this property is readable; otherwise returns \c false.
    The default value is true.

    \sa setReadable(), isWritable()
*/
bool QMetaPropertyBuilder::isReadable() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->flag(Readable);
    else
        return false;
}

/*!
    Returns \c true if this property is writable; otherwise returns \c false.
    The default value is true.

    \sa setWritable(), isReadable()
*/
bool QMetaPropertyBuilder::isWritable() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->flag(Writable);
    else
        return false;
}

/*!
    Returns \c true if this property can be reset to a default value; otherwise
    returns \c false.  The default value is false.

    \sa setResettable()
*/
bool QMetaPropertyBuilder::isResettable() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->flag(Resettable);
    else
        return false;
}

/*!
    Returns \c true if this property is designable; otherwise returns \c false.
    This default value is false.

    \sa setDesignable(), isScriptable(), isStored()
*/
bool QMetaPropertyBuilder::isDesignable() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->flag(Designable);
    else
        return false;
}

/*!
    Returns \c true if the property is scriptable; otherwise returns \c false.
    This default value is true.

    \sa setScriptable(), isDesignable(), isStored()
*/
bool QMetaPropertyBuilder::isScriptable() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->flag(Scriptable);
    else
        return false;
}

/*!
    Returns \c true if the property is stored; otherwise returns \c false.
    This default value is false.

    \sa setStored(), isDesignable(), isScriptable()
*/
bool QMetaPropertyBuilder::isStored() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->flag(Stored);
    else
        return false;
}

/*!
    Returns \c true if this property is designated as the \c USER
    property, i.e., the one that the user can edit or that is
    significant in some other way.  Otherwise it returns
    false.  This default value is false.

    \sa setUser(), isDesignable(), isScriptable()
*/
bool QMetaPropertyBuilder::isUser() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->flag(User);
    else
        return false;
}

/*!
    Returns \c true if the property has a C++ setter function that
    follows Qt's standard "name" / "setName" pattern. Designer and uic
    query hasStdCppSet() in order to avoid expensive
    QObject::setProperty() calls. All properties in Qt [should] follow
    this pattern.  The default value is false.

    \sa setStdCppSet()
*/
bool QMetaPropertyBuilder::hasStdCppSet() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->flag(StdCppSet);
    else
        return false;
}

/*!
    Returns \c true if the property is an enumerator or flag type;
    otherwise returns \c false.  This default value is false.

    \sa setEnumOrFlag()
*/
bool QMetaPropertyBuilder::isEnumOrFlag() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->flag(EnumOrFlag);
    else
        return false;
}

/*!
    Returns \c true if the property is constant; otherwise returns \c false.
    The default value is false.
*/
bool QMetaPropertyBuilder::isConstant() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->flag(Constant);
    else
        return false;
}

/*!
    Returns \c true if the property is final; otherwise returns \c false.
    The default value is false.
*/
bool QMetaPropertyBuilder::isFinal() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->flag(Final);
    else
        return false;
}

/*!
    Returns \c true if the property is virtual; otherwise returns \c false.
    The default value is false.
*/
bool QMetaPropertyBuilder::isVirtual() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->flag(Virtual);
    else
        return false;
}

/*!
    Returns \c true if the property does override; otherwise returns \c false.
    The default value is false.
*/
bool QMetaPropertyBuilder::isOverride() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->flag(Override);
    else
        return false;
}

/*!
 * Returns \c true if the property is an alias.
 * The default value is false
 */
bool QMetaPropertyBuilder::isAlias() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->flag(Alias);
    else
        return false;
}

/*!
  Returns \c true if the property is bindable
  The default value is false
 */
bool QMetaPropertyBuilder::isBindable() const
{
    if (auto d = d_func())
        return d->flag(Bindable);
    else
        return false;
}
/*!
  Returns \c true if the property is required.
  The default is \c false.
 */
bool QMetaPropertyBuilder::isRequired() const
{
    if (auto d = d_func())
        return d->flag(Required);
    else
        return false;
}

/*!
    Sets this property to readable if \a value is true.

    \sa isReadable(), setWritable()
*/
void QMetaPropertyBuilder::setReadable(bool value)
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        d->setFlag(Readable, value);
}

/*!
    Sets this property to writable if \a value is true.

    \sa isWritable(), setReadable()
*/
void QMetaPropertyBuilder::setWritable(bool value)
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        d->setFlag(Writable, value);
}

/*!
    Sets this property to resettable if \a value is true.

    \sa isResettable()
*/
void QMetaPropertyBuilder::setResettable(bool value)
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        d->setFlag(Resettable, value);
}

/*!
    Sets this property to designable if \a value is true.

    \sa isDesignable(), setScriptable(), setStored()
*/
void QMetaPropertyBuilder::setDesignable(bool value)
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        d->setFlag(Designable, value);
}

/*!
    Sets this property to scriptable if \a value is true.

    \sa isScriptable(), setDesignable(), setStored()
*/
void QMetaPropertyBuilder::setScriptable(bool value)
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        d->setFlag(Scriptable, value);
}

/*!
    Sets this property to storable if \a value is true.

    \sa isStored(), setDesignable(), setScriptable()
*/
void QMetaPropertyBuilder::setStored(bool value)
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        d->setFlag(Stored, value);
}

/*!
    Sets the \c USER flag on this property to \a value.

    \sa isUser(), setDesignable(), setScriptable()
*/
void QMetaPropertyBuilder::setUser(bool value)
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        d->setFlag(User, value);
}

/*!
    Sets the C++ setter flag on this property to \a value, which is
    true if the property has a C++ setter function that follows Qt's
    standard "name" / "setName" pattern.

    \sa hasStdCppSet()
*/
void QMetaPropertyBuilder::setStdCppSet(bool value)
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        d->setFlag(StdCppSet, value);
}

/*!
    Sets this property to be of an enumerator or flag type if
    \a value is true.

    \sa isEnumOrFlag()
*/
void QMetaPropertyBuilder::setEnumOrFlag(bool value)
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        d->setFlag(EnumOrFlag, value);
}

/*!
    Sets the \c CONSTANT flag on this property to \a value.

    \sa isConstant()
*/
void QMetaPropertyBuilder::setConstant(bool value)
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        d->setFlag(Constant, value);
}

/*!
    Sets the \c FINAL flag on this property to \a value.

    \sa isFinal()
*/
void QMetaPropertyBuilder::setFinal(bool value)
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        d->setFlag(Final, value);
}

/*!
    Sets the \c VIRTUAL flag on this property to \a value.

 \sa isFinal()
*/
void QMetaPropertyBuilder::setVirtual(bool value)
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        d->setFlag(Virtual, value);
}

/*!
    Sets the \c OVERRIDE flag on this property to \a value.

 \sa isOverride()
*/
void QMetaPropertyBuilder::setOverride(bool value)
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        d->setFlag(Override, value);
}

/*!
   Sets the \c ALIAS flag on this property to \a value
 */
void QMetaPropertyBuilder::setAlias(bool value)
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        d->setFlag(Alias, value);
}

/*!
   Sets the\c BINDABLE flag on this property to \a value
 */
void QMetaPropertyBuilder::setBindable(bool value)
{
    if (auto d = d_func())
        d->setFlag(Bindable, value);
}

/*!
   Sets the\c REQUIRED flag on this property to \a value
 */
void QMetaPropertyBuilder::setRequired(bool value)
{
    if (auto d = d_func())
        d->setFlag(Required, value);
}

/*!
    Returns the revision of this property.

    \sa setRevision()
*/
int QMetaPropertyBuilder::revision() const
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        return d->revision;
    return 0;
}

/*!
    Sets the \a revision of this property.

    \sa revision()
*/
void QMetaPropertyBuilder::setRevision(int revision)
{
    QMetaPropertyBuilderPrivate *d = d_func();
    if (d)
        d->revision = revision;
}

/*!
    \class QMetaEnumBuilder
    \inmodule QtCore
    \internal
    \brief The QMetaEnumBuilder class enables modifications to an enumerator definition on a meta object builder.
*/

QMetaEnumBuilderPrivate *QMetaEnumBuilder::d_func() const
{
    if (_mobj && _index >= 0 && _index < int(_mobj->d->enumerators.size()))
        return &(_mobj->d->enumerators[_index]);
    else
        return nullptr;
}

/*!
    \fn QMetaEnumBuilder::QMetaEnumBuilder()
    \internal
*/

/*!
    \fn int QMetaEnumBuilder::index() const

    Returns the index of this enumerator within its QMetaObjectBuilder.
*/

/*!
    Returns the type name of the enumerator (without the scope).
*/
QByteArray QMetaEnumBuilder::name() const
{
    QMetaEnumBuilderPrivate *d = d_func();
    if (d)
        return d->name;
    else
        return QByteArray();
}

/*!
    Returns the enum name of the enumerator (without the scope).

    \since 5.12
*/
QByteArray QMetaEnumBuilder::enumName() const
{
    QMetaEnumBuilderPrivate *d = d_func();
    if (d)
        return d->enumName;
    else
        return QByteArray();
}

/*!
    Sets this enumerator to have the enum name \c alias.

    \since 5.12
    \sa isFlag(), enumName()
*/
void QMetaEnumBuilder::setEnumName(const QByteArray &alias)
{
    QMetaEnumBuilderPrivate *d = d_func();
    if (d)
        d->enumName = alias;
}

/*!
    Returns the meta type of the enumerator.

    \since 6.6
*/
QMetaType QMetaEnumBuilder::metaType() const
{
    if (QMetaEnumBuilderPrivate *d = d_func())
        return d->metaType;
    return QMetaType();
}

/*!
    Sets this enumerator to have the given \c metaType. The is64Bit() flag will
    be set to match \a metaType's size.

    \since 6.6
    \sa metaType()
*/
void QMetaEnumBuilder::setMetaType(QMetaType metaType)
{
    QMetaEnumBuilderPrivate *d = d_func();
    if (d) {
        d->metaType = metaType;
        setIs64Bit(metaType.sizeOf() > 4);
    }
}

/*!
    Returns \c true if this enumerator is used as a flag; otherwise returns
    false.

    \sa setIsFlag()
*/
bool QMetaEnumBuilder::isFlag() const
{
    QMetaEnumBuilderPrivate *d = d_func();
    if (d)
        return d->flags.toInt() & EnumIsFlag;
    else
        return false;
}

/*!
    Sets this enumerator to be used as a flag if \a value is true.

    \sa isFlag()
*/
void QMetaEnumBuilder::setIsFlag(bool value)
{
    QMetaEnumBuilderPrivate *d = d_func();
    if (d)
        d->flags.setFlag(EnumIsFlag, value);
}

/*!
    Return \c true if this enumerator should be considered scoped (C++11 enum class).

    \sa setIsScoped()
*/
bool QMetaEnumBuilder::isScoped() const
{
    QMetaEnumBuilderPrivate *d = d_func();
    if (d)
        return d->flags.toInt() & EnumIsScoped;
    return false;
}

/*!
    Sets this enumerator to be a scoped enum if \value is true

    \sa isScoped()
*/
void QMetaEnumBuilder::setIsScoped(bool value)
{
    QMetaEnumBuilderPrivate *d = d_func();
    if (d)
        d->flags.setFlag(EnumIsScoped, value);
}

/*!
    Return \c true if this enumerations in this enumerator are 64-bit.

    This flag is autoamtically enabled if a 64-bit value is added with addKey().

    \sa setIs64Bit()
*/
bool QMetaEnumBuilder::is64Bit() const
{
    QMetaEnumBuilderPrivate *d = d_func();
    if (d)
        return d->flags.toInt() & EnumIs64Bit;
    return false;
}

/*!
    Sets this enumerator to be 64-bit wide if \a value is true. If \a value is
    false, any stored 64-bit keys will be truncated to 32 bits.

    This flag is autoamtically enabled if a 64-bit value is added with addKey().

    \sa is64Bit()
*/
void QMetaEnumBuilder::setIs64Bit(bool value)
{
    QMetaEnumBuilderPrivate *d = d_func();
    if (d)
        d->flags.setFlag(EnumIs64Bit, value);
}

/*!
    Returns the number of keys.

    \sa key(), addKey()
*/
int QMetaEnumBuilder::keyCount() const
{
    QMetaEnumBuilderPrivate *d = d_func();
    if (d)
        return d->keys.size();
    else
        return 0;
}

/*!
    Returns the key with the given \a index, or an empty QByteArray
    if no such key exists.

    \sa keyCount(), addKey(), value()
*/
QByteArray QMetaEnumBuilder::key(int index) const
{
    QMetaEnumBuilderPrivate *d = d_func();
    if (d && index >= 0 && index < d->keys.size())
        return d->keys[index];
    else
        return QByteArray();
}

/*!
    Returns the value with the given \a index; or returns -1 if there
    is no such value.

    If this is a 64-bit enumeration (see is64Bit()), this function returns the
    low 32-bit portion of the value. Use value64() to obtain the full value
    instead.

    \sa value64(), keyCount(), addKey(), key(), is64Bit()
*/
int QMetaEnumBuilder::value(int index) const
{
    return value64(index).value_or(-1);
}

/*!
    \since 6.9

    Returns the value with the given \a index if it exists; or returns a
    disengaged \c{std::optional} if it doesn't.

    \include qmetaobject.cpp qmetaenum-32bit-signextend-64bit

    \sa keyCount(), key(), addKey()
*/
std::optional<quint64> QMetaEnumBuilder::value64(int index) const
{
    QMetaEnumBuilderPrivate *d = d_func();
    if (d && index >= 0 && index < d->keys.size()) {
        quint64 v = d->values[index];
        if (d->flags & EnumIs64Bit)
            return v;
        return uint(v);     // return only the low 32 bits
    } else {
        return std::nullopt;
    }
}

/*!
    Adds a new key called \a name to this enumerator, associated
    with \a value.  Returns the index of the new key.

    \sa keyCount(), key(), value(), removeKey()
*/
int QMetaEnumBuilder::addKey(const QByteArray &name, int value)
{
    QMetaEnumBuilderPrivate *d = d_func();
    if (d) {
        return d->addKey(name, uint(value));
    } else {
        return -1;
    }
}

/*!
    \since 6.9

    Adds a new key called \a name to this enumerator, associated
    with \a value.  Returns the index of the new key.

    Using the 64-bit version of this function automatically makes this
    enumeration be stored as 64-bit.

    \sa keyCount(), key(), value(), removeKey(), is64Bit()
*/
int QMetaEnumBuilder::addKey(const QByteArray &name, quint64 value)
{
    QMetaEnumBuilderPrivate *d = d_func();
    if (d) {
        setIs64Bit(true);
        return d->addKey(name, value);
    }
    return -1;
}

/*!
    Removes the key at \a index from this enumerator.

    \sa addKey()
*/
void QMetaEnumBuilder::removeKey(int index)
{
    QMetaEnumBuilderPrivate *d = d_func();
    if (d && index >= 0 && index < d->keys.size()) {
        d->keys.removeAt(index);
        d->values.removeAt(index);
    }
}

QT_END_NAMESPACE
