diff options
Diffstat (limited to 'tools')
25 files changed, 3944 insertions, 1107 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..c5831a4 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + + +if(QT_FEATURE_commandlineparser) + add_subdirectory(qscxmlc) +endif() diff --git a/tools/qscxmlc/CMakeLists.txt b/tools/qscxmlc/CMakeLists.txt new file mode 100644 index 0000000..98b7b3e --- /dev/null +++ b/tools/qscxmlc/CMakeLists.txt @@ -0,0 +1,64 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## qscxmlc Tool: +##################################################################### + +qt_internal_include_in_repo_target_set(qtscxml) + +qt_get_tool_target_name(target_name qscxmlc) +qt_internal_add_tool(${target_name} + TOOLS_TARGET Scxml + INSTALL_DIR "${INSTALL_LIBEXECDIR}" + SOURCES + ../../src/scxml/qscxmlcompiler.cpp ../../src/scxml/qscxmlcompiler.h ../../src/scxml/qscxmlcompiler_p.h + ../../src/scxml/qscxmlerror.cpp ../../src/scxml/qscxmlerror.h + ../../src/scxml/qscxmlexecutablecontent.cpp ../../src/scxml/qscxmlexecutablecontent.h ../../src/scxml/qscxmlexecutablecontent_p.h + ../../src/scxml/qscxmlglobals.h + ../../src/scxml/qscxmltabledata.cpp ../../src/scxml/qscxmltabledata.h + generator.cpp generator.h + main.cpp + moc.cpp moc.h + outputrevision.h + qscxmlc.cpp qscxmlc.h + scxmlcppdumper.cpp scxmlcppdumper.h + utils.h + DEFINES + BUILD_QSCXMLC + QT_NO_CAST_FROM_ASCII + QT_NO_CAST_TO_ASCII + INCLUDE_DIRECTORIES + $<TARGET_PROPERTY:Qt::Scxml,INTERFACE_INCLUDE_DIRECTORIES> + $<TARGET_PROPERTY:Qt::ScxmlPrivate,INTERFACE_INCLUDE_DIRECTORIES> + LIBRARIES + Qt::CorePrivate +) +qt_internal_return_unless_building_tools() + +# qscxmlc uses header and source files from Scxml library instead of linking it. These sources use +# includes from the module(using the module include style) so need to sync the module header +# files first to avoid compiler errors. +add_dependencies(${target_name} Scxml_sync_headers) + +set_property(SOURCE ../../src/scxml/qscxmlerror.h PROPERTY SKIP_AUTOMOC ON) +set_property(SOURCE ../../src/scxml/qscxmlcompiler.cpp PROPERTY SKIP_AUTOMOC ON) + +# Resources: +set(templates_resource_files + "cppdatamodel.t" + "data.t" + "decl.t" +) + +qt_internal_add_resource(${target_name} "templates" + PREFIX + "/" + FILES + ${templates_resource_files} + OPTIONS --no-compress +) + + +#### Keys ignored in scope 1:.:.:qscxmlc.pro:<TRUE>: +# _OPTION = "host_build" diff --git a/tools/qscxmlc/data.t b/tools/qscxmlc/data.t index 3926507..1b1e6f7 100644 --- a/tools/qscxmlc/data.t +++ b/tools/qscxmlc/data.t @@ -33,7 +33,9 @@ struct ${classname}::Data: private QScxmlTableData { { Q_ASSERT(id >= QScxmlExecutableContent::NoString); Q_ASSERT(id < ${stringCount}); if (id == QScxmlExecutableContent::NoString) return QString(); - return QString({static_cast<QStringData*>(strings.data + id)}); + const auto dataOffset = strings.offsetsAndSize[id * 2]; + const auto dataSize = strings.offsetsAndSize[id * 2 + 1]; + return QString::fromRawData(reinterpret_cast<const QChar*>(&strings.stringdata[dataOffset]), dataSize); } const qint32 *stateMachineTable() const override final @@ -82,8 +84,8 @@ struct ${classname}::Data: private QScxmlTableData { static QScxmlExecutableContent::ForeachInfo foreaches[]; static const qint32 theStateMachineTable[]; static struct Strings { - QArrayData data[${stringCount}]; - qunicodechar stringdata[${stringdataSize}]; + const uint offsetsAndSize[${stringCount} * 2]; + char16_t stringdata[${stringdataSize}]; } strings; }; @@ -120,10 +122,6 @@ QScxmlExecutableContent::ForeachInfo ${classname}::Data::foreaches[] = { ${foreaches} }; -#define STR_LIT(idx, ofs, len) \ - Q_STATIC_STRING_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ - qptrdiff(offsetof(Strings, stringdata) + ofs * sizeof(qunicodechar) - idx * sizeof(QArrayData)) \ - ) ${classname}::Data::Strings ${classname}::Data::strings = {{ ${strLits} },{ diff --git a/tools/qscxmlc/doc/qscxmlc.qdoc b/tools/qscxmlc/doc/qscxmlc.qdoc index bf38a63..7e46b8a 100644 --- a/tools/qscxmlc/doc/qscxmlc.qdoc +++ b/tools/qscxmlc/doc/qscxmlc.qdoc @@ -1,29 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** 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 Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! \page qscxmlc.html @@ -39,27 +15,41 @@ \section1 Usage - The \c qscxml tool is invoked automatically if the QT variable in the - project file includes \c scxml, and the .scxml file to use is specified - using the \c STATECHARTS variable. + The \c qscxmlc tool is invoked automatically if the project is linked against + the \c scxml library in the project file, and the \c .scxml file to use is specified + using the special build directives \c STATECHARTS or qt6_add_statecharts. - \badcode - QT += scxml - STATECHARTS = MyStatemachine.scxml - \endcode + When using cmake: + + \include qtscxml-module-use.qdocinc cmakebuild + \include qtscxml-module-use.qdocinc cmakestatecharts + + When using qmake: + + \include qtscxml-module-use.qdocinc qmakebuild + \include qtscxml-module-use.qdocinc qmakestatecharts - With above definitions, \c qmake invokes \c qscxmlc to generate - MyStatemachine.h and MyStatemachine.cpp, and adds them to \l [QMake] - HEADERS and \l [QMAKE] SOURCES variables. + With above definitions, \c qmake or \c cmake invokes \c qscxmlc to generate + MyStatemachine.h and MyStatemachine.cpp, and adds them appropriately + to the project as headers and sources. By default, the name of the generated class that implements the state machine corresponds with the \e name attribute of the \c <scxml> root element. + The \c qscxmlc tool can also be invoked manually and the resulting header and + source files can be used as regular source files in a project. When + using these source files as part of a \c cmake project, one must + additionally disable automatic moc in the CMakeLists.txt file as + illustrated by this example: + \code + set_source_files_properties(statemachine.h PROPERTIES SKIP_AUTOMOC TRUE) + \endcode + If you omit this, you will see duplicate symbol errors during compilation. + \section1 Command-Line Options - The \c qscxmlc tool supports the following command-line options, which can be specified using - the \c QSCXMLC_ARGUMENTS variable in the project file: + The \c qscxmlc tool supports the following command-line options: \table \header @@ -67,8 +57,7 @@ \li Description \row \li \c {--namespace <namespace>} - \li Put the generated class(es) in the specified namespace. You can use the - \c QSCXMLC_NAMESPACE variable to specify this in your project file. + \li Put the generated class(es) in the specified namespace. \row \li \c {-o <base/out/name>} \li The base name of the output files. This can include a path. If none is specified, the @@ -90,4 +79,25 @@ state changes with plain QObject::connect() and directly call a method to find out if a state is currently active. \endtable + + The \c qmake and \c CMake project files support the following options: + + \table + \header + \li Option + \li Description + \row + \li \c {QSCXMLC_DIR|OUTPUT_DIRECTORY <directory>} + \li \c QSCXMLC_DIR (qmake) or \c OUTPUT_DIRECTORY (cmake) specifies the directory for + the output files. OUTPUT_DIR (cmake) has been deprecated. + \row + \li \c {QSCXMLC_NAMESPACE|NAMESPACE <namespace>} + \li \c QSCXMLC_NAMESPACE (qmake) or \c NAMESPACE (cmake) specifies the namespace for the + generated classes. + \row + \li \c {QSCXMLC_ARGUMENTS|OPTIONS <options>} + \li \c QSCXMLC_ARGUMENTS (qmake) or \c OPTIONS (cmake) allows specifying additional + options for the \c qscxmlc compiler. QSCXMLC_ARGUMENTS with cmake has been + deprecated. + \endtable */ diff --git a/tools/qscxmlc/generator.cpp b/tools/qscxmlc/generator.cpp index 6febf09..4586c1d 100644 --- a/tools/qscxmlc/generator.cpp +++ b/tools/qscxmlc/generator.cpp @@ -1,32 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtScxml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// Copyright (C) 2019 Olivier Goffart <ogoffart@woboq.com> +// Copyright (C) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "generator.h" +#if 0 // -- QtScxml +#include "cbordevice.h" +#endif // -- QtScxml #include "outputrevision.h" #include "utils.h" #include <QtCore/qmetatype.h> @@ -35,12 +15,19 @@ #include <QtCore/qjsonvalue.h> #include <QtCore/qjsonarray.h> #include <QtCore/qplugin.h> +#include <QtCore/qstringview.h> -#include <private/qmetaobject_p.h> //for the flags. +#include <math.h> #include <stdio.h> +#include <private/qmetaobject_p.h> //for the flags. +#include <private/qplugin_p.h> //for the flags. + QT_BEGIN_NAMESPACE +using namespace QtMiscUtils; + +// -- QtScxml void fprintf(QIODevice &out, const char *fmt, ...) { va_list argp; @@ -61,13 +48,14 @@ void fputs(const char *s, QIODevice &out) { out.write(s); } +// -- QtScxml uint nameToBuiltinType(const QByteArray &name) { if (name.isEmpty()) return 0; - uint tp = QMetaType::type(name.constData()); + uint tp = qMetaTypeTypeInternal(name.constData()); return tp < uint(QMetaType::User) ? tp : uint(QMetaType::UnknownType); } @@ -76,7 +64,7 @@ uint nameToBuiltinType(const QByteArray &name) */ bool isBuiltinType(const QByteArray &type) { - int id = QMetaType::type(type.constData()); + int id = qMetaTypeTypeInternal(type.constData()); if (id == QMetaType::UnknownType) return false; return (id < QMetaType::User); @@ -91,42 +79,73 @@ static const char *metaTypeEnumValueString(int type) QT_FOR_EACH_STATIC_TYPE(RETURN_METATYPENAME_STRING) } #undef RETURN_METATYPENAME_STRING - return 0; + return nullptr; } -Generator::Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes, const QHash<QByteArray, - QByteArray> &knownQObjectClasses, const QHash<QByteArray, - QByteArray> &knownGadgets, QIODevice &outfile) - : out(outfile), cdef(classDef), metaTypes(metaTypes), knownQObjectClasses(knownQObjectClasses) - , knownGadgets(knownGadgets) +// -- QtScxml +Generator::Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes, + const QHash<QByteArray, QByteArray> &knownQObjectClasses, + const QHash<QByteArray, QByteArray> &knownGadgets, + QIODevice &outfile, + bool requireCompleteTypes) + : out(outfile), + cdef(classDef), + metaTypes(metaTypes), + knownQObjectClasses(knownQObjectClasses), + knownGadgets(knownGadgets), + requireCompleteTypes(requireCompleteTypes) { if (cdef->superclassList.size()) - purestSuperClass = cdef->superclassList.first().first; + purestSuperClass = cdef->superclassList.constFirst().classname; } +// -- QtScxml -static inline int lengthOfEscapeSequence(const QByteArray &s, int i) +#if 0 // -- QtScxml +static inline qsizetype lengthOfEscapeSequence(const QByteArray &s, qsizetype i) { - if (s.at(i) != '\\' || i >= s.length() - 1) + if (s.at(i) != '\\' || i >= s.size() - 1) return 1; - const int startPos = i; + const qsizetype startPos = i; ++i; char ch = s.at(i); if (ch == 'x') { ++i; - while (i < s.length() && is_hex_char(s.at(i))) + while (i < s.size() && isHexDigit(s.at(i))) ++i; - } else if (is_octal_char(ch)) { + } else if (isOctalDigit(ch)) { while (i < startPos + 4 - && i < s.length() - && is_octal_char(s.at(i))) { + && i < s.size() + && isOctalDigit(s.at(i))) { ++i; } } else { // single character escape sequence - i = qMin(i + 1, s.length()); + i = qMin(i + 1, s.size()); } return i - startPos; } +// Prints \a s to \a out, breaking it into lines of at most ColumnWidth. The +// opening and closing quotes are NOT included (it's up to the caller). +static void printStringWithIndentation(QIODevice &out, const QByteArray &s) // -- QtScxml +{ + static constexpr int ColumnWidth = 72; + const qsizetype len = s.size(); + qsizetype idx = 0; + + do { + qsizetype spanLen = qMin(ColumnWidth - 2, len - idx); + // don't cut escape sequences at the end of a line + const qsizetype backSlashPos = s.lastIndexOf('\\', idx + spanLen - 1); + if (backSlashPos >= idx) { + const qsizetype escapeLen = lengthOfEscapeSequence(s, backSlashPos); + spanLen = qBound(spanLen, backSlashPos + escapeLen - idx, len - idx); + } + fprintf(out, "\n \"%.*s\"", int(spanLen), s.constData() + idx); + idx += spanLen; + } while (idx < len); +} +#endif // -- QtSxcml + void Generator::strreg(const QByteArray &s) { if (!strings.contains(s)) @@ -135,7 +154,7 @@ void Generator::strreg(const QByteArray &s) int Generator::stridx(const QByteArray &s) { - int i = strings.indexOf(s); + int i = int(strings.indexOf(s)); Q_ASSERT_X(i != -1, Q_FUNC_INFO, "We forgot to register some strings"); return i; } @@ -146,8 +165,8 @@ int Generator::stridx(const QByteArray &s) static int aggregateParameterCount(const QList<FunctionDef> &list) { int sum = 0; - for (int i = 0; i < list.count(); ++i) - sum += list.at(i).arguments.count() + 1; // +1 for return type + for (const FunctionDef &def : list) + sum += int(def.arguments.size()) + 1; // +1 for return type return sum; } @@ -166,31 +185,35 @@ bool Generator::registerableMetaType(const QByteArray &propertyType) return true; } - static const QVector<QByteArray> smartPointers = QVector<QByteArray>() + static const QList<QByteArray> smartPointers = QList<QByteArray>() #define STREAM_SMART_POINTER(SMART_POINTER) << #SMART_POINTER - QT_FOR_EACH_AUTOMATIC_TEMPLATE_SMART_POINTER(STREAM_SMART_POINTER) + QT_FOR_EACH_AUTOMATIC_TEMPLATE_SMART_POINTER(STREAM_SMART_POINTER) #undef STREAM_SMART_POINTER - ; + ; - for (const QByteArray &smartPointer : smartPointers) - if (propertyType.startsWith(smartPointer + "<") && !propertyType.endsWith("&")) + for (const QByteArray &smartPointer : smartPointers) { + QByteArray ba = smartPointer + "<"; + if (propertyType.startsWith(ba) && !propertyType.endsWith("&")) return knownQObjectClasses.contains(propertyType.mid(smartPointer.size() + 1, propertyType.size() - smartPointer.size() - 1 - 1)); + } - static const QVector<QByteArray> oneArgTemplates = QVector<QByteArray>() + static const QList<QByteArray> oneArgTemplates = QList<QByteArray>() #define STREAM_1ARG_TEMPLATE(TEMPLATENAME) << #TEMPLATENAME - QT_FOR_EACH_AUTOMATIC_TEMPLATE_1ARG(STREAM_1ARG_TEMPLATE) + QT_FOR_EACH_AUTOMATIC_TEMPLATE_1ARG(STREAM_1ARG_TEMPLATE) #undef STREAM_1ARG_TEMPLATE - ; - for (const QByteArray &oneArgTemplateType : oneArgTemplates) - if (propertyType.startsWith(oneArgTemplateType + "<") && propertyType.endsWith(">")) { - const int argumentSize = propertyType.size() - oneArgTemplateType.size() - 1 + ; + for (const QByteArray &oneArgTemplateType : oneArgTemplates) { + const QByteArray ba = oneArgTemplateType + "<"; + if (propertyType.startsWith(ba) && propertyType.endsWith(">")) { + const qsizetype argumentSize = propertyType.size() - ba.size() // The closing '>' - 1 // templates inside templates have an extra whitespace char to strip. - (propertyType.at(propertyType.size() - 2) == ' ' ? 1 : 0 ); - const QByteArray templateArg = propertyType.mid(oneArgTemplateType.size() + 1, argumentSize); + const QByteArray templateArg = propertyType.sliced(ba.size(), argumentSize); return isBuiltinType(templateArg) || registerableMetaType(templateArg); } + } return false; } @@ -200,26 +223,41 @@ static bool qualifiedNameEquals(const QByteArray &qualifiedName, const QByteArra { if (qualifiedName == name) return true; - int index = qualifiedName.indexOf("::"); + const qsizetype index = qualifiedName.indexOf("::"); if (index == -1) return false; return qualifiedNameEquals(qualifiedName.mid(index+2), name); } +static QByteArray generateQualifiedClassNameIdentifier(const QByteArray &identifier) +{ + QByteArray qualifiedClassNameIdentifier = identifier; + + // Remove ':'s in the name, but be sure not to create any illegal + // identifiers in the process. (Don't replace with '_', because + // that will create problems with things like NS_::_class.) + qualifiedClassNameIdentifier.replace("::", "SCOPE"); + + // Also, avoid any leading/trailing underscores (we'll concatenate + // the generated name with other prefixes/suffixes, and these latter + // may already include an underscore, leading to two underscores) + qualifiedClassNameIdentifier = "CLASS" + qualifiedClassNameIdentifier + "ENDCLASS"; + return qualifiedClassNameIdentifier; +} + void Generator::generateCode() { - bool isQt = (cdef->classname == "Qt"); bool isQObject = (cdef->classname == "QObject"); bool isConstructible = !cdef->constructorList.isEmpty(); // filter out undeclared enumerators and sets { QList<EnumDef> enumList; - for (int i = 0; i < cdef->enumList.count(); ++i) { - EnumDef def = cdef->enumList.at(i); + for (EnumDef def : std::as_const(cdef->enumList)) { if (cdef->enumDeclarations.contains(def.name)) { enumList += def; } + def.enumName = def.name; QByteArray alias = cdef->flagAliases.value(def.name); if (cdef->enumDeclarations.contains(alias)) { def.name = alias; @@ -238,102 +276,74 @@ void Generator::generateCode() registerFunctionStrings(cdef->slotList); registerFunctionStrings(cdef->methodList); registerFunctionStrings(cdef->constructorList); + registerByteArrayVector(cdef->nonClassSignalList); registerPropertyStrings(); registerEnumStrings(); - QByteArray qualifiedClassNameIdentifier = cdef->qualified; - qualifiedClassNameIdentifier.replace(':', '_'); + const bool hasStaticMetaCall = + (cdef->hasQObject || !cdef->methodList.isEmpty() + || !cdef->propertyList.isEmpty() || !cdef->constructorList.isEmpty()); + + const QByteArray qualifiedClassNameIdentifier = generateQualifiedClassNameIdentifier(cdef->qualified); + + // ensure the qt_meta_stringdata_XXXX_t type is local + fprintf(out, "namespace {\n"); // -// Build stringdata struct +// Build the strings using QtMocHelpers::StringData // - const int constCharArraySizeLimit = 65535; - fprintf(out, "struct qt_meta_stringdata_%s_t {\n", qualifiedClassNameIdentifier.constData()); - fprintf(out, " QByteArrayData data[%d];\n", strings.size()); - { - int stringDataLength = 0; - int stringDataCounter = 0; - for (int i = 0; i < strings.size(); ++i) { - int thisLength = strings.at(i).length() + 1; - stringDataLength += thisLength; - if (stringDataLength / constCharArraySizeLimit) { - // save previous stringdata and start computing the next one. - fprintf(out, " unsigned char stringdata%d[%d];\n", stringDataCounter++, - stringDataLength - thisLength); - stringDataLength = thisLength; - } - } - fprintf(out, " unsigned char stringdata%d[%d];\n", stringDataCounter, stringDataLength); - } - fprintf(out, "};\n"); - - // Macro that expands into a QByteArrayData. The offset member is - // calculated from 1) the offset of the actual characters in the - // stringdata.stringdata member, and 2) the stringdata.data index of the - // QByteArrayData being defined. This calculation relies on the - // QByteArrayData::data() implementation returning simply "this + offset". - fprintf(out, "#define QT_MOC_LITERAL(idx, ofs, len) \\\n" - " Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \\\n" - " qptrdiff(offsetof(qt_meta_stringdata_%s_t, stringdata0) + ofs \\\n" - " - idx * sizeof(QByteArrayData)) \\\n" - " )\n", - qualifiedClassNameIdentifier.constData()); - - fprintf(out, "static const qt_meta_stringdata_%s_t qt_meta_stringdata_%s = {\n", + fprintf(out, "\n#ifdef QT_MOC_HAS_STRINGDATA\n" + "struct qt_meta_stringdata_%s_t {};\n" + "constexpr auto qt_meta_stringdata_%s = QtMocHelpers::stringData(", qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData()); - fprintf(out, " {\n"); { - int idx = 0; - for (int i = 0; i < strings.size(); ++i) { - const QByteArray &str = strings.at(i); - fprintf(out, "QT_MOC_LITERAL(%d, %d, %d)", i, idx, str.length()); - if (i != strings.size() - 1) - fputc(',', out); - const QByteArray comment = str.length() > 32 ? str.left(29) + "..." : str; - fprintf(out, " // \"%s\"\n", comment.constData()); - idx += str.length() + 1; - for (int j = 0; j < str.length(); ++j) { - if (str.at(j) == '\\') { - int cnt = lengthOfEscapeSequence(str, j) - 1; - idx -= cnt; - j += cnt; - } - } + char comma = 0; +// -- QtScxml + for (qsizetype i = 0, end = strings.size(); i < end; ++i) { + if (comma) + fputc(comma, out); + fprintf(out, "\n {"); + const QByteArray s = strings.at(i); + const qsizetype len = s.size(); + for (qsizetype charPos = 0; charPos < len; ++charPos) + fprintf(out, "char(0x%.2x),", static_cast<quint8>(s.at(charPos))); + const bool isLast = (i == end - 1); + fprintf(out, "char(0)%s // %d: %s", isLast ? "}" : "},", i, s.constData()); + comma = ','; } - fprintf(out, " },{\n"); - } +// -- QtScxml -// -// Build stringdata array -// - for (int i = 0; i < strings.size(); ++i) { - QByteArray s = strings.at(i); - int len = s.length(); - for (int charPos = 0; charPos < len; ++charPos) - fprintf(out, "0x%.2x,", static_cast<quint8>(s.at(charPos))); - fprintf(out, "0%s // %d: %s\n", i < strings.size() - 1 ? "," : "", i, s.constData()); } - -// Terminate stringdata struct - fprintf(out, " }};\n"); - fprintf(out, "#undef QT_MOC_LITERAL\n\n"); + fprintf(out, "\n);\n" + "#else // !QT_MOC_HAS_STRINGDATA\n"); + fprintf(out, "#error \"qtmochelpers.h not found or too old.\"\n"); + fprintf(out, "#endif // !QT_MOC_HAS_STRINGDATA\n"); + fprintf(out, "} // unnamed namespace\n\n"); // // build the data array // int index = MetaObjectPrivateFieldCount; - fprintf(out, "static const uint qt_meta_data_%s[] = {\n", qualifiedClassNameIdentifier.constData()); + fprintf(out, "Q_CONSTINIT static const uint qt_meta_data_%s[] = {\n", qualifiedClassNameIdentifier.constData()); fprintf(out, "\n // content:\n"); fprintf(out, " %4d, // revision\n", int(QMetaObjectPrivate::OutputRevision)); fprintf(out, " %4d, // classname\n", stridx(cdef->qualified)); - fprintf(out, " %4d, %4d, // classinfo\n", cdef->classInfoList.count(), cdef->classInfoList.count() ? index : 0); - index += cdef->classInfoList.count() * 2; + fprintf(out, " %4d, %4d, // classinfo\n", int(cdef->classInfoList.size()), int(cdef->classInfoList.size() ? index : 0)); + index += cdef->classInfoList.size() * 2; + + qsizetype methodCount = 0; + if (qAddOverflow(cdef->signalList.size(), cdef->slotList.size(), &methodCount) + || qAddOverflow(cdef->methodList.size(), methodCount, &methodCount)) { +// -- QtScxml + qFatal("internal limit exceeded: the total number of member functions" + " (including signals and slots) is too big."); +// -- QtScxml + } - int methodCount = cdef->signalList.count() + cdef->slotList.count() + cdef->methodList.count(); - fprintf(out, " %4d, %4d, // methods\n", methodCount, methodCount ? index : 0); - index += methodCount * 5; + fprintf(out, " %4" PRIdQSIZETYPE ", %4d, // methods\n", methodCount, methodCount ? index : 0); + index += methodCount * QMetaObjectPrivate::IntsPerMethod; if (cdef->revisionedMethods) index += methodCount; int paramsIndex = index; @@ -343,30 +353,27 @@ void Generator::generateCode() + aggregateParameterCount(cdef->constructorList); index += totalParameterCount * 2 // types and parameter names - methodCount // return "parameters" don't have names - - cdef->constructorList.count(); // "this" parameters don't have names + - int(cdef->constructorList.size()); // "this" parameters don't have names - fprintf(out, " %4d, %4d, // properties\n", cdef->propertyList.count(), cdef->propertyList.count() ? index : 0); - index += cdef->propertyList.count() * 3; - if(cdef->notifyableProperties) - index += cdef->propertyList.count(); - if (cdef->revisionedProperties) - index += cdef->propertyList.count(); - fprintf(out, " %4d, %4d, // enums/sets\n", cdef->enumList.count(), cdef->enumList.count() ? index : 0); + fprintf(out, " %4d, %4d, // properties\n", int(cdef->propertyList.size()), int(cdef->propertyList.size() ? index : 0)); + index += cdef->propertyList.size() * QMetaObjectPrivate::IntsPerProperty; + fprintf(out, " %4d, %4d, // enums/sets\n", int(cdef->enumList.size()), cdef->enumList.size() ? index : 0); int enumsIndex = index; - for (int i = 0; i < cdef->enumList.count(); ++i) - index += 4 + (cdef->enumList.at(i).values.count() * 2); - fprintf(out, " %4d, %4d, // constructors\n", isConstructible ? cdef->constructorList.count() : 0, + for (const EnumDef &def : std::as_const(cdef->enumList)) + index += QMetaObjectPrivate::IntsPerEnum + (def.values.size() * 2); + + fprintf(out, " %4d, %4d, // constructors\n", isConstructible ? int(cdef->constructorList.size()) : 0, isConstructible ? index : 0); int flags = 0; - if (cdef->hasQGadget) { + if (cdef->hasQGadget || cdef->hasQNamespace) { // Ideally, all the classes could have that flag. But this broke classes generated // by qdbusxml2cpp which generate code that require that we call qt_metacall for properties flags |= PropertyAccessInStaticMetaCall; } fprintf(out, " %4d, // flags\n", flags); - fprintf(out, " %4d, // signalCount\n", cdef->signalList.count()); + fprintf(out, " %4d, // signalCount\n", int(cdef->signalList.size())); // @@ -374,20 +381,31 @@ void Generator::generateCode() // generateClassInfos(); + qsizetype propEnumCount = 0; + // all property metatypes + all enum metatypes + 1 for the type of the current class itself + if (qAddOverflow(cdef->propertyList.size(), cdef->enumList.size(), &propEnumCount) + || qAddOverflow(propEnumCount, qsizetype(1), &propEnumCount) + || propEnumCount >= std::numeric_limits<int>::max()) { +// -- QtScxml + qFatal("internal limit exceeded: number of property and enum metatypes is too big."); +// -- QtScxml + } + int initialMetaTypeOffset = int(propEnumCount); + // // Build signals array first, otherwise the signal indices would be wrong // - generateFunctions(cdef->signalList, "signal", MethodSignal, paramsIndex); + generateFunctions(cdef->signalList, "signal", MethodSignal, paramsIndex, initialMetaTypeOffset); // // Build slots array // - generateFunctions(cdef->slotList, "slot", MethodSlot, paramsIndex); + generateFunctions(cdef->slotList, "slot", MethodSlot, paramsIndex, initialMetaTypeOffset); // // Build method array // - generateFunctions(cdef->methodList, "method", MethodMethod, paramsIndex); + generateFunctions(cdef->methodList, "method", MethodMethod, paramsIndex, initialMetaTypeOffset); // // Build method version arrays @@ -421,7 +439,7 @@ void Generator::generateCode() // Build constructors array // if (isConstructible) - generateFunctions(cdef->constructorList, "constructor", MethodConstructor, paramsIndex); + generateFunctions(cdef->constructorList, "constructor", MethodConstructor, paramsIndex, initialMetaTypeOffset); // // Terminate data array @@ -429,41 +447,31 @@ void Generator::generateCode() fprintf(out, "\n 0 // eod\n};\n\n"); // -// Generate internal qt_static_metacall() function -// - const bool hasStaticMetaCall = !isQt && - (cdef->hasQObject || !cdef->methodList.isEmpty() - || !cdef->propertyList.isEmpty() || !cdef->constructorList.isEmpty()); - if (hasStaticMetaCall) - generateStaticMetacall(); - -// // Build extra array // QList<QByteArray> extraList; - QHash<QByteArray, QByteArray> knownExtraMetaObject = knownGadgets; + QMultiHash<QByteArray, QByteArray> knownExtraMetaObject(knownGadgets); knownExtraMetaObject.unite(knownQObjectClasses); - for (int i = 0; i < cdef->propertyList.count(); ++i) { - const PropertyDef &p = cdef->propertyList.at(i); + for (const PropertyDef &p : std::as_const(cdef->propertyList)) { if (isBuiltinType(p.type)) continue; if (p.type.contains('*') || p.type.contains('<') || p.type.contains('>')) continue; - int s = p.type.lastIndexOf("::"); + const qsizetype s = p.type.lastIndexOf("::"); if (s <= 0) continue; QByteArray unqualifiedScope = p.type.left(s); // The scope may be a namespace for example, so it's only safe to include scopes that are known QObjects (QTBUG-2151) - QHash<QByteArray, QByteArray>::ConstIterator scopeIt; + QMultiHash<QByteArray, QByteArray>::ConstIterator scopeIt; QByteArray thisScope = cdef->qualified; do { - int s = thisScope.lastIndexOf("::"); + const qsizetype s = thisScope.lastIndexOf("::"); thisScope = thisScope.left(s); QByteArray currentScope = thisScope.isEmpty() ? unqualifiedScope : thisScope + "::" + unqualifiedScope; scopeIt = knownExtraMetaObject.constFind(currentScope); @@ -486,10 +494,10 @@ void Generator::generateCode() // QTBUG-20639 - Accept non-local enums for QML signal/slot parameters. // Look for any scoped enum declarations, and add those to the list // of extra/related metaobjects for this object. - QList<QByteArray> enumKeys = cdef->enumDeclarations.keys(); - for (int i = 0; i < enumKeys.count(); ++i) { - const QByteArray &enumKey = enumKeys[i]; - int s = enumKey.lastIndexOf("::"); + for (auto it = cdef->enumDeclarations.keyBegin(), + end = cdef->enumDeclarations.keyEnd(); it != end; ++it) { + const QByteArray &enumKey = *it; + const qsizetype s = enumKey.lastIndexOf("::"); if (s > 0) { QByteArray scope = enumKey.left(s); if (scope != "Qt" && !qualifiedNameEquals(cdef->qualified, scope) && !extraList.contains(scope)) @@ -497,44 +505,114 @@ void Generator::generateCode() } } +// +// Generate meta object link to parent meta objects +// + if (!extraList.isEmpty()) { - fprintf(out, "static const QMetaObject * const qt_meta_extradata_%s[] = {\n ", qualifiedClassNameIdentifier.constData()); - for (int i = 0; i < extraList.count(); ++i) { - fprintf(out, " &%s::staticMetaObject,\n", extraList.at(i).constData()); - } + fprintf(out, "Q_CONSTINIT static const QMetaObject::SuperData qt_meta_extradata_%s[] = {\n", + qualifiedClassNameIdentifier.constData()); + for (const QByteArray &ba : std::as_const(extraList)) + fprintf(out, " QMetaObject::SuperData::link<%s::staticMetaObject>(),\n", ba.constData()); + fprintf(out, " nullptr\n};\n\n"); } // // Finally create and initialize the static meta object // - if (isQt) - fprintf(out, "const QMetaObject QObject::staticQtMetaObject = {\n"); - else - fprintf(out, "const QMetaObject %s::staticMetaObject = {\n", cdef->qualified.constData()); + fprintf(out, "Q_CONSTINIT const QMetaObject %s::staticMetaObject = { {\n", + cdef->qualified.constData()); if (isQObject) - fprintf(out, " { nullptr, "); - else if (cdef->superclassList.size() && (!cdef->hasQGadget || knownGadgets.contains(purestSuperClass))) - fprintf(out, " { &%s::staticMetaObject, ", purestSuperClass.constData()); + fprintf(out, " nullptr,\n"); + else if (cdef->superclassList.size() && !cdef->hasQGadget && !cdef->hasQNamespace) // for qobject, we know the super class must have a static metaobject + fprintf(out, " QMetaObject::SuperData::link<%s::staticMetaObject>(),\n", purestSuperClass.constData()); + else if (cdef->superclassList.size()) // for gadgets we need to query at compile time for it + fprintf(out, " QtPrivate::MetaObjectForType<%s>::value,\n", purestSuperClass.constData()); else - fprintf(out, " { nullptr, "); - fprintf(out, "qt_meta_stringdata_%s.data,\n" - " qt_meta_data_%s, ", qualifiedClassNameIdentifier.constData(), + fprintf(out, " nullptr,\n"); + fprintf(out, " qt_meta_stringdata_%s.offsetsAndSizes,\n" + " qt_meta_data_%s,\n", qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData()); if (hasStaticMetaCall) - fprintf(out, " qt_static_metacall, "); + fprintf(out, " qt_static_metacall,\n"); else - fprintf(out, " nullptr, "); + fprintf(out, " nullptr,\n"); if (extraList.isEmpty()) - fprintf(out, "nullptr, "); + fprintf(out, " nullptr,\n"); else - fprintf(out, "qt_meta_extradata_%s, ", qualifiedClassNameIdentifier.constData()); - fprintf(out, "nullptr}\n};\n\n"); + fprintf(out, " qt_meta_extradata_%s,\n", qualifiedClassNameIdentifier.constData()); + + const char *comma = ""; + const bool requireCompleteness = requireCompleteTypes || cdef->requireCompleteMethodTypes; + auto stringForType = [requireCompleteness](const QByteArray &type, bool forceComplete) -> QByteArray { + const char *forceCompleteType = forceComplete ? ", std::true_type>" : ", std::false_type>"; + if (requireCompleteness) + return type; + return "QtPrivate::TypeAndForceComplete<" % type % forceCompleteType; + }; + if (!requireCompleteness) { + fprintf(out, " qt_incomplete_metaTypeArray<qt_meta_stringdata_%s_t", qualifiedClassNameIdentifier.constData()); + comma = ","; + } else { + fprintf(out, " qt_metaTypeArray<"); + } + // metatypes for properties + for (const PropertyDef &p : std::as_const(cdef->propertyList)) { + fprintf(out, "%s\n // property '%s'\n %s", + comma, p.name.constData(), stringForType(p.type, true).constData()); + comma = ","; + } - if(isQt) - return; + // metatypes for enums + for (const EnumDef &e : std::as_const(cdef->enumList)) { + fprintf(out, "%s\n // enum '%s'\n %s", + comma, e.name.constData(), stringForType(e.qualifiedType(cdef), true).constData()); + comma = ","; + } + + // type name for the Q_OJBECT/GADGET itself, void for namespaces + auto ownType = !cdef->hasQNamespace ? cdef->classname.data() : "void"; + fprintf(out, "%s\n // Q_OBJECT / Q_GADGET\n %s", + comma, stringForType(ownType, true).constData()); + comma = ","; + + // metatypes for all exposed methods + // because we definitely printed something above, this section doesn't need comma control + const auto allMethods = {&cdef->signalList, &cdef->slotList, &cdef->methodList}; + for (const QList<FunctionDef> *methodContainer : allMethods) { + for (const FunctionDef &fdef : *methodContainer) { + fprintf(out, ",\n // method '%s'\n %s", + fdef.name.constData(), stringForType(fdef.type.name, false).constData()); + for (const auto &argument: fdef.arguments) + fprintf(out, ",\n %s", stringForType(argument.type.name, false).constData()); + } + } + + // but constructors have no return types, so this needs comma control again + for (const FunctionDef &fdef : std::as_const(cdef->constructorList)) { + if (fdef.arguments.isEmpty()) + continue; + + fprintf(out, "%s\n // constructor '%s'", comma, fdef.name.constData()); + comma = ""; + for (const auto &argument: fdef.arguments) { + fprintf(out, "%s\n %s", comma, + stringForType(argument.type.name, false).constData()); + comma = ","; + } + } + fprintf(out, "\n >,\n"); + + fprintf(out, " nullptr\n} };\n\n"); + +// +// Generate internal qt_static_metacall() function +// + if (hasStaticMetaCall) + generateStaticMetacall(); if (!cdef->hasQObject) return; @@ -542,30 +620,35 @@ void Generator::generateCode() fprintf(out, "\nconst QMetaObject *%s::metaObject() const\n{\n return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;\n}\n", cdef->qualified.constData()); + // // Generate smart cast function // fprintf(out, "\nvoid *%s::qt_metacast(const char *_clname)\n{\n", cdef->qualified.constData()); fprintf(out, " if (!_clname) return nullptr;\n"); - fprintf(out, " if (!strcmp(_clname, reinterpret_cast<const char *>(\n" - " qt_meta_stringdata_%s.stringdata0)))\n" - " return static_cast<void*>(const_cast< %s*>(this));\n", - qualifiedClassNameIdentifier.constData(), cdef->classname.constData()); - for (int i = 1; i < cdef->superclassList.size(); ++i) { // for all superclasses but the first one - if (cdef->superclassList.at(i).second == FunctionDef::Private) - continue; - const char *cname = cdef->superclassList.at(i).first.constData(); - fprintf(out, " if (!strcmp(_clname, \"%s\"))\n return static_cast< %s*>(const_cast< %s*>(this));\n", - cname, cname, cdef->classname.constData()); + fprintf(out, " if (!strcmp(_clname, qt_meta_stringdata_%s.stringdata0))\n" + " return static_cast<void*>(this);\n", + qualifiedClassNameIdentifier.constData()); + + // for all superclasses but the first one + if (cdef->superclassList.size() > 1) { + auto it = cdef->superclassList.cbegin() + 1; + const auto end = cdef->superclassList.cend(); + for (; it != end; ++it) { + if (it->access == FunctionDef::Private) + continue; + const char *cname = it->classname.constData(); + fprintf(out, " if (!strcmp(_clname, \"%s\"))\n return static_cast< %s*>(this);\n", + cname, cname); + } } - for (int i = 0; i < cdef->interfaceList.size(); ++i) { - const QList<ClassDef::Interface> &iface = cdef->interfaceList.at(i); - for (int j = 0; j < iface.size(); ++j) { + + for (const QList<ClassDef::Interface> &iface : std::as_const(cdef->interfaceList)) { + for (qsizetype j = 0; j < iface.size(); ++j) { fprintf(out, " if (!strcmp(_clname, %s))\n return ", iface.at(j).interfaceId.constData()); - for (int k = j; k >= 0; --k) + for (qsizetype k = j; k >= 0; --k) fprintf(out, "static_cast< %s*>(", iface.at(k).className.constData()); - fprintf(out, "const_cast< %s*>(this)%s;\n", - cdef->classname.constData(), QByteArray(j+1, ')').constData()); + fprintf(out, "this%s;\n", QByteArray(j + 1, ')').constData()); } } if (!purestSuperClass.isEmpty() && !isQObject) { @@ -584,21 +667,51 @@ void Generator::generateCode() // // Generate internal signal functions // - for (int signalindex = 0; signalindex < cdef->signalList.size(); ++signalindex) - generateSignal(&cdef->signalList[signalindex], signalindex); + for (int signalindex = 0; signalindex < int(cdef->signalList.size()); ++signalindex) + generateSignal(&cdef->signalList.at(signalindex), signalindex); - fprintf(out, "\n"); // // Generate plugin meta data // -// generatePluginMetaData(); +#if 0 // -- QtScxml + generatePluginMetaData(); +#endif // -- QtScxml + +// +// Generate function to make sure the non-class signals exist in the parent classes +// + if (!cdef->nonClassSignalList.isEmpty()) { + fprintf(out, "namespace CheckNotifySignalValidity_%s {\n", qualifiedClassNameIdentifier.constData()); + for (const QByteArray &nonClassSignal : std::as_const(cdef->nonClassSignalList)) { + const auto propertyIt = std::find_if(cdef->propertyList.constBegin(), + cdef->propertyList.constEnd(), + [&nonClassSignal](const PropertyDef &p) { + return nonClassSignal == p.notify; + }); + // must find something, otherwise checkProperties wouldn't have inserted an entry into nonClassSignalList + Q_ASSERT(propertyIt != cdef->propertyList.constEnd()); + fprintf(out, "template<typename T> using has_nullary_%s = decltype(std::declval<T>().%s());\n", + nonClassSignal.constData(), + nonClassSignal.constData()); + const auto &propertyType = propertyIt->type; + fprintf(out, "template<typename T> using has_unary_%s = decltype(std::declval<T>().%s(std::declval<%s>()));\n", + nonClassSignal.constData(), + nonClassSignal.constData(), + propertyType.constData()); + fprintf(out, "static_assert(qxp::is_detected_v<has_nullary_%s, %s> || qxp::is_detected_v<has_unary_%s, %s>,\n" + " \"NOTIFY signal %s does not exist in class (or is private in its parent)\");\n", + nonClassSignal.constData(), cdef->qualified.constData(), + nonClassSignal.constData(), cdef->qualified.constData(), + nonClassSignal.constData()); + } + fprintf(out, "}\n"); + } } void Generator::registerClassInfoStrings() { - for (int i = 0; i < cdef->classInfoList.size(); ++i) { - const ClassInfoDef &c = cdef->classInfoList.at(i); + for (const ClassInfoDef &c : std::as_const(cdef->classInfoList)) { strreg(c.name); strreg(c.value); } @@ -611,25 +724,19 @@ void Generator::generateClassInfos() fprintf(out, "\n // classinfo: key, value\n"); - for (int i = 0; i < cdef->classInfoList.size(); ++i) { - const ClassInfoDef &c = cdef->classInfoList.at(i); + for (const ClassInfoDef &c : std::as_const(cdef->classInfoList)) fprintf(out, " %4d, %4d,\n", stridx(c.name), stridx(c.value)); - } } void Generator::registerFunctionStrings(const QList<FunctionDef> &list) { - for (int i = 0; i < list.count(); ++i) { - const FunctionDef &f = list.at(i); - + for (const FunctionDef &f : list) { strreg(f.name); if (!isBuiltinType(f.normalizedType)) strreg(f.normalizedType); strreg(f.tag); - int argsCount = f.arguments.count(); - for (int j = 0; j < argsCount; ++j) { - const ArgumentDef &a = f.arguments.at(j); + for (const ArgumentDef &a : f.arguments) { if (!isBuiltinType(a.normalizedType)) strreg(a.normalizedType); strreg(a.name); @@ -637,17 +744,22 @@ void Generator::registerFunctionStrings(const QList<FunctionDef> &list) } } -void Generator::generateFunctions(const QList<FunctionDef> &list, const char *functype, int type, int ¶msIndex) +void Generator::registerByteArrayVector(const QList<QByteArray> &list) +{ + for (const QByteArray &ba : list) + strreg(ba); +} + +void Generator::generateFunctions(const QList<FunctionDef> &list, const char *functype, int type, + int ¶msIndex, int &initialMetatypeOffset) { if (list.isEmpty()) return; - fprintf(out, "\n // %ss: name, argc, parameters, tag, flags\n", functype); - - for (int i = 0; i < list.count(); ++i) { - const FunctionDef &f = list.at(i); + fprintf(out, "\n // %ss: name, argc, parameters, tag, flags, initial metatype offsets\n", functype); + for (const FunctionDef &f : list) { QByteArray comment; - unsigned char flags = type; + uint flags = type; if (f.access == FunctionDef::Private) { flags |= AccessPrivate; comment.append("Private"); @@ -675,22 +787,27 @@ void Generator::generateFunctions(const QList<FunctionDef> &list, const char *fu comment.append(" | MethodRevisioned"); } - int argc = f.arguments.count(); - fprintf(out, " %4d, %4d, %4d, %4d, 0x%02x /* %s */,\n", - stridx(f.name), argc, paramsIndex, stridx(f.tag), flags, comment.constData()); + if (f.isConst) { + flags |= MethodIsConst; + comment.append(" | MethodIsConst "); + } + + const int argc = int(f.arguments.size()); + fprintf(out, " %4d, %4d, %4d, %4d, 0x%02x, %4d /* %s */,\n", + stridx(f.name), argc, paramsIndex, stridx(f.tag), flags, initialMetatypeOffset, comment.constData()); paramsIndex += 1 + argc * 2; + // constructors don't have a return type + initialMetatypeOffset += (f.isConstructor ? 0 : 1) + argc; } } void Generator::generateFunctionRevisions(const QList<FunctionDef> &list, const char *functype) { - if (list.count()) + if (list.size()) fprintf(out, "\n // %ss: revision\n", functype); - for (int i = 0; i < list.count(); ++i) { - const FunctionDef &f = list.at(i); + for (const FunctionDef &f : list) fprintf(out, " %4d,\n", f.revision); - } } void Generator::generateFunctionParameters(const QList<FunctionDef> &list, const char *functype) @@ -698,25 +815,22 @@ void Generator::generateFunctionParameters(const QList<FunctionDef> &list, const if (list.isEmpty()) return; fprintf(out, "\n // %ss: parameters\n", functype); - for (int i = 0; i < list.count(); ++i) { - const FunctionDef &f = list.at(i); + for (const FunctionDef &f : list) { fprintf(out, " "); // Types - int argsCount = f.arguments.count(); - for (int j = -1; j < argsCount; ++j) { - if (j > -1) - fputc(' ', out); - const QByteArray &typeName = (j < 0) ? f.normalizedType : f.arguments.at(j).normalizedType; - generateTypeInfo(typeName, /*allowEmptyName=*/f.isConstructor); + const bool allowEmptyName = f.isConstructor; + generateTypeInfo(f.normalizedType, allowEmptyName); + fputc(',', out); + for (const ArgumentDef &arg : f.arguments) { + fputc(' ', out); + generateTypeInfo(arg.normalizedType, allowEmptyName); fputc(',', out); } // Parameter names - for (int j = 0; j < argsCount; ++j) { - const ArgumentDef &arg = f.arguments.at(j); + for (const ArgumentDef &arg : f.arguments) fprintf(out, " %4d,", stridx(arg.name)); - } fprintf(out, "\n"); } @@ -749,8 +863,7 @@ void Generator::generateTypeInfo(const QByteArray &typeName, bool allowEmptyName void Generator::registerPropertyStrings() { - for (int i = 0; i < cdef->propertyList.count(); ++i) { - const PropertyDef &p = cdef->propertyList.at(i); + for (const PropertyDef &p : std::as_const(cdef->propertyList)) { strreg(p.name); if (!isBuiltinType(p.type)) strreg(p.type); @@ -763,10 +876,9 @@ void Generator::generateProperties() // Create meta data // - if (cdef->propertyList.count()) + if (cdef->propertyList.size()) fprintf(out, "\n // properties: name, type, flags\n"); - for (int i = 0; i < cdef->propertyList.count(); ++i) { - const PropertyDef &p = cdef->propertyList.at(i); + for (const PropertyDef &p : std::as_const(cdef->propertyList)) { uint flags = Invalid; if (!isBuiltinType(p.type)) flags |= EnumOrFlag; @@ -779,81 +891,52 @@ void Generator::generateProperties() if (p.stdCppSet()) flags |= StdCppSet; } + if (!p.reset.isEmpty()) flags |= Resettable; -// if (p.override) -// flags |= Override; - - if (p.designable.isEmpty()) - flags |= ResolveDesignable; - else if (p.designable != "false") + if (p.designable != "false") flags |= Designable; - if (p.scriptable.isEmpty()) - flags |= ResolveScriptable; - else if (p.scriptable != "false") + if (p.scriptable != "false") flags |= Scriptable; - if (p.stored.isEmpty()) - flags |= ResolveStored; - else if (p.stored != "false") + if (p.stored != "false") flags |= Stored; - if (p.editable.isEmpty()) - flags |= ResolveEditable; - else if (p.editable != "false") - flags |= Editable; - - if (p.user.isEmpty()) - flags |= ResolveUser; - else if (p.user != "false") + if (p.user != "false") flags |= User; - if (p.notifyId != -1) - flags |= Notify; - - if (p.revision > 0) - flags |= Revisioned; - if (p.constant) flags |= Constant; if (p.final) flags |= Final; + if (p.required) + flags |= Required; + + if (!p.bind.isEmpty()) + flags |= Bindable; fprintf(out, " %4d, ", stridx(p.name)); generateTypeInfo(p.type); - fprintf(out, ", 0x%.8x,\n", flags); - } - - if(cdef->notifyableProperties) { - fprintf(out, "\n // properties: notify_signal_id\n"); - for (int i = 0; i < cdef->propertyList.count(); ++i) { - const PropertyDef &p = cdef->propertyList.at(i); - if(p.notifyId == -1) - fprintf(out, " %4d,\n", - 0); - else - fprintf(out, " %4d,\n", - p.notifyId); - } - } - if (cdef->revisionedProperties) { - fprintf(out, "\n // properties: revision\n"); - for (int i = 0; i < cdef->propertyList.count(); ++i) { - const PropertyDef &p = cdef->propertyList.at(i); - fprintf(out, " %4d,\n", p.revision); + int notifyId = p.notifyId; + if (p.notifyId < -1) { + // signal is in parent class + const int indexInStrings = int(strings.indexOf(p.notify)); + notifyId = indexInStrings | IsUnresolvedSignal; } + fprintf(out, ", 0x%.8x, uint(%d), %d,\n", flags, notifyId, p.revision); } } void Generator::registerEnumStrings() { - for (int i = 0; i < cdef->enumList.count(); ++i) { - const EnumDef &e = cdef->enumList.at(i); + for (const EnumDef &e : std::as_const(cdef->enumList)) { strreg(e.name); - for (int j = 0; j < e.values.count(); ++j) - strreg(e.values.at(j)); + if (!e.enumName.isNull()) + strreg(e.enumName); + for (const QByteArray &val : e.values) + strreg(val); } } @@ -862,27 +945,31 @@ void Generator::generateEnums(int index) if (cdef->enumDeclarations.isEmpty()) return; - fprintf(out, "\n // enums: name, flags, count, data\n"); - index += 4 * cdef->enumList.count(); + fprintf(out, "\n // enums: name, alias, flags, count, data\n"); + index += QMetaObjectPrivate::IntsPerEnum * cdef->enumList.size(); int i; - for (i = 0; i < cdef->enumList.count(); ++i) { + for (i = 0; i < cdef->enumList.size(); ++i) { const EnumDef &e = cdef->enumList.at(i); - fprintf(out, " %4d, 0x%.1x, %4d, %4d,\n", + int flags = 0; + if (cdef->enumDeclarations.value(e.name)) + flags |= EnumIsFlag; + if (e.isEnumClass) + flags |= EnumIsScoped; + fprintf(out, " %4d, %4d, 0x%.1x, %4d, %4d,\n", stridx(e.name), - cdef->enumDeclarations.value(e.name) ? 1 : 0, - e.values.count(), + e.enumName.isNull() ? stridx(e.name) : stridx(e.enumName), + flags, + int(e.values.size()), index); - index += e.values.count() * 2; + index += e.values.size() * 2; } fprintf(out, "\n // enum data: key, value\n"); - for (i = 0; i < cdef->enumList.count(); ++i) { - const EnumDef &e = cdef->enumList.at(i); - for (int j = 0; j < e.values.count(); ++j) { - const QByteArray &val = e.values.at(j); + for (const EnumDef &e : std::as_const(cdef->enumList)) { + for (const QByteArray &val : e.values) { QByteArray code = cdef->qualified.constData(); if (e.isEnumClass) - code += "::" + e.name; + code += "::" + (e.enumName.isNull() ? e.name : e.enumName); code += "::" + val; fprintf(out, " %4d, uint(%s),\n", stridx(val), code.constData()); @@ -902,8 +989,6 @@ void Generator::generateMetacall() fprintf(out, " _id = %s::qt_metacall(_c, _id, _a);\n", superClass.constData()); } - fprintf(out, " if (_id < 0)\n return _id;\n"); - fprintf(out, " "); bool needElse = false; QList<FunctionDef> methodList; @@ -911,156 +996,54 @@ void Generator::generateMetacall() methodList += cdef->slotList; methodList += cdef->methodList; + // If there are no methods or properties, we will return _id anyway, so + // don't emit this comparison -- it is unnecessary, and it makes coverity + // unhappy. + if (methodList.size() || cdef->propertyList.size()) { + fprintf(out, " if (_id < 0)\n return _id;\n"); + } + + fprintf(out, " "); + if (methodList.size()) { needElse = true; fprintf(out, "if (_c == QMetaObject::InvokeMetaMethod) {\n"); - fprintf(out, " if (_id < %d)\n", methodList.size()); + fprintf(out, " if (_id < %d)\n", int(methodList.size())); fprintf(out, " qt_static_metacall(this, _c, _id, _a);\n"); - fprintf(out, " _id -= %d;\n }", methodList.size()); + fprintf(out, " _id -= %d;\n }", int(methodList.size())); fprintf(out, " else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {\n"); - fprintf(out, " if (_id < %d)\n", methodList.size()); + fprintf(out, " if (_id < %d)\n", int(methodList.size())); if (methodsWithAutomaticTypesHelper(methodList).isEmpty()) - fprintf(out, " *reinterpret_cast<int*>(_a[0]) = -1;\n"); + fprintf(out, " *reinterpret_cast<QMetaType *>(_a[0]) = QMetaType();\n"); else fprintf(out, " qt_static_metacall(this, _c, _id, _a);\n"); - fprintf(out, " _id -= %d;\n }", methodList.size()); + fprintf(out, " _id -= %d;\n }", int(methodList.size())); } if (cdef->propertyList.size()) { - bool needDesignable = false; - bool needScriptable = false; - bool needStored = false; - bool needEditable = false; - bool needUser = false; - for (int i = 0; i < cdef->propertyList.size(); ++i) { - const PropertyDef &p = cdef->propertyList.at(i); - needDesignable |= p.designable.endsWith(')'); - needScriptable |= p.scriptable.endsWith(')'); - needStored |= p.stored.endsWith(')'); - needEditable |= p.editable.endsWith(')'); - needUser |= p.user.endsWith(')'); - } - - fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n "); if (needElse) fprintf(out, "else "); fprintf(out, "if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty\n" - " || _c == QMetaObject::ResetProperty || _c == QMetaObject::RegisterPropertyMetaType) {\n" + " || _c == QMetaObject::ResetProperty || _c == QMetaObject::BindableProperty\n" + " || _c == QMetaObject::RegisterPropertyMetaType) {\n" " qt_static_metacall(this, _c, _id, _a);\n" - " _id -= %d;\n }", cdef->propertyList.count()); - - fprintf(out, " else "); - fprintf(out, "if (_c == QMetaObject::QueryPropertyDesignable) {\n"); - if (needDesignable) { - fprintf(out, " bool *_b = reinterpret_cast<bool*>(_a[0]);\n"); - fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { - const PropertyDef &p = cdef->propertyList.at(propindex); - if (!p.designable.endsWith(')')) - continue; - fprintf(out, " case %d: *_b = %s; break;\n", - propindex, p.designable.constData()); - } - fprintf(out, " default: break;\n"); - fprintf(out, " }\n"); - } - fprintf(out, - " _id -= %d;\n" - " }", cdef->propertyList.count()); - - fprintf(out, " else "); - fprintf(out, "if (_c == QMetaObject::QueryPropertyScriptable) {\n"); - if (needScriptable) { - fprintf(out, " bool *_b = reinterpret_cast<bool*>(_a[0]);\n"); - fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { - const PropertyDef &p = cdef->propertyList.at(propindex); - if (!p.scriptable.endsWith(')')) - continue; - fprintf(out, " case %d: *_b = %s; break;\n", - propindex, p.scriptable.constData()); - } - fprintf(out, " default: break;\n"); - fprintf(out, " }\n"); - } - fprintf(out, - " _id -= %d;\n" - " }", cdef->propertyList.count()); - - fprintf(out, " else "); - fprintf(out, "if (_c == QMetaObject::QueryPropertyStored) {\n"); - if (needStored) { - fprintf(out, " bool *_b = reinterpret_cast<bool*>(_a[0]);\n"); - fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { - const PropertyDef &p = cdef->propertyList.at(propindex); - if (!p.stored.endsWith(')')) - continue; - fprintf(out, " case %d: *_b = %s; break;\n", - propindex, p.stored.constData()); - } - fprintf(out, " default: break;\n"); - fprintf(out, " }\n"); - } - fprintf(out, - " _id -= %d;\n" - " }", cdef->propertyList.count()); - - fprintf(out, " else "); - fprintf(out, "if (_c == QMetaObject::QueryPropertyEditable) {\n"); - if (needEditable) { - fprintf(out, " bool *_b = reinterpret_cast<bool*>(_a[0]);\n"); - fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { - const PropertyDef &p = cdef->propertyList.at(propindex); - if (!p.editable.endsWith(')')) - continue; - fprintf(out, " case %d: *_b = %s; break;\n", - propindex, p.editable.constData()); - } - fprintf(out, " default: break;\n"); - fprintf(out, " }\n"); - } - fprintf(out, - " _id -= %d;\n" - " }", cdef->propertyList.count()); - - - fprintf(out, " else "); - fprintf(out, "if (_c == QMetaObject::QueryPropertyUser) {\n"); - if (needUser) { - fprintf(out, " bool *_b = reinterpret_cast<bool*>(_a[0]);\n"); - fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { - const PropertyDef &p = cdef->propertyList.at(propindex); - if (!p.user.endsWith(')')) - continue; - fprintf(out, " case %d: *_b = %s; break;\n", - propindex, p.user.constData()); - } - fprintf(out, " default: break;\n"); - fprintf(out, " }\n"); - } - fprintf(out, - " _id -= %d;\n" - " }", cdef->propertyList.count()); - - fprintf(out, "\n#endif // QT_NO_PROPERTIES"); + " _id -= %d;\n }", int(cdef->propertyList.size())); } - if (methodList.size() || cdef->signalList.size() || cdef->propertyList.size()) + if (methodList.size() || cdef->propertyList.size()) fprintf(out, "\n "); fprintf(out,"return _id;\n}\n"); } +// ### Qt 7 (6.x?): remove QMultiMap<QByteArray, int> Generator::automaticPropertyMetaTypesHelper() { QMultiMap<QByteArray, int> automaticPropertyMetaTypes; - for (int i = 0; i < cdef->propertyList.size(); ++i) { + for (int i = 0; i < int(cdef->propertyList.size()); ++i) { const QByteArray propertyType = cdef->propertyList.at(i).type; if (registerableMetaType(propertyType) && !isBuiltinType(propertyType)) automaticPropertyMetaTypes.insert(propertyType, i); @@ -1068,12 +1051,13 @@ QMultiMap<QByteArray, int> Generator::automaticPropertyMetaTypesHelper() return automaticPropertyMetaTypes; } -QMap<int, QMultiMap<QByteArray, int> > Generator::methodsWithAutomaticTypesHelper(const QList<FunctionDef> &methodList) +QMap<int, QMultiMap<QByteArray, int>> +Generator::methodsWithAutomaticTypesHelper(const QList<FunctionDef> &methodList) { QMap<int, QMultiMap<QByteArray, int> > methodsWithAutomaticTypes; for (int i = 0; i < methodList.size(); ++i) { const FunctionDef &f = methodList.at(i); - for (int j = 0; j < f.arguments.count(); ++j) { + for (int j = 0; j < f.arguments.size(); ++j) { const QByteArray argType = f.arguments.at(j).normalizedType; if (registerableMetaType(argType) && !isBuiltinType(argType)) methodsWithAutomaticTypes[i].insert(argType, j); @@ -1090,30 +1074,43 @@ void Generator::generateStaticMetacall() bool needElse = false; bool isUsed_a = false; + const auto generateCtorArguments = [&](int ctorindex) { + const FunctionDef &f = cdef->constructorList.at(ctorindex); + Q_ASSERT(!f.isPrivateSignal); // That would be a strange ctor indeed + int offset = 1; + + const auto begin = f.arguments.cbegin(); + const auto end = f.arguments.cend(); + for (auto it = begin; it != end; ++it) { + const ArgumentDef &a = *it; + if (it != begin) + fprintf(out, ","); + fprintf(out, "(*reinterpret_cast<%s>(_a[%d]))", + a.typeNameForCast.constData(), offset++); + } + }; + if (!cdef->constructorList.isEmpty()) { fprintf(out, " if (_c == QMetaObject::CreateInstance) {\n"); fprintf(out, " switch (_id) {\n"); - for (int ctorindex = 0; ctorindex < cdef->constructorList.count(); ++ctorindex) { + const int ctorend = int(cdef->constructorList.size()); + for (int ctorindex = 0; ctorindex < ctorend; ++ctorindex) { fprintf(out, " case %d: { %s *_r = new %s(", ctorindex, cdef->classname.constData(), cdef->classname.constData()); - const FunctionDef &f = cdef->constructorList.at(ctorindex); - int offset = 1; - - int argsCount = f.arguments.count(); - for (int j = 0; j < argsCount; ++j) { - const ArgumentDef &a = f.arguments.at(j); - if (j) - fprintf(out, ","); - fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))", a.typeNameForCast.constData(), offset++); - } - if (f.isPrivateSignal) { - if (argsCount > 0) - fprintf(out, ", "); - fprintf(out, "%s", QByteArray("QPrivateSignal()").constData()); - } + generateCtorArguments(ctorindex); fprintf(out, ");\n"); fprintf(out, " if (_a[0]) *reinterpret_cast<%s**>(_a[0]) = _r; } break;\n", - cdef->hasQGadget ? "void" : "QObject"); + (cdef->hasQGadget || cdef->hasQNamespace) ? "void" : "QObject"); + } + fprintf(out, " default: break;\n"); + fprintf(out, " }\n"); + fprintf(out, " } else if (_c == QMetaObject::ConstructInPlace) {\n"); + fprintf(out, " switch (_id) {\n"); + for (int ctorindex = 0; ctorindex < ctorend; ++ctorindex) { + fprintf(out, " case %d: { new (_a[0]) %s(", + ctorindex, cdef->classname.constData()); + generateCtorArguments(ctorindex); + fprintf(out, "); } break;\n"); } fprintf(out, " default: break;\n"); fprintf(out, " }\n"); @@ -1137,25 +1134,23 @@ void Generator::generateStaticMetacall() #ifndef QT_NO_DEBUG fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n"); #endif - fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + fprintf(out, " auto *_t = static_cast<%s *>(_o);\n", cdef->classname.constData()); } else { - fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + fprintf(out, " auto *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData()); } - fprintf(out, " Q_UNUSED(_t)\n"); + fprintf(out, " (void)_t;\n"); fprintf(out, " switch (_id) {\n"); for (int methodindex = 0; methodindex < methodList.size(); ++methodindex) { const FunctionDef &f = methodList.at(methodindex); Q_ASSERT(!f.normalizedType.isEmpty()); fprintf(out, " case %d: ", methodindex); - - //---- Changed from the original in moc + // -- QtScxml if (f.implementation) { fprintf(out, f.implementation, "_o", methodindex); fprintf(out, " break;\n"); continue; } - //---- End of change - + // -- QtScxml if (f.normalizedType != "void") fprintf(out, "{ %s _r = ", noRef(f.normalizedType).constData()); fprintf(out, "_t->"); @@ -1164,22 +1159,27 @@ void Generator::generateStaticMetacall() fprintf(out, "%s(", f.name.constData()); int offset = 1; - int argsCount = f.arguments.count(); - for (int j = 0; j < argsCount; ++j) { - const ArgumentDef &a = f.arguments.at(j); - if (j) - fprintf(out, ","); - fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))",a.typeNameForCast.constData(), offset++); - isUsed_a = true; - } - if (f.isPrivateSignal) { - if (argsCount > 0) - fprintf(out, ", "); - fprintf(out, "%s", "QPrivateSignal()"); + if (f.isRawSlot) { + fprintf(out, "QMethodRawArguments{ _a }"); + } else { + const auto begin = f.arguments.cbegin(); + const auto end = f.arguments.cend(); + for (auto it = begin; it != end; ++it) { + const ArgumentDef &a = *it; + if (it != begin) + fprintf(out, ","); + fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))",a.typeNameForCast.constData(), offset++); + isUsed_a = true; + } + if (f.isPrivateSignal) { + if (!f.arguments.isEmpty()) + fprintf(out, ", "); + fprintf(out, "%s", "QPrivateSignal()"); + } } fprintf(out, ");"); if (f.normalizedType != "void") { - fprintf(out, "\n if (_a[0]) *reinterpret_cast< %s*>(_a[0]) = _r; } ", + fprintf(out, "\n if (_a[0]) *reinterpret_cast< %s*>(_a[0]) = std::move(_r); } ", noRef(f.normalizedType).constData()); isUsed_a = true; } @@ -1195,17 +1195,21 @@ void Generator::generateStaticMetacall() if (!methodsWithAutomaticTypes.isEmpty()) { fprintf(out, " else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {\n"); fprintf(out, " switch (_id) {\n"); - fprintf(out, " default: *reinterpret_cast<int*>(_a[0]) = -1; break;\n"); + fprintf(out, " default: *reinterpret_cast<QMetaType *>(_a[0]) = QMetaType(); break;\n"); QMap<int, QMultiMap<QByteArray, int> >::const_iterator it = methodsWithAutomaticTypes.constBegin(); const QMap<int, QMultiMap<QByteArray, int> >::const_iterator end = methodsWithAutomaticTypes.constEnd(); for ( ; it != end; ++it) { fprintf(out, " case %d:\n", it.key()); fprintf(out, " switch (*reinterpret_cast<int*>(_a[1])) {\n"); - fprintf(out, " default: *reinterpret_cast<int*>(_a[0]) = -1; break;\n"); - for (const QByteArray &key : it->uniqueKeys()) { - for (int argumentID : it->values(key)) - fprintf(out, " case %d:\n", argumentID); - fprintf(out, " *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType< %s >(); break;\n", key.constData()); + fprintf(out, " default: *reinterpret_cast<QMetaType *>(_a[0]) = QMetaType(); break;\n"); + auto jt = it->begin(); + const auto jend = it->end(); + while (jt != jend) { + fprintf(out, " case %d:\n", jt.value()); + const QByteArray &lastKey = jt.key(); + ++jt; + if (jt == jend || jt.key() != lastKey) + fprintf(out, " *reinterpret_cast<QMetaType *>(_a[0]) = QMetaType::fromType< %s >(); break;\n", lastKey.constData()); } fprintf(out, " }\n"); fprintf(out, " break;\n"); @@ -1220,25 +1224,29 @@ void Generator::generateStaticMetacall() Q_ASSERT(needElse); // if there is signal, there was method. fprintf(out, " else if (_c == QMetaObject::IndexOfMethod) {\n"); fprintf(out, " int *result = reinterpret_cast<int *>(_a[0]);\n"); - fprintf(out, " void **func = reinterpret_cast<void **>(_a[1]);\n"); bool anythingUsed = false; - for (int methodindex = 0; methodindex < cdef->signalList.size(); ++methodindex) { + for (int methodindex = 0; methodindex < int(cdef->signalList.size()); ++methodindex) { const FunctionDef &f = cdef->signalList.at(methodindex); - if (f.wasCloned || !f.inPrivateClass.isEmpty() || f.isStatic || f.mangledName.isEmpty()) + if (f.wasCloned || !f.inPrivateClass.isEmpty() || f.isStatic) + continue; + // -- QtScxml + if (f.mangledName.isEmpty()) continue; + // -- QtScxml anythingUsed = true; fprintf(out, " {\n"); - fprintf(out, " typedef %s (%s::*_t)(",f.type.rawName.constData() , cdef->classname.constData()); + fprintf(out, " using _t = %s (%s::*)(",f.type.rawName.constData() , cdef->classname.constData()); - int argsCount = f.arguments.count(); - for (int j = 0; j < argsCount; ++j) { - const ArgumentDef &a = f.arguments.at(j); - if (j) + const auto begin = f.arguments.cbegin(); + const auto end = f.arguments.cend(); + for (auto it = begin; it != end; ++it) { + const ArgumentDef &a = *it; + if (it != begin) fprintf(out, ", "); fprintf(out, "%s", QByteArray(a.type.name + ' ' + a.rightType).constData()); } if (f.isPrivateSignal) { - if (argsCount > 0) + if (!f.arguments.isEmpty()) fprintf(out, ", "); fprintf(out, "%s", "QPrivateSignal"); } @@ -1246,19 +1254,19 @@ void Generator::generateStaticMetacall() fprintf(out, ") const;\n"); else fprintf(out, ");\n"); - fprintf(out, " if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&%s::%s)) {\n", - cdef->classname.constData(), f.mangledName.constData()); + fprintf(out, " if (_t _q_method = &%s::%s; *reinterpret_cast<_t *>(_a[1]) == _q_method) {\n", + cdef->classname.constData(), f.mangledName.constData()); // -- QtScxml fprintf(out, " *result = %d;\n", methodindex); fprintf(out, " return;\n"); fprintf(out, " }\n }\n"); } if (!anythingUsed) - fprintf(out, " Q_UNUSED(result);\n Q_UNUSED(func);\n"); + fprintf(out, " (void)result;\n"); fprintf(out, " }"); needElse = true; } - QMultiMap<QByteArray, int> automaticPropertyMetaTypes = automaticPropertyMetaTypesHelper(); + const QMultiMap<QByteArray, int> automaticPropertyMetaTypes = automaticPropertyMetaTypesHelper(); if (!automaticPropertyMetaTypes.isEmpty()) { if (needElse) @@ -1268,13 +1276,17 @@ void Generator::generateStaticMetacall() fprintf(out, "if (_c == QMetaObject::RegisterPropertyMetaType) {\n"); fprintf(out, " switch (_id) {\n"); fprintf(out, " default: *reinterpret_cast<int*>(_a[0]) = -1; break;\n"); - for (const QByteArray &key : automaticPropertyMetaTypes.uniqueKeys()) { - for (int propertyID : automaticPropertyMetaTypes.values(key)) - fprintf(out, " case %d:\n", propertyID); - fprintf(out, " *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType< %s >(); break;\n", key.constData()); + auto it = automaticPropertyMetaTypes.begin(); + const auto end = automaticPropertyMetaTypes.end(); + while (it != end) { + fprintf(out, " case %d:\n", it.value()); + const QByteArray &lastKey = it.key(); + ++it; + if (it == end || it.key() != lastKey) + fprintf(out, " *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType< %s >(); break;\n", lastKey.constData()); } fprintf(out, " }\n"); - fprintf(out, " }\n"); + fprintf(out, " } "); isUsed_a = true; needElse = true; } @@ -1284,8 +1296,8 @@ void Generator::generateStaticMetacall() bool needTempVarForGet = false; bool needSet = false; bool needReset = false; - for (int i = 0; i < cdef->propertyList.size(); ++i) { - const PropertyDef &p = cdef->propertyList.at(i); + bool hasBindableProperties = false; + for (const PropertyDef &p : std::as_const(cdef->propertyList)) { needGet |= !p.read.isEmpty() || !p.member.isEmpty(); if (!p.read.isEmpty() || !p.member.isEmpty()) needTempVarForGet |= (p.gspec != PropertyDef::PointerSpec @@ -1293,26 +1305,30 @@ void Generator::generateStaticMetacall() needSet |= !p.write.isEmpty() || (!p.member.isEmpty() && !p.constant); needReset |= !p.reset.isEmpty(); + hasBindableProperties |= !p.bind.isEmpty(); } - fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n "); - if (needElse) - fprintf(out, "else "); + fprintf(out, " else "); fprintf(out, "if (_c == QMetaObject::ReadProperty) {\n"); - if (needGet) { + + auto setupMemberAccess = [this]() { if (cdef->hasQObject) { #ifndef QT_NO_DEBUG fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n"); #endif - fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + fprintf(out, " auto *_t = static_cast<%s *>(_o);\n", cdef->classname.constData()); } else { - fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + fprintf(out, " auto *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData()); } - fprintf(out, " Q_UNUSED(_t)\n"); + fprintf(out, " (void)_t;\n"); + }; + + if (needGet) { + setupMemberAccess(); if (needTempVarForGet) fprintf(out, " void *_v = _a[0];\n"); fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { + for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) { const PropertyDef &p = cdef->propertyList.at(propindex); if (p.read.isEmpty() && p.member.isEmpty()) continue; @@ -1320,6 +1336,7 @@ void Generator::generateStaticMetacall() if (p.inPrivateClass.size()) { prefix += p.inPrivateClass + "->"; } + if (p.gspec == PropertyDef::PointerSpec) fprintf(out, " case %d: _a[0] = const_cast<void*>(reinterpret_cast<const void*>(%s%s())); break;\n", propindex, prefix.constData(), p.read.constData()); @@ -1329,14 +1346,15 @@ void Generator::generateStaticMetacall() else if (cdef->enumDeclarations.value(p.type, false)) fprintf(out, " case %d: *reinterpret_cast<int*>(_v) = QFlag(%s%s()); break;\n", propindex, prefix.constData(), p.read.constData()); + else if (p.read == "default") + fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s().value(); break;\n", + propindex, p.type.constData(), prefix.constData(), p.bind.constData()); else if (!p.read.isEmpty()) - //---- Changed from the original in moc - { - fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s%s; break;\n", - propindex, p.type.constData(), prefix.constData(), p.read.constData(), - p.read.endsWith(')') ? "" : "()"); - } - //---- End of change + // -- QtScxml + fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s%s; break;\n", + propindex, p.type.constData(), prefix.constData(), p.read.constData(), + p.read.endsWith(')') ? "" : "()"); + // -- QtScxml else fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s; break;\n", propindex, p.type.constData(), prefix.constData(), p.member.constData()); @@ -1351,18 +1369,10 @@ void Generator::generateStaticMetacall() fprintf(out, "if (_c == QMetaObject::WriteProperty) {\n"); if (needSet) { - if (cdef->hasQObject) { -#ifndef QT_NO_DEBUG - fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n"); -#endif - fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); - } else { - fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); - } - fprintf(out, " Q_UNUSED(_t)\n"); + setupMemberAccess(); fprintf(out, " void *_v = _a[0];\n"); fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { + for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) { const PropertyDef &p = cdef->propertyList.at(propindex); if (p.constant) continue; @@ -1375,6 +1385,12 @@ void Generator::generateStaticMetacall() if (cdef->enumDeclarations.value(p.type, false)) { fprintf(out, " case %d: %s%s(QFlag(*reinterpret_cast<int*>(_v))); break;\n", propindex, prefix.constData(), p.write.constData()); + } else if (p.write == "default") { + fprintf(out, " case %d: {\n", propindex); + fprintf(out, " %s%s().setValue(*reinterpret_cast< %s*>(_v));\n", + prefix.constData(), p.bind.constData(), p.type.constData()); + fprintf(out, " break;\n"); + fprintf(out, " }\n"); } else if (!p.write.isEmpty()) { fprintf(out, " case %d: %s%s(*reinterpret_cast< %s*>(_v)); break;\n", propindex, prefix.constData(), p.write.constData(), p.type.constData()); @@ -1384,13 +1400,15 @@ void Generator::generateStaticMetacall() prefix.constData(), p.member.constData(), p.type.constData()); fprintf(out, " %s%s = *reinterpret_cast< %s*>(_v);\n", prefix.constData(), p.member.constData(), p.type.constData()); - if (!p.notify.isEmpty() && p.notifyId != -1) { + if (!p.notify.isEmpty() && p.notifyId > -1) { const FunctionDef &f = cdef->signalList.at(p.notifyId); if (f.arguments.size() == 0) fprintf(out, " Q_EMIT _t->%s();\n", p.notify.constData()); else if (f.arguments.size() == 1 && f.arguments.at(0).normalizedType == p.type) fprintf(out, " Q_EMIT _t->%s(%s%s);\n", p.notify.constData(), prefix.constData(), p.member.constData()); + } else if (!p.notify.isEmpty() && p.notifyId < -1) { + fprintf(out, " Q_EMIT _t->%s();\n", p.notify.constData()); } fprintf(out, " }\n"); fprintf(out, " break;\n"); @@ -1405,32 +1423,46 @@ void Generator::generateStaticMetacall() fprintf(out, " else "); fprintf(out, "if (_c == QMetaObject::ResetProperty) {\n"); if (needReset) { - if (cdef->hasQObject) { -#ifndef QT_NO_DEBUG - fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n"); -#endif - fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); - } else { - fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); - } - fprintf(out, " Q_UNUSED(_t)\n"); + setupMemberAccess(); fprintf(out, " switch (_id) {\n"); - for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { + for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) { const PropertyDef &p = cdef->propertyList.at(propindex); - if (!p.reset.endsWith(')')) + if (p.reset.isEmpty()) continue; QByteArray prefix = "_t->"; if (p.inPrivateClass.size()) { prefix += p.inPrivateClass + "->"; } - fprintf(out, " case %d: %s%s; break;\n", + fprintf(out, " case %d: %s%s(); break;\n", propindex, prefix.constData(), p.reset.constData()); } fprintf(out, " default: break;\n"); fprintf(out, " }\n"); } fprintf(out, " }"); - fprintf(out, "\n#endif // QT_NO_PROPERTIES"); + + fprintf(out, " else "); + fprintf(out, "if (_c == QMetaObject::BindableProperty) {\n"); + if (hasBindableProperties) { + setupMemberAccess(); + fprintf(out, " switch (_id) {\n"); + for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) { + const PropertyDef &p = cdef->propertyList.at(propindex); + if (p.bind.isEmpty()) + continue; + QByteArray prefix = "_t->"; + if (p.inPrivateClass.size()) { + prefix += p.inPrivateClass + "->"; + } + fprintf(out, + " case %d: *static_cast<QUntypedBindable *>(_a[0]) = %s%s(); " + "break;\n", + propindex, prefix.constData(), p.bind.constData()); + } + fprintf(out, " default: break;\n"); + fprintf(out, " }\n"); + } + fprintf(out, " }"); needElse = true; } @@ -1438,22 +1470,26 @@ void Generator::generateStaticMetacall() fprintf(out, "\n"); if (methodList.isEmpty()) { - fprintf(out, " Q_UNUSED(_o);\n"); + fprintf(out, " (void)_o;\n"); if (cdef->constructorList.isEmpty() && automaticPropertyMetaTypes.isEmpty() && methodsWithAutomaticTypesHelper(methodList).isEmpty()) { - fprintf(out, " Q_UNUSED(_id);\n"); - fprintf(out, " Q_UNUSED(_c);\n"); + fprintf(out, " (void)_id;\n"); + fprintf(out, " (void)_c;\n"); } } if (!isUsed_a) - fprintf(out, " Q_UNUSED(_a);\n"); + fprintf(out, " (void)_a;\n"); - fprintf(out, "}\n\n"); + fprintf(out, "}\n"); } -void Generator::generateSignal(FunctionDef *def,int index) +void Generator::generateSignal(const FunctionDef *def, int index) { - if (def->wasCloned || def->isAbstract || def->implementation) + if (def->wasCloned || def->isAbstract) + return; +// -- QtScxml + if (def->implementation) return; +// -- QtScxml fprintf(out, "\n// SIGNAL %d\n%s %s::%s(", index, def->type.name.constData(), cdef->qualified.constData(), def->name.constData()); @@ -1461,17 +1497,12 @@ void Generator::generateSignal(FunctionDef *def,int index) const char *constQualifier = ""; if (def->isConst) { - thisPtr = "const_cast< "; - thisPtr += cdef->qualified; - thisPtr += " *>(this)"; + thisPtr = "const_cast< " + cdef->qualified + " *>(this)"; constQualifier = "const"; } Q_ASSERT(!def->normalizedType.isEmpty()); - if (def->arguments.isEmpty() && def->normalizedType == "void") { - if (def->isPrivateSignal) - fprintf(out, "QPrivateSignal"); - + if (def->arguments.isEmpty() && def->normalizedType == "void" && !def->isPrivateSignal) { fprintf(out, ")%s\n{\n" " QMetaObject::activate(%s, &staticMetaObject, %d, nullptr);\n" "}\n", constQualifier, thisPtr.constData(), index); @@ -1479,40 +1510,43 @@ void Generator::generateSignal(FunctionDef *def,int index) } int offset = 1; - for (int j = 0; j < def->arguments.count(); ++j) { - const ArgumentDef &a = def->arguments.at(j); - if (j) - fprintf(out, ", "); - fprintf(out, "%s _t%d%s", a.type.name.constData(), offset++, a.rightType.constData()); + const auto begin = def->arguments.cbegin(); + const auto end = def->arguments.cend(); + for (auto it = begin; it != end; ++it) { + const ArgumentDef &a = *it; + if (it != begin) + fputs(", ", out); + if (a.type.name.size()) + fputs(a.type.name.constData(), out); + fprintf(out, " _t%d", offset++); + if (a.rightType.size()) + fputs(a.rightType.constData(), out); } if (def->isPrivateSignal) { if (!def->arguments.isEmpty()) fprintf(out, ", "); - fprintf(out, "QPrivateSignal"); + fprintf(out, "QPrivateSignal _t%d", offset++); } fprintf(out, ")%s\n{\n", constQualifier); if (def->type.name.size() && def->normalizedType != "void") { QByteArray returnType = noRef(def->normalizedType); - if (returnType.endsWith('*')) { - fprintf(out, " %s _t0 = 0;\n", returnType.constData()); - } else { - fprintf(out, " %s _t0 = %s();\n", returnType.constData(), returnType.constData()); - } + fprintf(out, " %s _t0{};\n", returnType.constData()); } fprintf(out, " void *_a[] = { "); if (def->normalizedType == "void") { fprintf(out, "nullptr"); } else { - fprintf(out, "const_cast<void*>(reinterpret_cast<const void*>(&_t0))"); + // -- QtScxml removed unused returnTypeIsVolatile + fprintf(out, "const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t0)))"); } int i; for (i = 1; i < offset; ++i) - if (def->arguments.at(i - 1).type.isVolatile) - fprintf(out, ", const_cast<void*>(reinterpret_cast<const volatile void*>(&_t%d))", i); + if (i <= def->arguments.size() && def->arguments.at(i - 1).type.isVolatile) + fprintf(out, ", const_cast<void*>(reinterpret_cast<const volatile void*>(std::addressof(_t%d)))", i); else - fprintf(out, ", const_cast<void*>(reinterpret_cast<const void*>(&_t%d))", i); + fprintf(out, ", const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t%d)))", i); fprintf(out, " };\n"); fprintf(out, " QMetaObject::activate(%s, &staticMetaObject, %d, _a);\n", thisPtr.constData(), index); if (def->normalizedType != "void") @@ -1520,6 +1554,7 @@ void Generator::generateSignal(FunctionDef *def,int index) fprintf(out, "}\n"); } +// -- QtScxml void Generator::generateAccessorDefs() { for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { @@ -1549,31 +1584,55 @@ void Generator::generateSignalDefs() } #if 0 -static void writePluginMetaData(FILE *out, const QJsonObject &data) +static CborError jsonValueToCbor(CborEncoder *parent, const QJsonValue &v); +static CborError jsonObjectToCbor(CborEncoder *parent, const QJsonObject &o) { - const QJsonDocument doc(data); + auto it = o.constBegin(); + auto end = o.constEnd(); + CborEncoder map; + cbor_encoder_create_map(parent, &map, o.size()); + + for ( ; it != end; ++it) { + QByteArray key = it.key().toUtf8(); + cbor_encode_text_string(&map, key.constData(), key.size()); + jsonValueToCbor(&map, it.value()); + } + return cbor_encoder_close_container(parent, &map); +} - fputs("\nQT_PLUGIN_METADATA_SECTION\n" - "static const unsigned char qt_pluginMetaData[] = {\n" - " 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', ' ',\n ", out); -#if 0 - fprintf(out, "\"%s\";\n", doc.toJson().constData()); -#else - const QByteArray binary = doc.toBinaryData(); - const int last = binary.size() - 1; - for (int i = 0; i < last; ++i) { - uchar c = (uchar)binary.at(i); - if (c < 0x20 || c >= 0x7f) - fprintf(out, " 0x%02x,", c); - else if (c == '\'' || c == '\\') - fprintf(out, " '\\%c',", c); - else - fprintf(out, " '%c', ", c); - if (!((i + 1) % 8)) - fputs("\n ", out); +static CborError jsonArrayToCbor(CborEncoder *parent, const QJsonArray &a) +{ + CborEncoder array; + cbor_encoder_create_array(parent, &array, a.size()); + for (const QJsonValue v : a) + jsonValueToCbor(&array, v); + return cbor_encoder_close_container(parent, &array); +} + +static CborError jsonValueToCbor(CborEncoder *parent, const QJsonValue &v) +{ + switch (v.type()) { + case QJsonValue::Null: + case QJsonValue::Undefined: + return cbor_encode_null(parent); + case QJsonValue::Bool: + return cbor_encode_boolean(parent, v.toBool()); + case QJsonValue::Array: + return jsonArrayToCbor(parent, v.toArray()); + case QJsonValue::Object: + return jsonObjectToCbor(parent, v.toObject()); + case QJsonValue::String: { + QByteArray s = v.toString().toUtf8(); + return cbor_encode_text_string(parent, s.constData(), s.size()); } - fprintf(out, " 0x%02x\n};\n", (uchar)binary.at(last)); -#endif + case QJsonValue::Double: { + double d = v.toDouble(); + if (d == floor(d) && fabs(d) <= (Q_INT64_C(1) << std::numeric_limits<double>::digits)) + return cbor_encode_int(parent, qint64(d)); + return cbor_encode_double(parent, d); + } + } + Q_UNREACHABLE_RETURN(CborUnknownError); } void Generator::generatePluginMetaData() @@ -1581,40 +1640,90 @@ void Generator::generatePluginMetaData() if (cdef->pluginData.iid.isEmpty()) return; - // Write plugin meta data #ifdefed QT_NO_DEBUG with debug=false, - // true, respectively. + auto outputCborData = [this]() { + CborDevice dev(out); + CborEncoder enc; + cbor_encoder_init_writer(&enc, CborDevice::callback, &dev); - QJsonObject data; - const QString debugKey = QStringLiteral("debug"); - data.insert(QStringLiteral("IID"), QLatin1String(cdef->pluginData.iid.constData())); - data.insert(QStringLiteral("className"), QLatin1String(cdef->classname.constData())); - data.insert(QStringLiteral("version"), (int)QT_VERSION); - data.insert(debugKey, QJsonValue(false)); - data.insert(QStringLiteral("MetaData"), cdef->pluginData.metaData.object()); + CborEncoder map; + cbor_encoder_create_map(&enc, &map, CborIndefiniteLength); - // Add -M args from the command line: - for (auto it = cdef->pluginData.metaArgs.cbegin(), end = cdef->pluginData.metaArgs.cend(); it != end; ++it) - data.insert(it.key(), it.value()); + dev.nextItem("\"IID\""); + cbor_encode_int(&map, int(QtPluginMetaDataKeys::IID)); + cbor_encode_text_string(&map, cdef->pluginData.iid.constData(), cdef->pluginData.iid.size()); - fputs("\nQT_PLUGIN_METADATA_SECTION const uint qt_section_alignment_dummy = 42;\n\n" - "#ifdef QT_NO_DEBUG\n", out); - writePluginMetaData(out, data); + dev.nextItem("\"className\""); + cbor_encode_int(&map, int(QtPluginMetaDataKeys::ClassName)); + cbor_encode_text_string(&map, cdef->classname.constData(), cdef->classname.size()); - fputs("\n#else // QT_NO_DEBUG\n", out); + QJsonObject o = cdef->pluginData.metaData.object(); + if (!o.isEmpty()) { + dev.nextItem("\"MetaData\""); + cbor_encode_int(&map, int(QtPluginMetaDataKeys::MetaData)); + jsonObjectToCbor(&map, o); + } - data.remove(debugKey); - data.insert(debugKey, QJsonValue(true)); - writePluginMetaData(out, data); + if (!cdef->pluginData.uri.isEmpty()) { + dev.nextItem("\"URI\""); + cbor_encode_int(&map, int(QtPluginMetaDataKeys::URI)); + cbor_encode_text_string(&map, cdef->pluginData.uri.constData(), cdef->pluginData.uri.size()); + } - fputs("#endif // QT_NO_DEBUG\n\n", out); + // Add -M args from the command line: + for (auto it = cdef->pluginData.metaArgs.cbegin(), end = cdef->pluginData.metaArgs.cend(); it != end; ++it) { + const QJsonArray &a = it.value(); + QByteArray key = it.key().toUtf8(); + dev.nextItem(QByteArray("command-line \"" + key + "\"").constData()); + cbor_encode_text_string(&map, key.constData(), key.size()); + jsonArrayToCbor(&map, a); + } + + // Close the CBOR map manually + dev.nextItem(); + cbor_encoder_close_container(&enc, &map); + }; // 'Use' all namespaces. - int pos = cdef->qualified.indexOf("::"); + qsizetype pos = cdef->qualified.indexOf("::"); for ( ; pos != -1 ; pos = cdef->qualified.indexOf("::", pos + 2) ) fprintf(out, "using namespace %s;\n", cdef->qualified.left(pos).constData()); - fprintf(out, "QT_MOC_EXPORT_PLUGIN(%s, %s)\n\n", + + fputs("\n#ifdef QT_MOC_EXPORT_PLUGIN_V2", out); + + // Qt 6.3+ output + fprintf(out, "\nstatic constexpr unsigned char qt_pluginMetaDataV2_%s[] = {", + cdef->classname.constData()); + outputCborData(); + fprintf(out, "\n};\nQT_MOC_EXPORT_PLUGIN_V2(%s, %s, qt_pluginMetaDataV2_%s)\n", + cdef->qualified.constData(), cdef->classname.constData(), cdef->classname.constData()); + + // compatibility with Qt 6.0-6.2 + fprintf(out, "#else\nQT_PLUGIN_METADATA_SECTION\n" + "Q_CONSTINIT static constexpr unsigned char qt_pluginMetaData_%s[] = {\n" + " 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',\n" + " // metadata version, Qt version, architectural requirements\n" + " 0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),", + cdef->classname.constData()); + outputCborData(); + fprintf(out, "\n};\nQT_MOC_EXPORT_PLUGIN(%s, %s)\n" + "#endif // QT_MOC_EXPORT_PLUGIN_V2\n", cdef->qualified.constData(), cdef->classname.constData()); + + fputs("\n", out); } + +QT_WARNING_DISABLE_GCC("-Wunused-function") +QT_WARNING_DISABLE_CLANG("-Wunused-function") +QT_WARNING_DISABLE_CLANG("-Wundefined-internal") +QT_WARNING_DISABLE_MSVC(4334) // '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) + +#define CBOR_ENCODER_WRITER_CONTROL 1 +#define CBOR_ENCODER_WRITE_FUNCTION CborDevice::callback #endif +// -- QtScxml QT_END_NAMESPACE + +#if 0 // -- QtScxml +#include "cborencoder.c" +#endif // -- QtScxml diff --git a/tools/qscxmlc/generator.h b/tools/qscxmlc/generator.h index e1f10eb..8db1350 100644 --- a/tools/qscxmlc/generator.h +++ b/tools/qscxmlc/generator.h @@ -1,60 +1,47 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtScxml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef GENERATOR_H #define GENERATOR_H #include "moc.h" + +// -- QtScxml #include <QtCore/qhash.h> -#include <QtCore/qvector.h> +#include <QtCore/qlist.h> #include <QtCore/qiodevice.h> +// -- QtScxml QT_BEGIN_NAMESPACE class Generator { - QIODevice &out; + QIODevice &out; // -- QtScxml ClassDef *cdef; - QVector<uint> meta_data; + QList<uint> meta_data; + public: - Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes, const QHash<QByteArray, - QByteArray> &knownQObjectClasses, const QHash<QByteArray, QByteArray> &knownGadgets, - QIODevice &outfile); + Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes, + const QHash<QByteArray, QByteArray> &knownQObjectClasses, + const QHash<QByteArray, QByteArray> &knownGadgets, + QIODevice &outfile, // -- QtScxml + bool requireCompleteTypes = false); void generateCode(); + qsizetype registeredStringsCount() { return strings.size(); }; + +// -- QtScxml void generateAccessorDefs(); void generateSignalDefs(); +// -- QtScxml private: bool registerableMetaType(const QByteArray &propertyType); void registerClassInfoStrings(); void generateClassInfos(); void registerFunctionStrings(const QList<FunctionDef> &list); - void generateFunctions(const QList<FunctionDef> &list, const char *functype, int type, int ¶msIndex); + void registerByteArrayVector(const QList<QByteArray> &list); + void generateFunctions(const QList<FunctionDef> &list, const char *functype, int type, + int ¶msIndex, int &initialMetatypeOffset); void generateFunctionRevisions(const QList<FunctionDef> &list, const char *functype); void generateFunctionParameters(const QList<FunctionDef> &list, const char *functype); void generateTypeInfo(const QByteArray &typeName, bool allowEmptyName = false); @@ -64,10 +51,13 @@ private: void generateProperties(); void generateMetacall(); void generateStaticMetacall(); - void generateSignal(FunctionDef *def, int index); -// void generatePluginMetaData(); + void generateSignal(const FunctionDef *def, int index); +#if 0 // -- QtScxml + void generatePluginMetaData(); +#endif // -- QtScxml QMultiMap<QByteArray, int> automaticPropertyMetaTypesHelper(); - QMap<int, QMultiMap<QByteArray, int> > methodsWithAutomaticTypesHelper(const QList<FunctionDef> &methodList); + QMap<int, QMultiMap<QByteArray, int>> + methodsWithAutomaticTypesHelper(const QList<FunctionDef> &methodList); void strreg(const QByteArray &); // registers a string int stridx(const QByteArray &); // returns a string's id @@ -76,6 +66,7 @@ private: QList<QByteArray> metaTypes; QHash<QByteArray, QByteArray> knownQObjectClasses; QHash<QByteArray, QByteArray> knownGadgets; + bool requireCompleteTypes; }; QT_END_NAMESPACE diff --git a/tools/qscxmlc/main.cpp b/tools/qscxmlc/main.cpp index ccc99ed..9e09dbf 100644 --- a/tools/qscxmlc/main.cpp +++ b/tools/qscxmlc/main.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtScxml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include <QtScxml/qscxmltabledata.h> #include "qscxmlc.h" diff --git a/tools/qscxmlc/moc.cpp b/tools/qscxmlc/moc.cpp new file mode 100644 index 0000000..aeb3cad --- /dev/null +++ b/tools/qscxmlc/moc.cpp @@ -0,0 +1,2215 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2019 Olivier Goffart <ogoffart@woboq.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "moc.h" +#include "generator.h" +#include "qdatetime.h" +#include "utils.h" +#include "outputrevision.h" +#include <QtCore/qfile.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qdir.h> +#include <QtCore/qjsondocument.h> + +// -- QtScxml +#include <QtCore/qjsonobject.h> +// -- QtScxml + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +#if 0 // -- QtScxml +// only moc needs this function +static QByteArray normalizeType(const QByteArray &ba) +{ + return ba.size() ? normalizeTypeInternal(ba.constBegin(), ba.constEnd()) : ba; +} + +const QByteArray &Moc::toFullyQualified(const QByteArray &name) const noexcept +{ + if (auto it = knownQObjectClasses.find(name); it != knownQObjectClasses.end()) + return it.value(); + if (auto it = knownGadgets.find(name); it != knownGadgets.end()) + return it.value(); + return name; +} + +bool Moc::parseClassHead(ClassDef *def) +{ + // figure out whether this is a class declaration, or only a + // forward or variable declaration. + int i = 0; + Token token; + do { + token = lookup(i++); + if (token == COLON || token == LBRACE) + break; + if (token == SEMIC || token == RANGLE) + return false; + } while (token); + + // support attributes like "class [[deprecated]]] name" + skipCxxAttributes(); + + if (!test(IDENTIFIER)) // typedef struct { ... } + return false; + QByteArray name = lexem(); + + // support "class IDENT name" and "class IDENT(IDENT) name" + // also support "class IDENT name (final|sealed|Q_DECL_FINAL)" + if (test(LPAREN)) { + until(RPAREN); + if (!test(IDENTIFIER)) + return false; + name = lexem(); + } else if (test(IDENTIFIER)) { + const QByteArray lex = lexem(); + if (lex != "final" && lex != "sealed" && lex != "Q_DECL_FINAL") + name = lex; + } + + def->qualified += name; + while (test(SCOPE)) { + def->qualified += lexem(); + if (test(IDENTIFIER)) { + name = lexem(); + def->qualified += name; + } + } + def->classname = name; + + if (test(IDENTIFIER)) { + const QByteArray lex = lexem(); + if (lex != "final" && lex != "sealed" && lex != "Q_DECL_FINAL") + return false; + } + + if (test(COLON)) { + do { + test(VIRTUAL); + FunctionDef::Access access = FunctionDef::Public; + if (test(PRIVATE)) + access = FunctionDef::Private; + else if (test(PROTECTED)) + access = FunctionDef::Protected; + else + test(PUBLIC); + test(VIRTUAL); + const Type type = parseType(); + // ignore the 'class Foo : BAR(Baz)' case + if (test(LPAREN)) { + until(RPAREN); + } else { + def->superclassList.push_back({type.name, toFullyQualified(type.name), access}); + } + } while (test(COMMA)); + + if (!def->superclassList.isEmpty() + && knownGadgets.contains(def->superclassList.constFirst().classname)) { + // Q_GADGET subclasses are treated as Q_GADGETs + knownGadgets.insert(def->classname, def->qualified); + knownGadgets.insert(def->qualified, def->qualified); + } + } + if (!test(LBRACE)) + return false; + def->begin = index - 1; + bool foundRBrace = until(RBRACE); + def->end = index; + index = def->begin + 1; + return foundRBrace; +} + +Type Moc::parseType() +{ + Type type; + bool hasSignedOrUnsigned = false; + bool isVoid = false; + type.firstToken = lookup(); + for (;;) { + skipCxxAttributes(); + switch (next()) { + case SIGNED: + case UNSIGNED: + hasSignedOrUnsigned = true; + Q_FALLTHROUGH(); + case CONST: + case VOLATILE: + type.name += lexem(); + type.name += ' '; + if (lookup(0) == VOLATILE) + type.isVolatile = true; + continue; + case Q_MOC_COMPAT_TOKEN: + case Q_INVOKABLE_TOKEN: + case Q_SCRIPTABLE_TOKEN: + case Q_SIGNALS_TOKEN: + case Q_SLOTS_TOKEN: + case Q_SIGNAL_TOKEN: + case Q_SLOT_TOKEN: + type.name += lexem(); + return type; + case NOTOKEN: + return type; + default: + prev(); + break; + } + break; + } + + skipCxxAttributes(); + test(ENUM) || test(CLASS) || test(STRUCT); + for(;;) { + skipCxxAttributes(); + switch (next()) { + case IDENTIFIER: + // void mySlot(unsigned myArg) + if (hasSignedOrUnsigned) { + prev(); + break; + } + Q_FALLTHROUGH(); + case CHAR: + case SHORT: + case INT: + case LONG: + type.name += lexem(); + // preserve '[unsigned] long long', 'short int', 'long int', 'long double' + if (test(LONG) || test(INT) || test(DOUBLE)) { + type.name += ' '; + prev(); + continue; + } + break; + case FLOAT: + case DOUBLE: + case VOID: + case BOOL: + case AUTO: + type.name += lexem(); + isVoid |= (lookup(0) == VOID); + break; + case NOTOKEN: + return type; + default: + prev(); + ; + } + if (test(LANGLE)) { + if (type.name.isEmpty()) { + // '<' cannot start a type + return type; + } + type.name += lexemUntil(RANGLE); + } + if (test(SCOPE)) { + type.name += lexem(); + type.isScoped = true; + } else { + break; + } + } + while (test(CONST) || test(VOLATILE) || test(SIGNED) || test(UNSIGNED) + || test(STAR) || test(AND) || test(ANDAND)) { + type.name += ' '; + type.name += lexem(); + if (lookup(0) == AND) + type.referenceType = Type::Reference; + else if (lookup(0) == ANDAND) + type.referenceType = Type::RValueReference; + else if (lookup(0) == STAR) + type.referenceType = Type::Pointer; + } + type.rawName = type.name; + // transform stupid things like 'const void' or 'void const' into 'void' + if (isVoid && type.referenceType == Type::NoReference) { + type.name = "void"; + } + return type; +} + +enum class IncludeState { + IncludeBegin, + IncludeEnd, + NoInclude, +}; + +bool Moc::parseEnum(EnumDef *def) +{ + bool isTypdefEnum = false; // typedef enum { ... } Foo; + + if (test(CLASS) || test(STRUCT)) + def->isEnumClass = true; + + if (test(IDENTIFIER)) { + def->name = lexem(); + } else { + if (lookup(-1) != TYPEDEF) + return false; // anonymous enum + isTypdefEnum = true; + } + if (test(COLON)) { // C++11 strongly typed enum + // enum Foo : unsigned long { ... }; + def->type = normalizeType(parseType().name); + } + if (!test(LBRACE)) + return false; + auto handleInclude = [this]() -> IncludeState { + bool hadIncludeBegin = false; + if (test(MOC_INCLUDE_BEGIN)) { + currentFilenames.push(symbol().unquotedLexem()); + // we do not return early to handle empty headers in one go + hadIncludeBegin = true; + } + if (test(NOTOKEN)) { + next(MOC_INCLUDE_END); + currentFilenames.pop(); + return IncludeState::IncludeEnd; + } + if (hadIncludeBegin) + return IncludeState::IncludeBegin; + else + return IncludeState::NoInclude; + }; + do { + handleInclude(); + if (lookup() == RBRACE) // accept trailing comma + break; + next(IDENTIFIER); + def->values += lexem(); + handleInclude(); + skipCxxAttributes(); + } while (test(EQ) ? until(COMMA) : test(COMMA)); + next(RBRACE); + if (isTypdefEnum) { + if (!test(IDENTIFIER)) + return false; + def->name = lexem(); + } + return true; +} + +void Moc::parseFunctionArguments(FunctionDef *def) +{ + Q_UNUSED(def); + while (hasNext()) { + ArgumentDef arg; + arg.type = parseType(); + if (arg.type.name == "void") + break; + if (test(IDENTIFIER)) + arg.name = lexem(); + while (test(LBRACK)) { + arg.rightType += lexemUntil(RBRACK); + } + if (test(CONST) || test(VOLATILE)) { + arg.rightType += ' '; + arg.rightType += lexem(); + } + arg.normalizedType = normalizeType(QByteArray(arg.type.name + ' ' + arg.rightType)); + arg.typeNameForCast = QByteArray("std::add_pointer_t<"+arg.normalizedType+">"); + if (test(EQ)) + arg.isDefault = true; + def->arguments += arg; + if (!until(COMMA)) + break; + } + + if (!def->arguments.isEmpty() + && def->arguments.constLast().normalizedType == "QPrivateSignal") { + def->arguments.removeLast(); + def->isPrivateSignal = true; + } + if (def->arguments.size() == 1 + && def->arguments.constLast().normalizedType == "QMethodRawArguments") { + def->arguments.removeLast(); + def->isRawSlot = true; + } + + if (Q_UNLIKELY(def->arguments.size() >= std::numeric_limits<int>::max())) + error("number of function arguments exceeds std::numeric_limits<int>::max()"); +} + +bool Moc::testFunctionAttribute(FunctionDef *def) +{ + if (index < symbols.size() && testFunctionAttribute(symbols.at(index).token, def)) { + ++index; + return true; + } + return false; +} + +bool Moc::testFunctionAttribute(Token tok, FunctionDef *def) +{ + switch (tok) { + case Q_MOC_COMPAT_TOKEN: + def->isCompat = true; + return true; + case Q_INVOKABLE_TOKEN: + def->isInvokable = true; + return true; + case Q_SIGNAL_TOKEN: + def->isSignal = true; + return true; + case Q_SLOT_TOKEN: + def->isSlot = true; + return true; + case Q_SCRIPTABLE_TOKEN: + def->isInvokable = def->isScriptable = true; + return true; + default: break; + } + return false; +} + +bool Moc::skipCxxAttributes() +{ + auto rewind = index; + if (test(LBRACK) && test(LBRACK) && until(RBRACK) && test(RBRACK)) + return true; + index = rewind; + return false; +} + +QTypeRevision Moc::parseRevision() +{ + next(LPAREN); + QByteArray revisionString = lexemUntil(RPAREN); + revisionString.remove(0, 1); + revisionString.chop(1); + const QList<QByteArray> majorMinor = revisionString.split(','); + switch (majorMinor.size()) { + case 1: { + bool ok = false; + const int revision = revisionString.toInt(&ok); + if (!ok || !QTypeRevision::isValidSegment(revision)) + error("Invalid revision"); + return QTypeRevision::fromMinorVersion(revision); + } + case 2: { // major.minor + bool ok = false; + const int major = majorMinor[0].toInt(&ok); + if (!ok || !QTypeRevision::isValidSegment(major)) + error("Invalid major version"); + const int minor = majorMinor[1].toInt(&ok); + if (!ok || !QTypeRevision::isValidSegment(minor)) + error("Invalid minor version"); + return QTypeRevision::fromVersion(major, minor); + } + default: + error("Invalid revision"); + return QTypeRevision(); + } +} + +bool Moc::testFunctionRevision(FunctionDef *def) +{ + + if (test(Q_REVISION_TOKEN)) { + def->revision = parseRevision().toEncodedVersion<int>(); + return true; + } + + return false; +} + +// returns false if the function should be ignored +bool Moc::parseFunction(FunctionDef *def, bool inMacro) +{ + def->isVirtual = false; + def->isStatic = false; + //skip modifiers and attributes + while (testForFunctionModifiers(def) + || skipCxxAttributes() || testFunctionAttribute(def) || testFunctionRevision(def)) {} + bool templateFunction = (lookup() == TEMPLATE); + def->type = parseType(); + if (def->type.name.isEmpty()) { + if (templateFunction) + error("Template function as signal or slot"); + else + error(); + } + bool scopedFunctionName = false; + // we might have modifiers and attributes after a tag + // note that testFunctionAttribute is handled further below, + // and revisions and attributes must come first + while (testForFunctionModifiers(def)) {} + Type tempType = parseType();; + while (!tempType.name.isEmpty() && lookup() != LPAREN) { + if (testFunctionAttribute(def->type.firstToken, def)) + ; // fine + else if (def->type.firstToken == Q_SIGNALS_TOKEN) + error(); + else if (def->type.firstToken == Q_SLOTS_TOKEN) + error(); + else { + if (!def->tag.isEmpty()) + def->tag += ' '; + def->tag += def->type.name; + } + def->type = tempType; + tempType = parseType(); + } + next(LPAREN, "Not a signal or slot declaration"); + def->name = tempType.name; + scopedFunctionName = tempType.isScoped; + + if (!test(RPAREN)) { + parseFunctionArguments(def); + next(RPAREN); + } + + // support optional macros with compiler specific options + while (test(IDENTIFIER)) + ; + + def->isConst = test(CONST); + + while (test(IDENTIFIER)) + ; + + if (inMacro) { + next(RPAREN); + prev(); + } else { + if (test(THROW)) { + next(LPAREN); + until(RPAREN); + } + + if (def->type.name == "auto" && test(ARROW)) + def->type = parseType(); // Parse trailing return-type + + if (test(SEMIC)) + ; + else if ((def->inlineCode = test(LBRACE))) + until(RBRACE); + else if ((def->isAbstract = test(EQ))) + until(SEMIC); + else if (skipCxxAttributes()) + until(SEMIC); + else + error(); + } + if (scopedFunctionName) { + const QByteArray msg = "Function declaration " + def->name + + " contains extra qualification. Ignoring as signal or slot."; + warning(msg.constData()); + return false; + } + + QList<QByteArray> typeNameParts = normalizeType(def->type.name).split(' '); + if (typeNameParts.contains("auto")) { + // We expected a trailing return type but we haven't seen one + error("Function declared with auto as return type but missing trailing return type. " + "Return type deduction is not supported."); + } + + // we don't support references as return types, it's too dangerous + if (def->type.referenceType == Type::Reference) { + QByteArray rawName = def->type.rawName; + def->type = Type("void"); + def->type.rawName = rawName; + } + + def->normalizedType = normalizeType(def->type.name); + return true; +} + +bool Moc::testForFunctionModifiers(FunctionDef *def) +{ + return test(EXPLICIT) || test(INLINE) || + (test(STATIC) && (def->isStatic = true)) || + (test(VIRTUAL) && (def->isVirtual = true)); +} + +// like parseFunction, but never aborts with an error +bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def) +{ + def->isVirtual = false; + def->isStatic = false; + //skip modifiers and attributes + while (testForFunctionModifiers(def) + || skipCxxAttributes() || testFunctionAttribute(def) || testFunctionRevision(def)) {} + bool tilde = test(TILDE); + def->type = parseType(); + if (def->type.name.isEmpty()) + return false; + bool scopedFunctionName = false; + if (test(LPAREN)) { + def->name = def->type.name; + scopedFunctionName = def->type.isScoped; + if (def->name == cdef->classname) { + def->isDestructor = tilde; + def->isConstructor = !tilde; + def->type = Type(); + } else { + // missing type name? => Skip + return false; + } + } else { + // ### TODO: The condition before testForFunctionModifiers shoulnd't be necessary, + // but otherwise we end up with misparses + if (def->isSlot || def->isSignal || def->isInvokable) + while (testForFunctionModifiers(def)) {} + Type tempType = parseType();; + while (!tempType.name.isEmpty() && lookup() != LPAREN) { + if (testFunctionAttribute(def->type.firstToken, def)) + ; // fine + else if (def->type.name == "Q_SIGNAL") + def->isSignal = true; + else if (def->type.name == "Q_SLOT") + def->isSlot = true; + else { + if (!def->tag.isEmpty()) + def->tag += ' '; + def->tag += def->type.name; + } + def->type = tempType; + tempType = parseType(); + } + if (!test(LPAREN)) + return false; + def->name = tempType.name; + scopedFunctionName = tempType.isScoped; + } + + // we don't support references as return types, it's too dangerous + if (def->type.referenceType == Type::Reference) { + QByteArray rawName = def->type.rawName; + def->type = Type("void"); + def->type.rawName = rawName; + } + + def->normalizedType = normalizeType(def->type.name); + + if (!test(RPAREN)) { + parseFunctionArguments(def); + if (!test(RPAREN)) + return false; + } + def->isConst = test(CONST); + if (scopedFunctionName + && (def->isSignal || def->isSlot || def->isInvokable)) { + const QByteArray msg = "parsemaybe: Function declaration " + def->name + + " contains extra qualification. Ignoring as signal or slot."; + warning(msg.constData()); + return false; + } + return true; +} + +inline void handleDefaultArguments(QList<FunctionDef> *functionList, FunctionDef &function) +{ + // support a function with a default argument by pretending there is an + // overload without the argument (the original function is the overload with + // all arguments present) + while (function.arguments.size() > 0 && function.arguments.constLast().isDefault) { + function.wasCloned = true; + function.arguments.removeLast(); + *functionList += function; + } +} + +void Moc::prependNamespaces(BaseDef &def, const QList<NamespaceDef> &namespaceList) const +{ + auto it = namespaceList.crbegin(); + const auto rend = namespaceList.crend(); + for (; it != rend; ++it) { + if (inNamespace(&*it)) + def.qualified.prepend(it->classname + "::"); + } +} + +void Moc::checkListSizes(const ClassDef &def) +{ + if (Q_UNLIKELY(def.nonClassSignalList.size() > std::numeric_limits<int>::max())) + error("number of signals defined in parent class(es) exceeds " + "std::numeric_limits<int>::max()."); + + if (Q_UNLIKELY(def.propertyList.size() > std::numeric_limits<int>::max())) + error("number of bindable properties exceeds std::numeric_limits<int>::max()."); + + if (Q_UNLIKELY(def.classInfoList.size() > std::numeric_limits<int>::max())) + error("number of times Q_CLASSINFO macro is used exceeds " + "std::numeric_limits<int>::max()."); + + if (Q_UNLIKELY(def.enumList.size() > std::numeric_limits<int>::max())) + error("number of enumerations exceeds std::numeric_limits<int>::max()."); + + if (Q_UNLIKELY(def.superclassList.size() > std::numeric_limits<int>::max())) + error("number of super classes exceeds std::numeric_limits<int>::max()."); + + if (Q_UNLIKELY(def.constructorList.size() > std::numeric_limits<int>::max())) + error("number of constructor parameters exceeds std::numeric_limits<int>::max()."); + + if (Q_UNLIKELY(def.signalList.size() > std::numeric_limits<int>::max())) + error("number of signals exceeds std::numeric_limits<int>::max()."); + + if (Q_UNLIKELY(def.slotList.size() > std::numeric_limits<int>::max())) + error("number of declared slots exceeds std::numeric_limits<int>::max()."); + + if (Q_UNLIKELY(def.methodList.size() > std::numeric_limits<int>::max())) + error("number of methods exceeds std::numeric_limits<int>::max()."); + + if (Q_UNLIKELY(def.publicList.size() > std::numeric_limits<int>::max())) + error("number of public functions declared in this class exceeds " + "std::numeric_limits<int>::max()."); +} + +void Moc::parse() +{ + QList<NamespaceDef> namespaceList; + bool templateClass = false; + while (hasNext()) { + Token t = next(); + switch (t) { + case NAMESPACE: { + qsizetype rewind = index; + if (test(IDENTIFIER)) { + QByteArray nsName = lexem(); + QByteArrayList nested; + while (test(SCOPE)) { + /* treat (C++20's) namespace A::inline B {} as A::B + this is mostly to not break compilation when encountering such + a construct in a header; the interaction of Qt's meta-macros with + inline namespaces is still rather poor. + */ + test(INLINE); + next(IDENTIFIER); + nested.append(nsName); + nsName = lexem(); + } + if (test(EQ)) { + // namespace Foo = Bar::Baz; + until(SEMIC); + } else if (test(LPAREN)) { + // Ignore invalid code such as: 'namespace __identifier("x")' (QTBUG-56634) + until(RPAREN); + } else if (!test(SEMIC)) { + NamespaceDef def; + def.classname = nsName; + def.doGenerate = currentFilenames.size() <= 1; + + next(LBRACE); + def.begin = index - 1; + until(RBRACE); + def.end = index; + index = def.begin + 1; + + prependNamespaces(def, namespaceList); + + for (const QByteArray &ns : nested) { + NamespaceDef parentNs; + parentNs.classname = ns; + parentNs.qualified = def.qualified; + def.qualified += ns + "::"; + parentNs.begin = def.begin; + parentNs.end = def.end; + namespaceList += parentNs; + } + + while (inNamespace(&def) && hasNext()) { + switch (next()) { + case NAMESPACE: + if (test(IDENTIFIER)) { + while (test(SCOPE)) { + test(INLINE); // ignore inline namespaces + next(IDENTIFIER); + } + if (test(EQ)) { + // namespace Foo = Bar::Baz; + until(SEMIC); + } else if (!test(SEMIC)) { + until(RBRACE); + } + } + break; + case Q_NAMESPACE_TOKEN: + def.hasQNamespace = true; + break; + case Q_NAMESPACE_EXPORT_TOKEN: + next(LPAREN); + while (test(IDENTIFIER)) + {} + next(RPAREN); + def.hasQNamespace = true; + break; + case Q_ENUMS_TOKEN: + case Q_ENUM_NS_TOKEN: + parseEnumOrFlag(&def, false); + break; + case Q_ENUM_TOKEN: + error("Q_ENUM can't be used in a Q_NAMESPACE, use Q_ENUM_NS instead"); + break; + case Q_FLAGS_TOKEN: + case Q_FLAG_NS_TOKEN: + parseEnumOrFlag(&def, true); + break; + case Q_FLAG_TOKEN: + error("Q_FLAG can't be used in a Q_NAMESPACE, use Q_FLAG_NS instead"); + break; + case Q_DECLARE_FLAGS_TOKEN: + parseFlag(&def); + break; + case Q_CLASSINFO_TOKEN: + parseClassInfo(&def); + break; + case Q_MOC_INCLUDE_TOKEN: + // skip it, the namespace is parsed twice + next(LPAREN); + lexemUntil(RPAREN); + break; + case ENUM: { + EnumDef enumDef; + if (parseEnum(&enumDef)) + def.enumList += enumDef; + } break; + case CLASS: + case STRUCT: { + ClassDef classdef; + if (!parseClassHead(&classdef)) + continue; + while (inClass(&classdef) && hasNext()) + next(); // consume all Q_XXXX macros from this class + } break; + default: break; + } + } + namespaceList += def; + index = rewind; + if (!def.hasQNamespace && (!def.classInfoList.isEmpty() || !def.enumDeclarations.isEmpty())) + error("Namespace declaration lacks Q_NAMESPACE macro."); + } + } + break; + } + case SEMIC: + case RBRACE: + templateClass = false; + break; + case TEMPLATE: + templateClass = true; + break; + case MOC_INCLUDE_BEGIN: + currentFilenames.push(symbol().unquotedLexem()); + break; + case MOC_INCLUDE_END: + currentFilenames.pop(); + break; + case Q_DECLARE_INTERFACE_TOKEN: + parseDeclareInterface(); + break; + case Q_DECLARE_METATYPE_TOKEN: + parseDeclareMetatype(); + break; + case Q_MOC_INCLUDE_TOKEN: + parseMocInclude(); + break; + case USING: + if (test(NAMESPACE)) { + while (test(SCOPE) || test(IDENTIFIER)) + ; + // Ignore invalid code such as: 'using namespace __identifier("x")' (QTBUG-63772) + if (test(LPAREN)) + until(RPAREN); + next(SEMIC); + } + break; + case CLASS: + case STRUCT: { + if (currentFilenames.size() <= 1) + break; + + ClassDef def; + if (!parseClassHead(&def)) + continue; + + while (inClass(&def) && hasNext()) { + switch (next()) { + case Q_OBJECT_TOKEN: + def.hasQObject = true; + break; + case Q_GADGET_EXPORT_TOKEN: + next(LPAREN); + while (test(IDENTIFIER)) + {} + next(RPAREN); + Q_FALLTHROUGH(); + case Q_GADGET_TOKEN: + def.hasQGadget = true; + break; + default: break; + } + } + + if (!def.hasQObject && !def.hasQGadget) + continue; + + prependNamespaces(def, namespaceList); + + QHash<QByteArray, QByteArray> &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets; + classHash.insert(def.classname, def.qualified); + classHash.insert(def.qualified, def.qualified); + + continue; } + default: break; + } + if ((t != CLASS && t != STRUCT)|| currentFilenames.size() > 1) + continue; + ClassDef def; + if (parseClassHead(&def)) { + prependNamespaces(def, namespaceList); + + FunctionDef::Access access = FunctionDef::Private; + while (inClass(&def) && hasNext()) { + switch ((t = next())) { + case PRIVATE: + access = FunctionDef::Private; + if (test(Q_SIGNALS_TOKEN)) + error("Signals cannot have access specifier"); + break; + case PROTECTED: + access = FunctionDef::Protected; + if (test(Q_SIGNALS_TOKEN)) + error("Signals cannot have access specifier"); + break; + case PUBLIC: + access = FunctionDef::Public; + if (test(Q_SIGNALS_TOKEN)) + error("Signals cannot have access specifier"); + break; + case CLASS: { + ClassDef nestedDef; + if (parseClassHead(&nestedDef)) { + while (inClass(&nestedDef) && inClass(&def)) { + t = next(); + if (t >= Q_META_TOKEN_BEGIN && t < Q_META_TOKEN_END) + error("Meta object features not supported for nested classes"); + } + } + } break; + case Q_SIGNALS_TOKEN: + parseSignals(&def); + break; + case Q_SLOTS_TOKEN: + switch (lookup(-1)) { + case PUBLIC: + case PROTECTED: + case PRIVATE: + parseSlots(&def, access); + break; + default: + error("Missing access specifier for slots"); + } + break; + case Q_OBJECT_TOKEN: + def.hasQObject = true; + if (templateClass) + error("Template classes not supported by Q_OBJECT"); + if (def.classname != "Qt" && def.classname != "QObject" && def.superclassList.isEmpty()) + error("Class contains Q_OBJECT macro but does not inherit from QObject"); + break; + case Q_GADGET_EXPORT_TOKEN: + next(LPAREN); + while (test(IDENTIFIER)) + {} + next(RPAREN); + Q_FALLTHROUGH(); + case Q_GADGET_TOKEN: + def.hasQGadget = true; + if (templateClass) + error("Template classes not supported by Q_GADGET"); + break; + case Q_PROPERTY_TOKEN: + parseProperty(&def, Named); + break; + case QT_ANONYMOUS_PROPERTY_TOKEN: + parseProperty(&def, Anonymous); + break; + case Q_PLUGIN_METADATA_TOKEN: + parsePluginData(&def); + break; + case Q_ENUMS_TOKEN: + case Q_ENUM_TOKEN: + parseEnumOrFlag(&def, false); + break; + case Q_ENUM_NS_TOKEN: + error("Q_ENUM_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_ENUM instead"); + break; + case Q_FLAGS_TOKEN: + case Q_FLAG_TOKEN: + parseEnumOrFlag(&def, true); + break; + case Q_FLAG_NS_TOKEN: + error("Q_FLAG_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_FLAG instead"); + break; + case Q_DECLARE_FLAGS_TOKEN: + parseFlag(&def); + break; + case Q_CLASSINFO_TOKEN: + parseClassInfo(&def); + break; + case Q_MOC_INCLUDE_TOKEN: + parseMocInclude(); + break; + case Q_INTERFACES_TOKEN: + parseInterfaces(&def); + break; + case Q_PRIVATE_SLOT_TOKEN: + parseSlotInPrivate(&def, access); + break; + case Q_PRIVATE_PROPERTY_TOKEN: + parsePrivateProperty(&def, Named); + break; + case QT_ANONYMOUS_PRIVATE_PROPERTY_TOKEN: + parsePrivateProperty(&def, Anonymous); + break; + case ENUM: { + EnumDef enumDef; + if (parseEnum(&enumDef)) + def.enumList += enumDef; + } break; + case SEMIC: + case COLON: + break; + default: + FunctionDef funcDef; + funcDef.access = access; + qsizetype rewind = index--; + if (parseMaybeFunction(&def, &funcDef)) { + if (funcDef.isConstructor) { + if ((access == FunctionDef::Public) && funcDef.isInvokable) { + def.constructorList += funcDef; + handleDefaultArguments(&def.constructorList, funcDef); + } + } else if (funcDef.isDestructor) { + // don't care about destructors + } else { + if (access == FunctionDef::Public) + def.publicList += funcDef; + if (funcDef.isSlot) { + def.slotList += funcDef; + handleDefaultArguments(&def.slotList, funcDef); + if (funcDef.revision > 0) + ++def.revisionedMethods; + } else if (funcDef.isSignal) { + def.signalList += funcDef; + handleDefaultArguments(&def.signalList, funcDef); + if (funcDef.revision > 0) + ++def.revisionedMethods; + } else if (funcDef.isInvokable) { + def.methodList += funcDef; + handleDefaultArguments(&def.methodList, funcDef); + if (funcDef.revision > 0) + ++def.revisionedMethods; + } + } + } else { + index = rewind; + } + } + } + + next(RBRACE); + + if (!def.hasQObject && !def.hasQGadget && def.signalList.isEmpty() && def.slotList.isEmpty() + && def.propertyList.isEmpty() && def.enumDeclarations.isEmpty()) + continue; // no meta object code required + + + if (!def.hasQObject && !def.hasQGadget) + error("Class declaration lacks Q_OBJECT macro."); + + // Add meta tags to the plugin meta data: + if (!def.pluginData.iid.isEmpty()) + def.pluginData.metaArgs = metaArgs; + + if (def.hasQObject && !def.superclassList.isEmpty()) + checkSuperClasses(&def); + + checkProperties(&def); + + checkListSizes(def); + + classList += def; + QHash<QByteArray, QByteArray> &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets; + classHash.insert(def.classname, def.qualified); + classHash.insert(def.qualified, def.qualified); + } + } + for (const auto &n : std::as_const(namespaceList)) { + if (!n.hasQNamespace) + continue; + ClassDef def; + static_cast<BaseDef &>(def) = static_cast<BaseDef>(n); + def.qualified += def.classname; + def.hasQNamespace = true; + auto it = std::find_if(classList.begin(), classList.end(), [&def](const ClassDef &val) { + return def.classname == val.classname && def.qualified == val.qualified; + }); + + if (it != classList.end()) { + it->classInfoList += def.classInfoList; + Q_ASSERT(it->classInfoList.size() <= std::numeric_limits<int>::max()); + it->enumDeclarations.insert(def.enumDeclarations); + it->enumList += def.enumList; + Q_ASSERT(it->enumList.size() <= std::numeric_limits<int>::max()); + it->flagAliases.insert(def.flagAliases); + } else { + knownGadgets.insert(def.classname, def.qualified); + knownGadgets.insert(def.qualified, def.qualified); + if (n.doGenerate) + classList += def; + } + } +} + +static bool any_type_contains(const QList<PropertyDef> &properties, const QByteArray &pattern) +{ + for (const auto &p : properties) { + if (p.type.contains(pattern)) + return true; + } + return false; +} + +static bool any_arg_contains(const QList<FunctionDef> &functions, const QByteArray &pattern) +{ + for (const auto &f : functions) { + for (const auto &arg : f.arguments) { + if (arg.normalizedType.contains(pattern)) + return true; + } + } + return false; +} + +static QByteArrayList make_candidates() +{ + QByteArrayList result; + result +#define STREAM_SMART_POINTER(SMART_POINTER) << #SMART_POINTER + QT_FOR_EACH_AUTOMATIC_TEMPLATE_SMART_POINTER(STREAM_SMART_POINTER) +#undef STREAM_SMART_POINTER +#define STREAM_1ARG_TEMPLATE(TEMPLATENAME) << #TEMPLATENAME + QT_FOR_EACH_AUTOMATIC_TEMPLATE_1ARG(STREAM_1ARG_TEMPLATE) +#undef STREAM_1ARG_TEMPLATE + ; + return result; +} + +static QByteArrayList requiredQtContainers(const QList<ClassDef> &classes) +{ + static const QByteArrayList candidates = make_candidates(); + + QByteArrayList required; + required.reserve(candidates.size()); + + bool needsQProperty = false; + + for (const auto &candidate : candidates) { + const QByteArray pattern = candidate + '<'; + + for (const auto &c : classes) { + for (const auto &p : c.propertyList) + needsQProperty |= !p.bind.isEmpty(); + if (any_type_contains(c.propertyList, pattern) || + any_arg_contains(c.slotList, pattern) || + any_arg_contains(c.signalList, pattern) || + any_arg_contains(c.methodList, pattern)) { + required.push_back(candidate); + break; + } + } + } + + if (needsQProperty) + required.push_back("QProperty"); + + return required; +} + +void Moc::generate(FILE *out, FILE *jsonOutput) +{ + QByteArrayView fn = QByteArrayView(filename); + + auto isSlash = [](char ch) { return ch == '/' || ch == '\\'; }; + auto rit = std::find_if(fn.crbegin(), fn.crend(), isSlash); + if (rit != fn.crend()) + fn = fn.last(rit - fn.crbegin()); + + fprintf(out, "/****************************************************************************\n" + "** Meta object code from reading C++ file '%s'\n**\n" , fn.constData()); + fprintf(out, "** Created by: The Qt Meta Object Compiler version %d (Qt %s)\n**\n" , mocOutputRevision, QT_VERSION_STR); + fprintf(out, "** WARNING! All changes made in this file will be lost!\n" + "*****************************************************************************/\n\n"); + + // include header(s) of user class definitions at _first_ to allow + // for preprocessor definitions possibly affecting standard headers. + // see https://codereview.qt-project.org/c/qt/qtbase/+/445937 + if (!noInclude) { + if (includePath.size() && !includePath.endsWith('/')) + includePath += '/'; + for (QByteArray inc : std::as_const(includeFiles)) { + if (!inc.isEmpty() && inc.at(0) != '<' && inc.at(0) != '"') { + if (includePath.size() && includePath != "./") + inc.prepend(includePath); + inc = '\"' + inc + '\"'; + } + fprintf(out, "#include %s\n", inc.constData()); + } + } + if (classList.size() && classList.constFirst().classname == "Qt") + fprintf(out, "#include <QtCore/qobject.h>\n"); + + fprintf(out, "#include <QtCore/qmetatype.h>\n"); // For QMetaType::Type + if (mustIncludeQPluginH) + fprintf(out, "#include <QtCore/qplugin.h>\n"); + + const auto qtContainers = requiredQtContainers(classList); + for (const QByteArray &qtContainer : qtContainers) + fprintf(out, "#include <QtCore/%s>\n", qtContainer.constData()); + + fprintf(out, "\n#include <QtCore/qtmochelpers.h>\n"); + + fprintf(out, "\n#include <memory>\n\n"); // For std::addressof + fprintf(out, "\n#include <QtCore/qxptype_traits.h>\n"); // is_detected + + fprintf(out, "#if !defined(Q_MOC_OUTPUT_REVISION)\n" + "#error \"The header file '%s' doesn't include <QObject>.\"\n", fn.constData()); + fprintf(out, "#elif Q_MOC_OUTPUT_REVISION != %d\n", mocOutputRevision); + fprintf(out, "#error \"This file was generated using the moc from %s." + " It\"\n#error \"cannot be used with the include files from" + " this version of Qt.\"\n#error \"(The moc has changed too" + " much.)\"\n", QT_VERSION_STR); + fprintf(out, "#endif\n\n"); + +#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0) + fprintf(out, "#ifndef Q_CONSTINIT\n" + "#define Q_CONSTINIT\n" + "#endif\n\n"); +#endif + + fprintf(out, "QT_WARNING_PUSH\n"); + fprintf(out, "QT_WARNING_DISABLE_DEPRECATED\n"); + fprintf(out, "QT_WARNING_DISABLE_GCC(\"-Wuseless-cast\")\n"); + + fputs("", out); + for (ClassDef &def : classList) { + Generator generator(this, &def, metaTypes, knownQObjectClasses, knownGadgets, out, + requireCompleteTypes); + generator.generateCode(); + + // generator.generateCode() should have already registered all strings + if (Q_UNLIKELY(generator.registeredStringsCount() >= std::numeric_limits<int>::max())) { + error("internal limit exceeded: number of parsed strings is too big."); + exit(EXIT_FAILURE); + } + } + fputs("", out); + + fprintf(out, "QT_WARNING_POP\n"); + + if (jsonOutput) { + QJsonObject mocData; + mocData["outputRevision"_L1] = mocOutputRevision; + mocData["inputFile"_L1] = QLatin1StringView(fn.constData()); + + QJsonArray classesJsonFormatted; + + for (const ClassDef &cdef: std::as_const(classList)) + classesJsonFormatted.append(cdef.toJson()); + + if (!classesJsonFormatted.isEmpty()) + mocData["classes"_L1] = classesJsonFormatted; + + QJsonDocument jsonDoc(mocData); + fputs(jsonDoc.toJson().constData(), jsonOutput); + } +} + +void Moc::parseSlots(ClassDef *def, FunctionDef::Access access) +{ + QTypeRevision defaultRevision; + if (test(Q_REVISION_TOKEN)) + defaultRevision = parseRevision(); + + next(COLON); + while (inClass(def) && hasNext()) { + switch (next()) { + case PUBLIC: + case PROTECTED: + case PRIVATE: + case Q_SIGNALS_TOKEN: + case Q_SLOTS_TOKEN: + prev(); + return; + case SEMIC: + continue; + case FRIEND: + until(SEMIC); + continue; + case USING: + error("'using' directive not supported in 'slots' section"); + default: + prev(); + } + + FunctionDef funcDef; + funcDef.access = access; + if (!parseFunction(&funcDef)) + continue; + if (funcDef.revision > 0) { + ++def->revisionedMethods; + } else if (defaultRevision.isValid()) { + funcDef.revision = defaultRevision.toEncodedVersion<int>(); + ++def->revisionedMethods; + } + def->slotList += funcDef; + handleDefaultArguments(&def->slotList, funcDef); + } +} + +void Moc::parseSignals(ClassDef *def) +{ + QTypeRevision defaultRevision; + if (test(Q_REVISION_TOKEN)) + defaultRevision = parseRevision(); + + next(COLON); + while (inClass(def) && hasNext()) { + switch (next()) { + case PUBLIC: + case PROTECTED: + case PRIVATE: + case Q_SIGNALS_TOKEN: + case Q_SLOTS_TOKEN: + prev(); + return; + case SEMIC: + continue; + case FRIEND: + until(SEMIC); + continue; + case USING: + error("'using' directive not supported in 'signals' section"); + default: + prev(); + } + FunctionDef funcDef; + funcDef.access = FunctionDef::Public; + parseFunction(&funcDef); + if (funcDef.isVirtual) + warning("Signals cannot be declared virtual"); + if (funcDef.inlineCode) + error("Not a signal declaration"); + if (funcDef.revision > 0) { + ++def->revisionedMethods; + } else if (defaultRevision.isValid()) { + funcDef.revision = defaultRevision.toEncodedVersion<int>(); + ++def->revisionedMethods; + } + def->signalList += funcDef; + handleDefaultArguments(&def->signalList, funcDef); + } +} + +void Moc::createPropertyDef(PropertyDef &propDef, int propertyIndex, Moc::PropertyMode mode) +{ + propDef.location = index; + propDef.relativeIndex = propertyIndex; + + QByteArray type = parseType().name; + if (type.isEmpty()) + error(); + propDef.designable = propDef.scriptable = propDef.stored = "true"; + propDef.user = "false"; + /* + The Q_PROPERTY construct cannot contain any commas, since + commas separate macro arguments. We therefore expect users + to type "QMap" instead of "QMap<QString, QVariant>". For + coherence, we also expect the same for + QValueList<QVariant>, the other template class supported by + QVariant. + */ + type = normalizeType(type); + if (type == "QMap") + type = "QMap<QString,QVariant>"; + else if (type == "QValueList") + type = "QValueList<QVariant>"; + else if (type == "LongLong") + type = "qlonglong"; + else if (type == "ULongLong") + type = "qulonglong"; + + propDef.type = type; + + if (mode == Moc::Named) { + next(); + propDef.name = lexem(); + } + + parsePropertyAttributes(propDef); +} + +void Moc::parsePropertyAttributes(PropertyDef &propDef) +{ + auto checkIsFunction = [&](const QByteArray &def, const char *name) { + if (def.endsWith(')')) { + QByteArray msg = "Providing a function for "; + msg += name; + msg += " in a property declaration is not be supported in Qt 6."; + error(msg.constData()); + } + }; + + while (test(IDENTIFIER)) { + const Symbol &lsym = symbol(); + const QByteArray l = lsym.lexem(); + if (l[0] == 'C' && l == "CONSTANT") { + propDef.constant = true; + continue; + } else if (l[0] == 'F' && l == "FINAL") { + propDef.final = true; + continue; + } else if (l[0] == 'N' && l == "NAME") { + next(IDENTIFIER); + propDef.name = lexem(); + continue; + } else if (l[0] == 'R' && l == "REQUIRED") { + propDef.required = true; + continue; + } else if (l[0] == 'R' && l == "REVISION" && test(LPAREN)) { + prev(); + propDef.revision = parseRevision().toEncodedVersion<int>(); + continue; + } + + QByteArray v, v2; + if (test(LPAREN)) { + v = lexemUntil(RPAREN); + v = v.mid(1, v.size() - 2); // removes the '(' and ')' + } else if (test(INTEGER_LITERAL)) { + v = lexem(); + if (l != "REVISION") + error(lsym); + } else if (test(DEFAULT)) { + v = lexem(); + if (l != "READ" && l != "WRITE") + error(lsym); + } else { + next(IDENTIFIER); + v = lexem(); + if (test(LPAREN)) + v2 = lexemUntil(RPAREN); + else if (v != "true" && v != "false") + v2 = "()"; + } + switch (l[0]) { + case 'M': + if (l == "MEMBER") + propDef.member = v; + else + error(lsym); + break; + case 'R': + if (l == "READ") + propDef.read = v; + else if (l == "RESET") + propDef.reset = v; + else if (l == "REVISION") { + bool ok = false; + const int minor = v.toInt(&ok); + if (!ok || !QTypeRevision::isValidSegment(minor)) + error(lsym); + propDef.revision = QTypeRevision::fromMinorVersion(minor).toEncodedVersion<int>(); + } else + error(lsym); + break; + case 'S': + if (l == "SCRIPTABLE") { + propDef.scriptable = v + v2; + checkIsFunction(propDef.scriptable, "SCRIPTABLE"); + } else if (l == "STORED") { + propDef.stored = v + v2; + checkIsFunction(propDef.stored, "STORED"); + } else + error(lsym); + break; + case 'W': if (l != "WRITE") error(lsym); + propDef.write = v; + break; + case 'B': if (l != "BINDABLE") error(lsym); + propDef.bind = v; + break; + case 'D': if (l != "DESIGNABLE") error(lsym); + propDef.designable = v + v2; + checkIsFunction(propDef.designable, "DESIGNABLE"); + break; + case 'N': if (l != "NOTIFY") error(lsym); + propDef.notify = v; + break; + case 'U': if (l != "USER") error(lsym); + propDef.user = v + v2; + checkIsFunction(propDef.user, "USER"); + break; + default: + error(lsym); + } + } + if (propDef.constant && !propDef.write.isNull()) { + const QByteArray msg = "Property declaration " + propDef.name + + " is both WRITEable and CONSTANT. CONSTANT will be ignored."; + propDef.constant = false; + warning(msg.constData()); + } + if (propDef.constant && !propDef.notify.isNull()) { + const QByteArray msg = "Property declaration " + propDef.name + + " is both NOTIFYable and CONSTANT. CONSTANT will be ignored."; + propDef.constant = false; + warning(msg.constData()); + } + if (propDef.constant && !propDef.bind.isNull()) { + const QByteArray msg = "Property declaration " + propDef.name + + " is both BINDable and CONSTANT. CONSTANT will be ignored."; + propDef.constant = false; + warning(msg.constData()); + } + if (propDef.read == "default" && propDef.bind.isNull()) { + const QByteArray msg = "Property declaration " + propDef.name + + " is not BINDable but default-READable. READ will be ignored."; + propDef.read = ""; + warning(msg.constData()); + } + if (propDef.write == "default" && propDef.bind.isNull()) { + const QByteArray msg = "Property declaration " + propDef.name + + " is not BINDable but default-WRITEable. WRITE will be ignored."; + propDef.write = ""; + warning(msg.constData()); + } +} + +void Moc::parseProperty(ClassDef *def, Moc::PropertyMode mode) +{ + next(LPAREN); + PropertyDef propDef; + createPropertyDef(propDef, int(def->propertyList.size()), mode); + next(RPAREN); + + def->propertyList += propDef; +} + +void Moc::parsePluginData(ClassDef *def) +{ + next(LPAREN); + QByteArray metaData; + while (test(IDENTIFIER)) { + QByteArray l = lexem(); + if (l == "IID") { + next(STRING_LITERAL); + def->pluginData.iid = unquotedLexem(); + } else if (l == "URI") { + next(STRING_LITERAL); + def->pluginData.uri = unquotedLexem(); + } else if (l == "FILE") { + next(STRING_LITERAL); + QByteArray metaDataFile = unquotedLexem(); + QFileInfo fi(QFileInfo(QString::fromLocal8Bit(currentFilenames.top())).dir(), + QString::fromLocal8Bit(metaDataFile)); + for (const IncludePath &p : std::as_const(includes)) { + if (fi.exists()) + break; + if (p.isFrameworkPath) + continue; + + fi.setFile(QString::fromLocal8Bit(p.path.constData()), QString::fromLocal8Bit(metaDataFile.constData())); + // try again, maybe there's a file later in the include paths with the same name + if (fi.isDir()) { + fi = QFileInfo(); + continue; + } + } + if (!fi.exists()) { + const QByteArray msg = "Plugin Metadata file " + lexem() + + " does not exist. Declaration will be ignored"; + error(msg.constData()); + return; + } + QFile file(fi.canonicalFilePath()); + if (!file.open(QFile::ReadOnly)) { + QByteArray msg = "Plugin Metadata file " + lexem() + " could not be opened: " + + file.errorString().toUtf8(); + error(msg.constData()); + return; + } + parsedPluginMetadataFiles.append(fi.canonicalFilePath()); + metaData = file.readAll(); + } + } + + if (!metaData.isEmpty()) { + def->pluginData.metaData = QJsonDocument::fromJson(metaData); + if (!def->pluginData.metaData.isObject()) { + const QByteArray msg = "Plugin Metadata file " + lexem() + + " does not contain a valid JSON object. Declaration will be ignored"; + warning(msg.constData()); + def->pluginData.iid = QByteArray(); + def->pluginData.uri = QByteArray(); + return; + } + } + + mustIncludeQPluginH = true; + next(RPAREN); +} + +QByteArray Moc::parsePropertyAccessor() +{ + int nesting = 0; + QByteArray accessor; + while (1) { + Token t = peek(); + if (!nesting && (t == RPAREN || t == COMMA)) + break; + t = next(); + if (t == LPAREN) + ++nesting; + if (t == RPAREN) + --nesting; + accessor += lexem(); + } + return accessor; +} + +void Moc::parsePrivateProperty(ClassDef *def, Moc::PropertyMode mode) +{ + next(LPAREN); + PropertyDef propDef; + propDef.inPrivateClass = parsePropertyAccessor(); + + next(COMMA); + + createPropertyDef(propDef, int(def->propertyList.size()), mode); + + def->propertyList += propDef; +} + +void Moc::parseEnumOrFlag(BaseDef *def, bool isFlag) +{ + next(LPAREN); + QByteArray identifier; + while (test(IDENTIFIER)) { + identifier = lexem(); + while (test(SCOPE) && test(IDENTIFIER)) { + identifier += "::"; + identifier += lexem(); + } + def->enumDeclarations[identifier] = isFlag; + } + next(RPAREN); +} + +void Moc::parseFlag(BaseDef *def) +{ + next(LPAREN); + QByteArray flagName, enumName; + while (test(IDENTIFIER)) { + flagName = lexem(); + while (test(SCOPE) && test(IDENTIFIER)) { + flagName += "::"; + flagName += lexem(); + } + } + next(COMMA); + while (test(IDENTIFIER)) { + enumName = lexem(); + while (test(SCOPE) && test(IDENTIFIER)) { + enumName += "::"; + enumName += lexem(); + } + } + + def->flagAliases.insert(enumName, flagName); + next(RPAREN); +} + +Moc::EncounteredQmlMacro Moc::parseClassInfo(BaseDef *def) +{ + bool encounteredQmlMacro = false; + next(LPAREN); + ClassInfoDef infoDef; + next(STRING_LITERAL); + infoDef.name = symbol().unquotedLexem(); + if (infoDef.name.startsWith("QML.")) + encounteredQmlMacro = true; + next(COMMA); + if (test(STRING_LITERAL)) { + infoDef.value = symbol().unquotedLexem(); + } else if (test(Q_REVISION_TOKEN)) { + infoDef.value = QByteArray::number(parseRevision().toEncodedVersion<quint16>()); + } else { + // support Q_CLASSINFO("help", QT_TR_NOOP("blah")) + next(IDENTIFIER); + next(LPAREN); + next(STRING_LITERAL); + infoDef.value = symbol().unquotedLexem(); + next(RPAREN); + } + next(RPAREN); + def->classInfoList += infoDef; + return encounteredQmlMacro ? EncounteredQmlMacro::Yes : EncounteredQmlMacro::No; +} + +void Moc::parseClassInfo(ClassDef *def) +{ + if (parseClassInfo(static_cast<BaseDef *>(def)) == EncounteredQmlMacro::Yes) + def->requireCompleteMethodTypes = true; +} + +void Moc::parseInterfaces(ClassDef *def) +{ + next(LPAREN); + while (test(IDENTIFIER)) { + QList<ClassDef::Interface> iface; + iface += ClassDef::Interface(lexem()); + while (test(SCOPE)) { + iface.last().className += lexem(); + next(IDENTIFIER); + iface.last().className += lexem(); + } + while (test(COLON)) { + next(IDENTIFIER); + iface += ClassDef::Interface(lexem()); + while (test(SCOPE)) { + iface.last().className += lexem(); + next(IDENTIFIER); + iface.last().className += lexem(); + } + } + // resolve from classnames to interface ids + for (qsizetype i = 0; i < iface.size(); ++i) { + const QByteArray iid = interface2IdMap.value(iface.at(i).className); + if (iid.isEmpty()) + error("Undefined interface"); + + iface[i].interfaceId = iid; + } + def->interfaceList += iface; + } + next(RPAREN); +} + +void Moc::parseDeclareInterface() +{ + next(LPAREN); + QByteArray interface; + next(IDENTIFIER); + interface += lexem(); + while (test(SCOPE)) { + interface += lexem(); + next(IDENTIFIER); + interface += lexem(); + } + next(COMMA); + QByteArray iid; + if (test(STRING_LITERAL)) { + iid = lexem(); + } else { + next(IDENTIFIER); + iid = lexem(); + } + interface2IdMap.insert(interface, iid); + next(RPAREN); +} + +void Moc::parseDeclareMetatype() +{ + next(LPAREN); + QByteArray typeName = lexemUntil(RPAREN); + typeName.remove(0, 1); + typeName.chop(1); + metaTypes.append(typeName); +} + +void Moc::parseMocInclude() +{ + next(LPAREN); + QByteArray include = lexemUntil(RPAREN); + // remove parentheses + include.remove(0, 1); + include.chop(1); + includeFiles.append(include); +} + +void Moc::parseSlotInPrivate(ClassDef *def, FunctionDef::Access access) +{ + next(LPAREN); + FunctionDef funcDef; + next(IDENTIFIER); + funcDef.inPrivateClass = lexem(); + // also allow void functions + if (test(LPAREN)) { + next(RPAREN); + funcDef.inPrivateClass += "()"; + } + next(COMMA); + funcDef.access = access; + parseFunction(&funcDef, true); + def->slotList += funcDef; + handleDefaultArguments(&def->slotList, funcDef); + if (funcDef.revision > 0) + ++def->revisionedMethods; + +} + +QByteArray Moc::lexemUntil(Token target) +{ + qsizetype from = index; + until(target); + QByteArray s; + while (from <= index) { + QByteArray n = symbols.at(from++-1).lexem(); + if (s.size() && n.size()) { + char prev = s.at(s.size()-1); + char next = n.at(0); + if ((is_ident_char(prev) && is_ident_char(next)) + || (prev == '<' && next == ':') + || (prev == '>' && next == '>')) + s += ' '; + } + s += n; + } + return s; +} + +bool Moc::until(Token target) { + int braceCount = 0; + int brackCount = 0; + int parenCount = 0; + int angleCount = 0; + if (index) { + switch(symbols.at(index-1).token) { + case LBRACE: ++braceCount; break; + case LBRACK: ++brackCount; break; + case LPAREN: ++parenCount; break; + case LANGLE: ++angleCount; break; + default: break; + } + } + + //when searching commas within the default argument, we should take care of template depth (anglecount) + // unfortunately, we do not have enough semantic information to know if '<' is the operator< or + // the beginning of a template type. so we just use heuristics. + qsizetype possible = -1; + + while (index < symbols.size()) { + Token t = symbols.at(index++).token; + switch (t) { + case LBRACE: ++braceCount; break; + case RBRACE: --braceCount; break; + case LBRACK: ++brackCount; break; + case RBRACK: --brackCount; break; + case LPAREN: ++parenCount; break; + case RPAREN: --parenCount; break; + case LANGLE: + if (parenCount == 0 && braceCount == 0) + ++angleCount; + break; + case RANGLE: + if (parenCount == 0 && braceCount == 0) + --angleCount; + break; + case GTGT: + if (parenCount == 0 && braceCount == 0) { + angleCount -= 2; + t = RANGLE; + } + break; + default: break; + } + if (t == target + && braceCount <= 0 + && brackCount <= 0 + && parenCount <= 0 + && (target != RANGLE || angleCount <= 0)) { + if (target != COMMA || angleCount <= 0) + return true; + possible = index; + } + + if (target == COMMA && t == EQ && possible != -1) { + index = possible; + return true; + } + + if (braceCount < 0 || brackCount < 0 || parenCount < 0 + || (target == RANGLE && angleCount < 0)) { + --index; + break; + } + + if (braceCount <= 0 && t == SEMIC) { + // Abort on semicolon. Allow recovering bad template parsing (QTBUG-31218) + break; + } + } + + if (target == COMMA && angleCount != 0 && possible != -1) { + index = possible; + return true; + } + + return false; +} + +void Moc::checkSuperClasses(ClassDef *def) +{ + Q_ASSERT(!def->superclassList.isEmpty()); + const QByteArray &firstSuperclass = def->superclassList.at(0).classname; + + if (!knownQObjectClasses.contains(firstSuperclass)) { + // enable once we /require/ include paths +#if 0 + const QByteArray msg + = "Class " + + def->className + + " contains the Q_OBJECT macro and inherits from " + + def->superclassList.value(0) + + " but that is not a known QObject subclass. You may get compilation errors."; + warning(msg.constData()); +#endif + return; + } + + auto isRegisteredInterface = [&def](QByteArrayView super) { + auto matchesSuperClass = [&super](const auto &ifaces) { + return !ifaces.isEmpty() && ifaces.first().className == super; + }; + return std::any_of(def->interfaceList.cbegin(), def->interfaceList.cend(), matchesSuperClass); + }; + + const auto end = def->superclassList.cend(); + auto it = def->superclassList.cbegin() + 1; + for (; it != end; ++it) { + const QByteArray &superClass = it->classname; + if (knownQObjectClasses.contains(superClass)) { + const QByteArray msg + = "Class " + + def->classname + + " inherits from two QObject subclasses " + + firstSuperclass + + " and " + + superClass + + ". This is not supported!"; + warning(msg.constData()); + } + + if (interface2IdMap.contains(superClass)) { + if (!isRegisteredInterface(superClass)) { + const QByteArray msg + = "Class " + + def->classname + + " implements the interface " + + superClass + + " but does not list it in Q_INTERFACES. qobject_cast to " + + superClass + + " will not work!"; + warning(msg.constData()); + } + } + } +} + +void Moc::checkProperties(ClassDef *cdef) +{ + // + // specify get function, for compatibility we accept functions + // returning pointers, or const char * for QByteArray. + // + QDuplicateTracker<QByteArray> definedProperties(cdef->propertyList.size()); + auto hasNoAttributes = [&](const PropertyDef &p) { + if (definedProperties.hasSeen(p.name)) { + QByteArray msg = "The property '" + p.name + "' is defined multiple times in class " + cdef->classname + "."; + warning(msg.constData()); + } + + if (p.read.isEmpty() && p.member.isEmpty() && p.bind.isEmpty()) { + QByteArray msg = "Property declaration " + p.name + " has neither an associated QProperty<> member" + ", nor a READ accessor function nor an associated MEMBER variable. The property will be invalid."; + const auto &sym = p.location >= 0 ? symbolAt(p.location) : Symbol(); + warning(sym, msg.constData()); + if (p.write.isEmpty()) + return true; + } + return false; + }; + cdef->propertyList.removeIf(hasNoAttributes); + + for (PropertyDef &p : cdef->propertyList) { + for (const FunctionDef &f : std::as_const(cdef->publicList)) { + if (f.name != p.read) + continue; + if (!f.isConst) // get functions must be const + continue; + if (f.arguments.size()) // and must not take any arguments + continue; + PropertyDef::Specification spec = PropertyDef::ValueSpec; + QByteArray tmp = f.normalizedType; + if (p.type == "QByteArray" && tmp == "const char *") + tmp = "QByteArray"; + if (tmp.left(6) == "const ") + tmp = tmp.mid(6); + if (p.type != tmp && tmp.endsWith('*')) { + tmp.chop(1); + spec = PropertyDef::PointerSpec; + } else if (f.type.name.endsWith('&')) { // raw type, not normalized type + spec = PropertyDef::ReferenceSpec; + } + if (p.type != tmp) + continue; + p.gspec = spec; + break; + } + if (!p.notify.isEmpty()) { + int notifyId = -1; + for (int j = 0; j < int(cdef->signalList.size()); ++j) { + const FunctionDef &f = cdef->signalList.at(j); + if (f.name != p.notify) { + continue; + } else { + notifyId = j /* Signal indexes start from 0 */; + break; + } + } + p.notifyId = notifyId; + if (notifyId == -1) { + const int index = int(cdef->nonClassSignalList.indexOf(p.notify)); + if (index == -1) { + cdef->nonClassSignalList << p.notify; + p.notifyId = int(-1 - cdef->nonClassSignalList.size()); + } else { + p.notifyId = int(-2 - index); + } + } + } + } +} +#endif // -- QtScxml + +QJsonObject ClassDef::toJson() const +{ + QJsonObject cls; + cls["className"_L1] = QString::fromUtf8(classname.constData()); + cls["qualifiedClassName"_L1] = QString::fromUtf8(qualified.constData()); + + QJsonArray classInfos; + for (const auto &info: std::as_const(classInfoList)) { + QJsonObject infoJson; + infoJson["name"_L1] = QString::fromUtf8(info.name); + infoJson["value"_L1] = QString::fromUtf8(info.value); + classInfos.append(infoJson); + } + + if (classInfos.size()) + cls["classInfos"_L1] = classInfos; + + const auto appendFunctions = [&cls](const QString &type, const QList<FunctionDef> &funcs) { + QJsonArray jsonFuncs; + + for (const FunctionDef &fdef: funcs) + jsonFuncs.append(fdef.toJson()); + + if (!jsonFuncs.isEmpty()) + cls[type] = jsonFuncs; + }; + + appendFunctions("signals"_L1, signalList); + appendFunctions("slots"_L1, slotList); + appendFunctions("constructors"_L1, constructorList); + appendFunctions("methods"_L1, methodList); + + QJsonArray props; + + for (const PropertyDef &propDef: std::as_const(propertyList)) + props.append(propDef.toJson()); + + if (!props.isEmpty()) + cls["properties"_L1] = props; + + if (hasQObject) + cls["object"_L1] = true; + if (hasQGadget) + cls["gadget"_L1] = true; + if (hasQNamespace) + cls["namespace"_L1] = true; + + QJsonArray superClasses; + + for (const auto &super: std::as_const(superclassList)) { + QJsonObject superCls; + superCls["name"_L1] = QString::fromUtf8(super.classname); + if (super.classname != super.qualified) + superCls["fullyQualifiedName"_L1] = QString::fromUtf8(super.qualified); + FunctionDef::accessToJson(&superCls, super.access); + superClasses.append(superCls); + } + + if (!superClasses.isEmpty()) + cls["superClasses"_L1] = superClasses; + + QJsonArray enums; + for (const EnumDef &enumDef: std::as_const(enumList)) + enums.append(enumDef.toJson(*this)); + if (!enums.isEmpty()) + cls["enums"_L1] = enums; + + QJsonArray ifaces; + for (const QList<Interface> &ifaceList : interfaceList) { + QJsonArray jsonList; + for (const Interface &iface: ifaceList) { + QJsonObject ifaceJson; + ifaceJson["id"_L1] = QString::fromUtf8(iface.interfaceId); + ifaceJson["className"_L1] = QString::fromUtf8(iface.className); + jsonList.append(ifaceJson); + } + ifaces.append(jsonList); + } + if (!ifaces.isEmpty()) + cls["interfaces"_L1] = ifaces; + + return cls; +} + +QJsonObject FunctionDef::toJson() const +{ + QJsonObject fdef; + fdef["name"_L1] = QString::fromUtf8(name); + if (!tag.isEmpty()) + fdef["tag"_L1] = QString::fromUtf8(tag); + fdef["returnType"_L1] = QString::fromUtf8(normalizedType); + + QJsonArray args; + for (const ArgumentDef &arg: arguments) + args.append(arg.toJson()); + + if (!args.isEmpty()) + fdef["arguments"_L1] = args; + + accessToJson(&fdef, access); + + if (revision > 0) + fdef["revision"_L1] = revision; + + if (wasCloned) + fdef["isCloned"_L1] = true; + + return fdef; +} + +void FunctionDef::accessToJson(QJsonObject *obj, FunctionDef::Access acs) +{ + switch (acs) { + case Private: (*obj)["access"_L1] = "private"_L1; break; + case Public: (*obj)["access"_L1] = "public"_L1; break; + case Protected: (*obj)["access"_L1] = "protected"_L1; break; + } +} + +QJsonObject ArgumentDef::toJson() const +{ + QJsonObject arg; + arg["type"_L1] = QString::fromUtf8(normalizedType); + if (!name.isEmpty()) + arg["name"_L1] = QString::fromUtf8(name); + return arg; +} + +QJsonObject PropertyDef::toJson() const +{ + QJsonObject prop; + prop["name"_L1] = QString::fromUtf8(name); + prop["type"_L1] = QString::fromUtf8(type); + + const auto jsonify = [&prop](const char *str, const QByteArray &member) { + if (!member.isEmpty()) + prop[QLatin1StringView(str)] = QString::fromUtf8(member); + }; + + jsonify("member", member); + jsonify("read", read); + jsonify("write", write); + jsonify("bindable", bind); + jsonify("reset", reset); + jsonify("notify", notify); + jsonify("privateClass", inPrivateClass); + + const auto jsonifyBoolOrString = [&prop](const char *str, const QByteArray &boolOrString) { + QJsonValue value; + if (boolOrString == "true") + value = true; + else if (boolOrString == "false") + value = false; + else + value = QString::fromUtf8(boolOrString); // function name to query at run-time + prop[QLatin1StringView(str)] = value; + }; + + jsonifyBoolOrString("designable", designable); + jsonifyBoolOrString("scriptable", scriptable); + jsonifyBoolOrString("stored", stored); + jsonifyBoolOrString("user", user); + + prop["constant"_L1] = constant; + prop["final"_L1] = final; + prop["required"_L1] = required; + prop["index"_L1] = relativeIndex; + if (revision > 0) + prop["revision"_L1] = revision; + + return prop; +} + +QJsonObject EnumDef::toJson(const ClassDef &cdef) const +{ + QJsonObject def; + def["name"_L1] = QString::fromUtf8(name); + if (!enumName.isEmpty()) + def["alias"_L1] = QString::fromUtf8(enumName); + if (!type.isEmpty()) + def["type"_L1] = QString::fromUtf8(type); + def["isFlag"_L1] = cdef.enumDeclarations.value(name); + def["isClass"_L1] = isEnumClass; + + QJsonArray valueArr; + for (const QByteArray &value: values) + valueArr.append(QString::fromUtf8(value)); + if (!valueArr.isEmpty()) + def["values"_L1] = valueArr; + + return def; +} + +QByteArray EnumDef::qualifiedType(const ClassDef *cdef) const +{ + if (name == cdef->classname) { + // The name of the enclosing namespace is the same as the enum class name + if (cdef->qualified.contains("::")) { + // QTBUG-112996, fully qualify by using cdef->qualified to disambiguate enum + // class name and enclosing namespace, e.g.: + // namespace A { namespace B { Q_NAMESPACE; enum class B { }; Q_ENUM_NS(B) } } + return cdef->qualified % "::" % name; + } else { + // Just "B"; otherwise the compiler complains about the type "B::B" inside + // "B::staticMetaObject" in the generated code; e.g.: + // namespace B { Q_NAMESPACE; enum class B { }; Q_ENUM_NS(B) } + return name; + } + } + return cdef->classname % "::" % name; +} + +QT_END_NAMESPACE diff --git a/tools/qscxmlc/moc.h b/tools/qscxmlc/moc.h index 56aedad..19aefb9 100644 --- a/tools/qscxmlc/moc.h +++ b/tools/qscxmlc/moc.h @@ -1,39 +1,17 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtScxml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef MOC_H #define MOC_H +// -- QtScxml #include <QtCore/qmap.h> #include <QtCore/qpair.h> #include <QtCore/qjsondocument.h> #include <QtCore/qjsonarray.h> -#include <ctype.h> +// -- QtScxml + +#include <private/qtools_p.h> QT_BEGIN_NAMESPACE @@ -43,26 +21,35 @@ struct Type { enum ReferenceType { NoReference, Reference, RValueReference, Pointer }; - inline Type() : isVolatile(false), isScoped(false), /*firstToken(NOTOKEN), */referenceType(NoReference) {} + inline Type() : isVolatile(false), isScoped(false), /* firstToken(NOTOKEN) -- QtScxml ,*/ referenceType(NoReference) {} inline explicit Type(const QByteArray &_name) - : name(_name), rawName(name), isVolatile(false), isScoped(false), /*firstToken(NOTOKEN),*/ referenceType(NoReference) {} + : name(_name), rawName(name), isVolatile(false), isScoped(false), /* firstToken(NOTOKEN) -- QtScxml ,*/ referenceType(NoReference) {} QByteArray name; //When used as a return type, the type name may be modified to remove the references. // rawName is the type as found in the function signature QByteArray rawName; uint isVolatile : 1; uint isScoped : 1; -// Token firstToken; +#if 0 // -- QtScxml + Token firstToken; +#endif // -- QtScxml ReferenceType referenceType; }; +Q_DECLARE_TYPEINFO(Type, Q_RELOCATABLE_TYPE); +struct ClassDef; struct EnumDef { QByteArray name; + QByteArray enumName; + QByteArray type; QList<QByteArray> values; bool isEnumClass; // c++11 enum class EnumDef() : isEnumClass(false) {} + QJsonObject toJson(const ClassDef &cdef) const; + QByteArray qualifiedType(const ClassDef *cdef) const; }; +Q_DECLARE_TYPEINFO(EnumDef, Q_RELOCATABLE_TYPE); struct ArgumentDef { @@ -71,125 +58,260 @@ struct ArgumentDef QByteArray rightType, normalizedType, name; QByteArray typeNameForCast; // type name to be used in cast from void * in metacall bool isDefault; + + QJsonObject toJson() const; }; +Q_DECLARE_TYPEINFO(ArgumentDef, Q_RELOCATABLE_TYPE); struct FunctionDef { - FunctionDef(): access(Private), isConst(false), isVirtual(false), isStatic(false), - inlineCode(false), wasCloned(false), isCompat(false), isInvokable(false), - isScriptable(false), isSlot(false), isSignal(false), isPrivateSignal(false), - isConstructor(false), isDestructor(false), isAbstract(false), revision(0), implementation(0) {} Type type; + QList<ArgumentDef> arguments; QByteArray normalizedType; QByteArray tag; QByteArray name; - QByteArray mangledName; - - QList<ArgumentDef> arguments; + QByteArray inPrivateClass; enum Access { Private, Protected, Public }; - Access access; - bool isConst; - bool isVirtual; - bool isStatic; - bool inlineCode; - bool wasCloned; + Access access = Private; + int revision = 0; - QByteArray inPrivateClass; - bool isCompat; - bool isInvokable; - bool isScriptable; - bool isSlot; - bool isSignal; - bool isPrivateSignal; - bool isConstructor; - bool isDestructor; - bool isAbstract; - - int revision; - - const char *implementation; + bool isConst = false; + bool isVirtual = false; + bool isStatic = false; + bool inlineCode = false; + bool wasCloned = false; + +#if 0 // -- QtScxml + bool returnTypeIsVolatile = false; +#endif // -- QtScxml + bool isCompat = false; + bool isInvokable = false; + bool isScriptable = false; + bool isSlot = false; + bool isSignal = false; + bool isPrivateSignal = false; + bool isConstructor = false; + bool isDestructor = false; + bool isAbstract = false; + bool isRawSlot = false; + + QJsonObject toJson() const; + static void accessToJson(QJsonObject *obj, Access acs); + +// -- QtScxml + QByteArray mangledName; + const char *implementation = nullptr; +// -- QtScxml }; +Q_DECLARE_TYPEINFO(FunctionDef, Q_RELOCATABLE_TYPE); struct PropertyDef { - PropertyDef():notifyId(-1), constant(false), final(false), gspec(ValueSpec), revision(0){} - QByteArray name, mangledName, type, member, read, write, reset, designable, scriptable, - editable, stored, user, notify, inPrivateClass; - int notifyId; - bool constant; - bool final; - enum Specification { ValueSpec, ReferenceSpec, PointerSpec }; - Specification gspec; bool stdCppSet() const { + if (name.isEmpty()) + return false; QByteArray s("set"); - s += toupper(name[0]); + s += QtMiscUtils::toAsciiUpper(name[0]); s += name.mid(1); return (s == write); } - int revision; + + QByteArray name, type, member, read, write, bind, reset, designable, scriptable, stored, user, notify, inPrivateClass; + int notifyId = -1; // -1 means no notifyId, >= 0 means signal defined in this class, < -1 means signal not defined in this class + enum Specification { ValueSpec, ReferenceSpec, PointerSpec }; + Specification gspec = ValueSpec; + int revision = 0; + bool constant = false; + bool final = false; + bool required = false; + int relativeIndex = -1; // property index in current metaobject + + qsizetype location = -1; // token index, used for error reporting + + QJsonObject toJson() const; + +// -- QtScxml + QByteArray mangledName; +// -- QtScxml }; +Q_DECLARE_TYPEINFO(PropertyDef, Q_RELOCATABLE_TYPE); +struct PrivateQPropertyDef +{ + Type type; + QByteArray name; + QByteArray setter; + QByteArray accessor; + QByteArray storage; +}; +Q_DECLARE_TYPEINFO(PrivateQPropertyDef, Q_RELOCATABLE_TYPE); struct ClassInfoDef { QByteArray name; QByteArray value; }; +Q_DECLARE_TYPEINFO(ClassInfoDef, Q_RELOCATABLE_TYPE); + +struct BaseDef { + QByteArray classname; + QByteArray qualified; + QList<ClassInfoDef> classInfoList; + QMap<QByteArray, bool> enumDeclarations; + QList<EnumDef> enumList; + QMap<QByteArray, QByteArray> flagAliases; + qsizetype begin = 0; + qsizetype end = 0; +}; -struct ClassDef { - ClassDef(): - hasQObject(false), hasQGadget(false), notifyableProperties(0) - , revisionedMethods(0), revisionedProperties(0), begin(0), end(0){} +struct SuperClass { QByteArray classname; QByteArray qualified; - QList<QPair<QByteArray, FunctionDef::Access> > superclassList; + FunctionDef::Access access; +}; +Q_DECLARE_TYPEINFO(SuperClass, Q_RELOCATABLE_TYPE); + +struct ClassDef : BaseDef { + QList<SuperClass> superclassList; struct Interface { + Interface() { } // for QList, don't use inline explicit Interface(const QByteArray &_className) : className(_className) {} QByteArray className; QByteArray interfaceId; }; - QList<QList<Interface> >interfaceList; - - bool hasQObject; - bool hasQGadget; + QList<QList<Interface>> interfaceList; struct PluginData { QByteArray iid; + QByteArray uri; QMap<QString, QJsonArray> metaArgs; QJsonDocument metaData; } pluginData; QList<FunctionDef> constructorList; QList<FunctionDef> signalList, slotList, methodList, publicList; - int notifyableProperties; + QList<QByteArray> nonClassSignalList; QList<PropertyDef> propertyList; - QList<ClassInfoDef> classInfoList; - QMap<QByteArray, bool> enumDeclarations; - QList<EnumDef> enumList; - QMap<QByteArray, QByteArray> flagAliases; - int revisionedMethods; - int revisionedProperties; + int revisionedMethods = 0; + + bool hasQObject = false; + bool hasQGadget = false; + bool hasQNamespace = false; + bool requireCompleteMethodTypes = false; - int begin; - int end; + QJsonObject toJson() const; }; +Q_DECLARE_TYPEINFO(ClassDef, Q_RELOCATABLE_TYPE); +Q_DECLARE_TYPEINFO(ClassDef::Interface, Q_RELOCATABLE_TYPE); -struct NamespaceDef { - QByteArray name; - int begin; - int end; +struct NamespaceDef : BaseDef { + bool hasQNamespace = false; + bool doGenerate = false; +}; +Q_DECLARE_TYPEINFO(NamespaceDef, Q_RELOCATABLE_TYPE); + +#if 0 // -- QtScxml +class Moc : public Parser +{ +public: + enum PropertyMode { Named, Anonymous }; + + Moc() + : noInclude(false), mustIncludeQPluginH(false), requireCompleteTypes(false) + {} + + QByteArray filename; + + bool noInclude; + bool mustIncludeQPluginH; + bool requireCompleteTypes; + QByteArray includePath; + QList<QByteArray> includeFiles; + QList<ClassDef> classList; + QMap<QByteArray, QByteArray> interface2IdMap; + QList<QByteArray> metaTypes; + // map from class name to fully qualified name + QHash<QByteArray, QByteArray> knownQObjectClasses; + QHash<QByteArray, QByteArray> knownGadgets; + QMap<QString, QJsonArray> metaArgs; + QList<QString> parsedPluginMetadataFiles; + + void parse(); + void generate(FILE *out, FILE *jsonOutput); + + bool parseClassHead(ClassDef *def); + inline bool inClass(const ClassDef *def) const { + return index > def->begin && index < def->end - 1; + } + + inline bool inNamespace(const NamespaceDef *def) const { + return index > def->begin && index < def->end - 1; + } + + const QByteArray &toFullyQualified(const QByteArray &name) const noexcept; + + void prependNamespaces(BaseDef &def, const QList<NamespaceDef> &namespaceList) const; + + Type parseType(); + + bool parseEnum(EnumDef *def); + + bool parseFunction(FunctionDef *def, bool inMacro = false); + bool parseMaybeFunction(const ClassDef *cdef, FunctionDef *def); + + void parseSlots(ClassDef *def, FunctionDef::Access access); + void parseSignals(ClassDef *def); + void parseProperty(ClassDef *def, PropertyMode mode); + void parsePluginData(ClassDef *def); + + void createPropertyDef(PropertyDef &def, int propertyIndex, PropertyMode mode); + + void parsePropertyAttributes(PropertyDef &propDef); + void parseEnumOrFlag(BaseDef *def, bool isFlag); + void parseFlag(BaseDef *def); + enum class EncounteredQmlMacro {Yes, No}; + EncounteredQmlMacro parseClassInfo(BaseDef *def); + void parseClassInfo(ClassDef *def); + void parseInterfaces(ClassDef *def); + void parseDeclareInterface(); + void parseDeclareMetatype(); + void parseMocInclude(); + void parseSlotInPrivate(ClassDef *def, FunctionDef::Access access); + QByteArray parsePropertyAccessor(); + void parsePrivateProperty(ClassDef *def, PropertyMode mode); + + void parseFunctionArguments(FunctionDef *def); + + QByteArray lexemUntil(Token); + bool until(Token); + + // test for Q_INVOCABLE, Q_SCRIPTABLE, etc. and set the flags + // in FunctionDef accordingly + bool testFunctionAttribute(FunctionDef *def); + bool testFunctionAttribute(Token tok, FunctionDef *def); + bool testFunctionRevision(FunctionDef *def); + QTypeRevision parseRevision(); + + bool skipCxxAttributes(); + + void checkSuperClasses(ClassDef *def); + void checkProperties(ClassDef* cdef); + bool testForFunctionModifiers(FunctionDef *def); + + void checkListSizes(const ClassDef &def); }; +#endif // -- QtScxml inline QByteArray noRef(const QByteArray &type) { if (type.endsWith('&')) { if (type.endsWith("&&")) - return type.left(type.length()-2); - return type.left(type.length()-1); + return type.left(type.size()-2); + return type.left(type.size()-1); } return type; } diff --git a/tools/qscxmlc/moc_patches/generator.cpp.patch b/tools/qscxmlc/moc_patches/generator.cpp.patch new file mode 100644 index 0000000..26906d8 --- /dev/null +++ b/tools/qscxmlc/moc_patches/generator.cpp.patch @@ -0,0 +1,286 @@ +--- .upstream/generator.cpp 2024-02-01 11:08:00.055494626 +0100 ++++ generator.cpp 2024-02-05 14:18:32.229391845 +0100 +@@ -4,7 +4,9 @@ + // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + + #include "generator.h" ++#if 0 // -- QtScxml + #include "cbordevice.h" ++#endif // -- QtScxml + #include "outputrevision.h" + #include "utils.h" + #include <QtCore/qmetatype.h> +@@ -25,6 +27,29 @@ + + using namespace QtMiscUtils; + ++// -- QtScxml ++void fprintf(QIODevice &out, const char *fmt, ...) ++{ ++ va_list argp; ++ va_start(argp, fmt); ++ const int bufSize = 4096; ++ char buf[bufSize]; ++ vsnprintf(buf, bufSize, fmt, argp); ++ va_end(argp); ++ out.write(buf); ++} ++ ++void fputc(char c, QIODevice &out) ++{ ++ out.write(&c, 1); ++} ++ ++void fputs(const char *s, QIODevice &out) ++{ ++ out.write(s); ++} ++// -- QtScxml ++ + uint nameToBuiltinType(const QByteArray &name) + { + if (name.isEmpty()) +@@ -57,22 +82,25 @@ + return nullptr; + } + +- Generator::Generator(Moc *moc, ClassDef *classDef, const QList<QByteArray> &metaTypes, +- const QHash<QByteArray, QByteArray> &knownQObjectClasses, +- const QHash<QByteArray, QByteArray> &knownGadgets, FILE *outfile, +- bool requireCompleteTypes) +- : parser(moc), +- out(outfile), +- cdef(classDef), +- metaTypes(metaTypes), +- knownQObjectClasses(knownQObjectClasses), +- knownGadgets(knownGadgets), +- requireCompleteTypes(requireCompleteTypes) +- { +- if (cdef->superclassList.size()) +- purestSuperClass = cdef->superclassList.constFirst().classname; ++// -- QtScxml ++Generator::Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes, ++ const QHash<QByteArray, QByteArray> &knownQObjectClasses, ++ const QHash<QByteArray, QByteArray> &knownGadgets, ++ QIODevice &outfile, ++ bool requireCompleteTypes) ++ : out(outfile), ++ cdef(classDef), ++ metaTypes(metaTypes), ++ knownQObjectClasses(knownQObjectClasses), ++ knownGadgets(knownGadgets), ++ requireCompleteTypes(requireCompleteTypes) ++{ ++ if (cdef->superclassList.size()) ++ purestSuperClass = cdef->superclassList.constFirst().classname; + } ++// -- QtScxml + ++#if 0 // -- QtScxml + static inline qsizetype lengthOfEscapeSequence(const QByteArray &s, qsizetype i) + { + if (s.at(i) != '\\' || i >= s.size() - 1) +@@ -98,7 +126,7 @@ + + // Prints \a s to \a out, breaking it into lines of at most ColumnWidth. The + // opening and closing quotes are NOT included (it's up to the caller). +-static void printStringWithIndentation(FILE *out, const QByteArray &s) ++static void printStringWithIndentation(QIODevice &out, const QByteArray &s) // -- QtScxml + { + static constexpr int ColumnWidth = 72; + const qsizetype len = s.size(); +@@ -116,6 +144,7 @@ + idx += spanLen; + } while (idx < len); + } ++#endif // -- QtSxcml + + void Generator::strreg(const QByteArray &s) + { +@@ -270,12 +299,21 @@ + qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData()); + { + char comma = 0; +- for (const QByteArray &str : strings) { ++// -- QtScxml ++ for (qsizetype i = 0, end = strings.size(); i < end; ++i) { + if (comma) + fputc(comma, out); +- printStringWithIndentation(out, str); ++ fprintf(out, "\n {"); ++ const QByteArray s = strings.at(i); ++ const qsizetype len = s.size(); ++ for (qsizetype charPos = 0; charPos < len; ++charPos) ++ fprintf(out, "char(0x%.2x),", static_cast<quint8>(s.at(charPos))); ++ const bool isLast = (i == end - 1); ++ fprintf(out, "char(0)%s // %d: %s", isLast ? "}" : "},", i, s.constData()); + comma = ','; + } ++// -- QtScxml ++ + } + fprintf(out, "\n);\n" + "#else // !QT_MOC_HAS_STRINGDATA\n"); +@@ -298,8 +336,10 @@ + qsizetype methodCount = 0; + if (qAddOverflow(cdef->signalList.size(), cdef->slotList.size(), &methodCount) + || qAddOverflow(cdef->methodList.size(), methodCount, &methodCount)) { +- parser->error("internal limit exceeded: the total number of member functions" ++// -- QtScxml ++ qFatal("internal limit exceeded: the total number of member functions" + " (including signals and slots) is too big."); ++// -- QtScxml + } + + fprintf(out, " %4" PRIdQSIZETYPE ", %4d, // methods\n", methodCount, methodCount ? index : 0); +@@ -346,7 +386,9 @@ + if (qAddOverflow(cdef->propertyList.size(), cdef->enumList.size(), &propEnumCount) + || qAddOverflow(propEnumCount, qsizetype(1), &propEnumCount) + || propEnumCount >= std::numeric_limits<int>::max()) { +- parser->error("internal limit exceeded: number of property and enum metatypes is too big."); ++// -- QtScxml ++ qFatal("internal limit exceeded: number of property and enum metatypes is too big."); ++// -- QtScxml + } + int initialMetaTypeOffset = int(propEnumCount); + +@@ -585,7 +627,7 @@ + fprintf(out, "\nvoid *%s::qt_metacast(const char *_clname)\n{\n", cdef->qualified.constData()); + fprintf(out, " if (!_clname) return nullptr;\n"); + fprintf(out, " if (!strcmp(_clname, qt_meta_stringdata_%s.stringdata0))\n" +- " return static_cast<void*>(this);\n", ++ " return static_cast<void*>(this);\n", + qualifiedClassNameIdentifier.constData()); + + // for all superclasses but the first one +@@ -631,7 +673,9 @@ + // + // Generate plugin meta data + // ++#if 0 // -- QtScxml + generatePluginMetaData(); ++#endif // -- QtScxml + + // + // Generate function to make sure the non-class signals exist in the parent classes +@@ -1100,6 +1144,13 @@ + const FunctionDef &f = methodList.at(methodindex); + Q_ASSERT(!f.normalizedType.isEmpty()); + fprintf(out, " case %d: ", methodindex); ++ // -- QtScxml ++ if (f.implementation) { ++ fprintf(out, f.implementation, "_o", methodindex); ++ fprintf(out, " break;\n"); ++ continue; ++ } ++ // -- QtScxml + if (f.normalizedType != "void") + fprintf(out, "{ %s _r = ", noRef(f.normalizedType).constData()); + fprintf(out, "_t->"); +@@ -1178,6 +1229,10 @@ + const FunctionDef &f = cdef->signalList.at(methodindex); + if (f.wasCloned || !f.inPrivateClass.isEmpty() || f.isStatic) + continue; ++ // -- QtScxml ++ if (f.mangledName.isEmpty()) ++ continue; ++ // -- QtScxml + anythingUsed = true; + fprintf(out, " {\n"); + fprintf(out, " using _t = %s (%s::*)(",f.type.rawName.constData() , cdef->classname.constData()); +@@ -1200,7 +1255,7 @@ + else + fprintf(out, ");\n"); + fprintf(out, " if (_t _q_method = &%s::%s; *reinterpret_cast<_t *>(_a[1]) == _q_method) {\n", +- cdef->classname.constData(), f.name.constData()); ++ cdef->classname.constData(), f.mangledName.constData()); // -- QtScxml + fprintf(out, " *result = %d;\n", methodindex); + fprintf(out, " return;\n"); + fprintf(out, " }\n }\n"); +@@ -1295,8 +1350,11 @@ + fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s().value(); break;\n", + propindex, p.type.constData(), prefix.constData(), p.bind.constData()); + else if (!p.read.isEmpty()) +- fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s(); break;\n", +- propindex, p.type.constData(), prefix.constData(), p.read.constData()); ++ // -- QtScxml ++ fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s%s; break;\n", ++ propindex, p.type.constData(), prefix.constData(), p.read.constData(), ++ p.read.endsWith(')') ? "" : "()"); ++ // -- QtScxml + else + fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s; break;\n", + propindex, p.type.constData(), prefix.constData(), p.member.constData()); +@@ -1428,6 +1486,10 @@ + { + if (def->wasCloned || def->isAbstract) + return; ++// -- QtScxml ++ if (def->implementation) ++ return; ++// -- QtScxml + fprintf(out, "\n// SIGNAL %d\n%s %s::%s(", + index, def->type.name.constData(), cdef->qualified.constData(), def->name.constData()); + +@@ -1476,10 +1538,8 @@ + if (def->normalizedType == "void") { + fprintf(out, "nullptr"); + } else { +- if (def->returnTypeIsVolatile) +- fprintf(out, "const_cast<void*>(reinterpret_cast<const volatile void*>(std::addressof(_t0)))"); +- else +- fprintf(out, "const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t0)))"); ++ // -- QtScxml removed unused returnTypeIsVolatile ++ fprintf(out, "const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t0)))"); + } + int i; + for (i = 1; i < offset; ++i) +@@ -1494,6 +1554,36 @@ + fprintf(out, "}\n"); + } + ++// -- QtScxml ++void Generator::generateAccessorDefs() ++{ ++ for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { ++ const PropertyDef &p = cdef->propertyList.at(propindex); ++ if (p.read.isEmpty() || p.mangledName.isEmpty()) ++ continue; ++ ++ fprintf(out, "bool %s::%s() const\n{\n return %s;\n}\n\n", cdef->classname.constData(), ++ p.mangledName.constData(), p.read.constData()); ++ } ++} ++ ++void Generator::generateSignalDefs() ++{ ++ for (int methodindex = 0; methodindex < cdef->signalList.size(); ++methodindex) { ++ const FunctionDef &f = cdef->signalList.at(methodindex); ++ if (!f.implementation || f.mangledName.isEmpty()) ++ continue; ++ ++ fprintf(out, "void %s::%s(bool _t1)\n{\n", cdef->classname.constData(), ++ f.mangledName.constData()); ++ fprintf(out, " void *_a[] = { nullptr, " ++ "const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };\n "); ++ fprintf(out, f.implementation, "this", methodindex); ++ fprintf(out, "\n}\n\n"); ++ } ++} ++ ++#if 0 + static CborError jsonValueToCbor(CborEncoder *parent, const QJsonValue &v); + static CborError jsonObjectToCbor(CborEncoder *parent, const QJsonObject &o) + { +@@ -1629,7 +1719,11 @@ + + #define CBOR_ENCODER_WRITER_CONTROL 1 + #define CBOR_ENCODER_WRITE_FUNCTION CborDevice::callback ++#endif ++// -- QtScxml + + QT_END_NAMESPACE + ++#if 0 // -- QtScxml + #include "cborencoder.c" ++#endif // -- QtScxml diff --git a/tools/qscxmlc/moc_patches/generator.h.patch b/tools/qscxmlc/moc_patches/generator.h.patch new file mode 100644 index 0000000..4fc3325 --- /dev/null +++ b/tools/qscxmlc/moc_patches/generator.h.patch @@ -0,0 +1,51 @@ +--- .upstream/generator.h 2023-06-22 20:40:43.078529554 +0200 ++++ generator.h 2024-02-05 14:06:04.258179059 +0100 +@@ -6,23 +6,34 @@ + + #include "moc.h" + ++// -- QtScxml ++#include <QtCore/qhash.h> ++#include <QtCore/qlist.h> ++#include <QtCore/qiodevice.h> ++// -- QtScxml ++ + QT_BEGIN_NAMESPACE + + class Generator + { +- Moc *parser = nullptr; +- FILE *out; ++ QIODevice &out; // -- QtScxml + ClassDef *cdef; + QList<uint> meta_data; + + public: +- Generator(Moc *moc, ClassDef *classDef, const QList<QByteArray> &metaTypes, ++ Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes, + const QHash<QByteArray, QByteArray> &knownQObjectClasses, +- const QHash<QByteArray, QByteArray> &knownGadgets, FILE *outfile = nullptr, ++ const QHash<QByteArray, QByteArray> &knownGadgets, ++ QIODevice &outfile, // -- QtScxml + bool requireCompleteTypes = false); + void generateCode(); + qsizetype registeredStringsCount() { return strings.size(); }; + ++// -- QtScxml ++ void generateAccessorDefs(); ++ void generateSignalDefs(); ++// -- QtScxml ++ + private: + bool registerableMetaType(const QByteArray &propertyType); + void registerClassInfoStrings(); +@@ -41,7 +52,9 @@ + void generateMetacall(); + void generateStaticMetacall(); + void generateSignal(const FunctionDef *def, int index); ++#if 0 // -- QtScxml + void generatePluginMetaData(); ++#endif // -- QtScxml + QMultiMap<QByteArray, int> automaticPropertyMetaTypesHelper(); + QMap<int, QMultiMap<QByteArray, int>> + methodsWithAutomaticTypesHelper(const QList<FunctionDef> &methodList); diff --git a/tools/qscxmlc/moc_patches/moc.cpp.patch b/tools/qscxmlc/moc_patches/moc.cpp.patch new file mode 100644 index 0000000..7b55c65 --- /dev/null +++ b/tools/qscxmlc/moc_patches/moc.cpp.patch @@ -0,0 +1,29 @@ +--- .upstream/moc.cpp 2024-02-01 11:08:00.055494626 +0100 ++++ moc.cpp 2024-02-05 14:06:04.258179059 +0100 +@@ -12,14 +12,15 @@ + #include <QtCore/qdir.h> + #include <QtCore/qjsondocument.h> + +-// for normalizeTypeInternal +-#include <private/qmetaobject_moc_p.h> +-#include <private/qduplicatetracker_p.h> ++// -- QtScxml ++#include <QtCore/qjsonobject.h> ++// -- QtScxml + + QT_BEGIN_NAMESPACE + + using namespace Qt::StringLiterals; + ++#if 0 // -- QtScxml + // only moc needs this function + static QByteArray normalizeType(const QByteArray &ba) + { +@@ -1997,6 +1998,7 @@ + } + } + } ++#endif // -- QtScxml + + QJsonObject ClassDef::toJson() const + { diff --git a/tools/qscxmlc/moc_patches/moc.h.patch b/tools/qscxmlc/moc_patches/moc.h.patch new file mode 100644 index 0000000..dcdac6b --- /dev/null +++ b/tools/qscxmlc/moc_patches/moc.h.patch @@ -0,0 +1,94 @@ +--- .upstream/moc.h 2024-02-05 14:10:55.937714571 +0100 ++++ moc.h 2024-02-05 14:12:52.133597464 +0100 +@@ -4,14 +4,12 @@ + #ifndef MOC_H + #define MOC_H + +-#include "parser.h" +-#include <qstringlist.h> +-#include <qmap.h> +-#include <qjsondocument.h> +-#include <qjsonarray.h> +-#include <qjsonobject.h> +-#include <qtyperevision.h> +-#include <stdio.h> ++// -- QtScxml ++#include <QtCore/qmap.h> ++#include <QtCore/qpair.h> ++#include <QtCore/qjsondocument.h> ++#include <QtCore/qjsonarray.h> ++// -- QtScxml + + #include <private/qtools_p.h> + +@@ -23,16 +21,18 @@ + { + enum ReferenceType { NoReference, Reference, RValueReference, Pointer }; + +- inline Type() : isVolatile(false), isScoped(false), firstToken(NOTOKEN), referenceType(NoReference) {} ++ inline Type() : isVolatile(false), isScoped(false), /* firstToken(NOTOKEN) -- QtScxml ,*/ referenceType(NoReference) {} + inline explicit Type(const QByteArray &_name) +- : name(_name), rawName(name), isVolatile(false), isScoped(false), firstToken(NOTOKEN), referenceType(NoReference) {} ++ : name(_name), rawName(name), isVolatile(false), isScoped(false), /* firstToken(NOTOKEN) -- QtScxml ,*/ referenceType(NoReference) {} + QByteArray name; + //When used as a return type, the type name may be modified to remove the references. + // rawName is the type as found in the function signature + QByteArray rawName; + uint isVolatile : 1; + uint isScoped : 1; ++#if 0 // -- QtScxml + Token firstToken; ++#endif // -- QtScxml + ReferenceType referenceType; + }; + Q_DECLARE_TYPEINFO(Type, Q_RELOCATABLE_TYPE); +@@ -82,8 +82,9 @@ + bool inlineCode = false; + bool wasCloned = false; + ++#if 0 // -- QtScxml + bool returnTypeIsVolatile = false; +- ++#endif // -- QtScxml + bool isCompat = false; + bool isInvokable = false; + bool isScriptable = false; +@@ -97,6 +98,11 @@ + + QJsonObject toJson() const; + static void accessToJson(QJsonObject *obj, Access acs); ++ ++// -- QtScxml ++ QByteArray mangledName; ++ const char *implementation = nullptr; ++// -- QtScxml + }; + Q_DECLARE_TYPEINFO(FunctionDef, Q_RELOCATABLE_TYPE); + +@@ -124,6 +130,10 @@ + qsizetype location = -1; // token index, used for error reporting + + QJsonObject toJson() const; ++ ++// -- QtScxml ++ QByteArray mangledName; ++// -- QtScxml + }; + Q_DECLARE_TYPEINFO(PropertyDef, Q_RELOCATABLE_TYPE); + +@@ -204,6 +214,7 @@ + }; + Q_DECLARE_TYPEINFO(NamespaceDef, Q_RELOCATABLE_TYPE); + ++#if 0 // -- QtScxml + class Moc : public Parser + { + public: +@@ -293,6 +304,7 @@ + + void checkListSizes(const ClassDef &def); + }; ++#endif // -- QtScxml + + inline QByteArray noRef(const QByteArray &type) + { diff --git a/tools/qscxmlc/moc_patches/outputrevision.h.patch b/tools/qscxmlc/moc_patches/outputrevision.h.patch new file mode 100644 index 0000000..be3c2eb --- /dev/null +++ b/tools/qscxmlc/moc_patches/outputrevision.h.patch @@ -0,0 +1,16 @@ +--- .upstream/outputrevision.h 2022-05-23 08:46:14.490945334 +0200 ++++ outputrevision.h 2024-02-05 14:06:04.254179068 +0100 +@@ -4,7 +4,13 @@ + #ifndef OUTPUTREVISION_H + #define OUTPUTREVISION_H + ++#include <QtCore/qglobal.h> // -- QtScxml ++ ++QT_BEGIN_NAMESPACE // -- QtScxml ++ + // if the output revision changes, you MUST change it in qobjectdefs.h too + enum { mocOutputRevision = 68 }; // moc format output revision + ++QT_END_NAMESPACE // -- QtScxml ++ + #endif // OUTPUTREVISION_H diff --git a/tools/qscxmlc/moc_patches/update_moc.sh b/tools/qscxmlc/moc_patches/update_moc.sh new file mode 100755 index 0000000..410c9da --- /dev/null +++ b/tools/qscxmlc/moc_patches/update_moc.sh @@ -0,0 +1,94 @@ +#!/bin/bash +# Copyright (C) 2021 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +usage() { + cat <<EOF >&2 + +Usage: update_moc [gu] [moc directory] + +The moc directory should be qtbase's moc source directory +(absolute or relative). The script must be run from the directory +containing the moc files (moc.h et. al). + +The script has two modes: + +'g' Generates updated patches of the current qtscxml-specific moc + changes. Use this when the qtscxml-specific parts in moc files + have changed and the patches should be updated accordingly. + +'u' Updates the moc from the upstream qtbase moc. + This mode gets the applicable qtbase moc files from the provided + directory, and updates their qtscxml copies by applying patches to them. + If you have not modified qtscxml moc code and just want to update the moc + code from upstream, this is the one you should use. + +Examples: +moc_patches/update_moc g ../../../qtbase/src/tools/moc +moc_patches/update_moc u ../../../qtbase/src/tools/moc + +EOF + die "$@" +} + +checkFile () { + for f + do [[ -f "$f" ]] || die "Error: file \"$f\" does not exist." + done +} + +warn () { echo "$@" >&2; } +die () { + [[ -h .upstream ]] && rm .upstream + warn "$@" + exit 1 +} + +generate_patch() { + echo Generating patches recording how qscxmlc moc differs from upstream. + + # Link the upstream moc files to create a patch file with filepaths + # that are independent of the actual used upstream moc location. + ln -s "$MOC_DIR" .upstream + + for file in "${FILES[@]}" + do + checkFile "$file" ".upstream/$file" + diff -u ".upstream/$file" "$file" > "moc_patches/$file.patch" + echo Generated "moc_patches/$file.patch" + done + # tidy up + rm .upstream +} + +update_moc() { + echo Updating qscxmlc moc from upstream by applying saved patches. + + for file in "${FILES[@]}" + do + checkFile "moc_patches/$file.patch" "$MOC_DIR/$file" + echo Patching file: "$file" with "moc_patches/$file.patch" + # overwrite the current file from upstream + cp "$MOC_DIR/$file" "$file" + if patch "$file" "moc_patches/$file.patch" + then echo Patched "$file" + else warn "Please hand-patch $file; see $file.orig and $file.rej and tidy them away when you are done." + fi + done +} + +MODE="$1" +MOC_DIR="$2" +FILES=( "outputrevision.h" "moc.cpp" "moc.h" "generator.h" "generator.cpp" ) + +[[ -f moc_patches/update_moc.sh ]] || usage "Error: script must be run from the tools/qscxmlc/ directory." +[[ -n "$MOC_DIR" ]] || usage "Error: You did not specify a moc directory." +[[ -d "$MOC_DIR" ]] || usage "Error: moc directory \"$MOC_DIR\" does not exist." + +case "$MODE" in + g) generate_patch ;; + u) update_moc ;; + *) usage "Error: mode \"$MODE\" is not recognized." ;; +esac + +echo Done diff --git a/tools/qscxmlc/outputrevision.h b/tools/qscxmlc/outputrevision.h index 89a0b6e..c170203 100644 --- a/tools/qscxmlc/outputrevision.h +++ b/tools/qscxmlc/outputrevision.h @@ -1,41 +1,16 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtScxml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef OUTPUTREVISION_H #define OUTPUTREVISION_H -#include <QtCore/qglobal.h> +#include <QtCore/qglobal.h> // -- QtScxml -QT_BEGIN_NAMESPACE +QT_BEGIN_NAMESPACE // -- QtScxml // if the output revision changes, you MUST change it in qobjectdefs.h too -enum { mocOutputRevision = 67 }; // moc format output revision +enum { mocOutputRevision = 68 }; // moc format output revision -QT_END_NAMESPACE +QT_END_NAMESPACE // -- QtScxml #endif // OUTPUTREVISION_H diff --git a/tools/qscxmlc/qscxmlc.cpp b/tools/qscxmlc/qscxmlc.cpp index d83e786..fdf89a1 100644 --- a/tools/qscxmlc/qscxmlc.cpp +++ b/tools/qscxmlc/qscxmlc.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtScxml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include <QtScxml/private/qscxmlcompiler_p.h> #include <QtScxml/qscxmltabledata.h> @@ -35,7 +10,7 @@ #include <QCommandLineParser> #include <QFile> #include <QFileInfo> -#include <QTextCodec> +#include <QStringConverter> QT_BEGIN_NAMESPACE @@ -68,17 +43,17 @@ int write(TranslationUnit *tu) } // Make sure it outputs UTF-8, as that is what C++ expects. - QTextCodec *utf8 = QTextCodec::codecForName("UTF-8"); + auto utf8 = QStringConverter::encodingForName("UTF-8"); if (!utf8) { - errs << QStringLiteral("Error: cannot find a QTextCodec for generating UTF-8."); + errs << QStringLiteral("Error: cannot find a QStringConverter for generating UTF-8."); return NoTextCodecError; } QTextStream h(&outH); - h.setCodec(utf8); + h.setEncoding(utf8.value()); h.setGenerateByteOrderMark(true); QTextStream c(&outCpp); - c.setCodec(utf8); + c.setEncoding(utf8.value()); c.setGenerateByteOrderMark(true); CppDumper dumper(h, c); dumper.dump(tu); @@ -93,7 +68,7 @@ static void collectAllDocuments(DocumentModel::ScxmlDocument *doc, QList<DocumentModel::ScxmlDocument *> *docs) { docs->append(doc); - for (DocumentModel::ScxmlDocument *subDoc : qAsConst(doc->allSubDocuments)) + for (DocumentModel::ScxmlDocument *subDoc : std::as_const(doc->allSubDocuments)) collectAllDocuments(subDoc, docs); } @@ -138,12 +113,12 @@ int run(const QStringList &arguments) const QStringList inputFiles = cmdParser.positionalArguments(); - if (inputFiles.count() < 1) { + if (inputFiles.size() < 1) { errs << QCoreApplication::translate("main", "Error: no input file.") << Qt::endl; cmdParser.showHelp(NoInputFilesError); } - if (inputFiles.count() > 1) { + if (inputFiles.size() > 1) { errs << QCoreApplication::translate("main", "Error: unexpected argument(s): %1") .arg(inputFiles.mid(1).join(QLatin1Char(' '))) << Qt::endl; cmdParser.showHelp(NoInputFilesError); @@ -217,7 +192,7 @@ int run(const QStringList &arguments) docs.pop_front(); - for (DocumentModel::ScxmlDocument *doc : qAsConst(docs)) { + for (DocumentModel::ScxmlDocument *doc : std::as_const(docs)) { auto name = doc->root->name; auto prefix = name; if (name.isEmpty()) { diff --git a/tools/qscxmlc/qscxmlc.h b/tools/qscxmlc/qscxmlc.h index 81c2427..160ad76 100644 --- a/tools/qscxmlc/qscxmlc.h +++ b/tools/qscxmlc/qscxmlc.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtScxml 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef QSCXMLC_H #define QSCXMLC_H @@ -43,7 +7,6 @@ #include <QtCore/qglobal.h> QT_BEGIN_NAMESPACE -class QStringList; int run(const QStringList &arguments); diff --git a/tools/qscxmlc/qscxmlc.pri b/tools/qscxmlc/qscxmlc.pri deleted file mode 100644 index 4c7c991..0000000 --- a/tools/qscxmlc/qscxmlc.pri +++ /dev/null @@ -1,32 +0,0 @@ -DEFINES += BUILD_QSCXMLC - -SOURCES += \ - $$PWD/generator.cpp \ - $$PWD/qscxmlc.cpp \ - $$PWD/scxmlcppdumper.cpp - -HEADERS += \ - $$PWD/moc.h \ - $$PWD/generator.h \ - $$PWD/outputrevision.h \ - $$PWD/qscxmlc.h \ - $$PWD/utils.h \ - $$PWD/scxmlcppdumper.h - -HEADERS += \ - $$PWD/../../src/scxml/qscxmlcompiler.h \ - $$PWD/../../src/scxml/qscxmlcompiler_p.h \ - $$PWD/../../src/scxml/qscxmlglobals.h \ - $$PWD/../../src/scxml/qscxmlexecutablecontent.h \ - $$PWD/../../src/scxml/qscxmlexecutablecontent_p.h \ - $$PWD/../../src/scxml/qscxmlerror.h \ - $$PWD/../../src/scxml/qscxmltabledata.h - -SOURCES += \ - $$PWD/../../src/scxml/qscxmlcompiler.cpp \ - $$PWD/../../src/scxml/qscxmlexecutablecontent.cpp \ - $$PWD/../../src/scxml/qscxmlerror.cpp \ - $$PWD/../../src/scxml/qscxmltabledata.cpp - -DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII -INCLUDEPATH *= $$QT.scxml.includes $$QT.scxml_private.includes diff --git a/tools/qscxmlc/qscxmlc.pro b/tools/qscxmlc/qscxmlc.pro deleted file mode 100644 index 7620d3d..0000000 --- a/tools/qscxmlc/qscxmlc.pro +++ /dev/null @@ -1,16 +0,0 @@ -option(host_build) - -QT = core-private - -include(qscxmlc.pri) - -TARGET = qscxmlc -CONFIG += console c++11 - -SOURCES += \ - main.cpp - -load(qt_tool) -load(resources) - -RESOURCES += templates.qrc diff --git a/tools/qscxmlc/scxmlcppdumper.cpp b/tools/qscxmlc/scxmlcppdumper.cpp index 1aca09e..765498c 100644 --- a/tools/qscxmlc/scxmlcppdumper.cpp +++ b/tools/qscxmlc/scxmlcppdumper.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtScxml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "scxmlcppdumper.h" #include "generator.h" @@ -67,7 +42,7 @@ QString cEscape(const QString &str) { QString res; int lastI = 0; - for (int i = 0; i < str.length(); ++i) { + for (int i = 0; i < str.size(); ++i) { QChar c = str.at(i); if (c < QLatin1Char(' ') || c == QLatin1Char('\\') || c == QLatin1Char('\"')) { res.append(str.mid(lastI, i - lastI)); @@ -109,27 +84,26 @@ static void genTemplate(QTextStream &out, const QString &filename, const Replace qFatal("Unable to open template '%s'", qPrintable(filename)); } Q_ASSERT(file.compressionAlgorithm() == QResource::NoCompression); - QByteArray data; - data = QByteArray::fromRawData(reinterpret_cast<const char *>(file.data()), - int(file.size())); - const QString t = QString::fromLatin1(data); - data.clear(); + const QString data = QString::fromLatin1( + QByteArray::fromRawData(reinterpret_cast<const char *>(file.data()), int(file.size())) + ); + const QStringView t { data }; int start = 0; for (int openIdx = t.indexOf(QStringLiteral("${"), start); openIdx >= 0; openIdx = t.indexOf(QStringLiteral("${"), start)) { - out << t.midRef(start, openIdx - start); + out << t.mid(start, openIdx - start); openIdx += 2; const int closeIdx = t.indexOf(QLatin1Char('}'), openIdx); Q_ASSERT(closeIdx >= openIdx); - QString key = t.mid(openIdx, closeIdx - openIdx); + QString key = t.mid(openIdx, closeIdx - openIdx).toString(); if (!replacements.contains(key)) { qFatal("Replacing '%s' failed: no replacement found", qPrintable(key)); } out << replacements.value(key); start = closeIdx + 1; } - out << t.midRef(start); + out << t.mid(start); } static const char *headerStart = @@ -163,7 +137,7 @@ static void generateList(QString &out, std::function<QString(int)> next) if (i != 0) line += QLatin1Char(','); - if (line.length() + nr.length() + 1 > maxLineLength) { + if (line.size() + nr.size() + 1 > maxLineLength) { out += line + QLatin1Char('\n'); line.clear(); } else if (i != 0) { @@ -267,8 +241,8 @@ void generateTables(const GeneratedTableData &td, Replacements &replacements) return QString(); const int length = strings.at(idx).size(); - const QString str = QStringLiteral("STR_LIT(%1, %2, %3)").arg( - QString::number(idx), QString::number(ucharCount), QString::number(length)); + const QString str = QStringLiteral("%1, %2").arg( + QString::number(ucharCount), QString::number(length)); ucharCount += length + 1; return str; }); @@ -365,8 +339,8 @@ void generateCppDataModelEvaluators(const GeneratedTableData::DataModelInfo &inf int createFactoryId(QStringList &factories, const QString &className, const QString &namespacePrefix, const QScxmlExecutableContent::InvokeInfo &invokeInfo, - const QVector<QScxmlExecutableContent::StringId> &namelist, - const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters) + const QList<QScxmlExecutableContent::StringId> &namelist, + const QList<QScxmlExecutableContent::ParameterInfo> ¶meters) { const int idx = factories.size(); @@ -421,10 +395,10 @@ void CppDumper::dump(TranslationUnit *unit) } QStringList classNames; - QVector<GeneratedTableData> tables; - QVector<GeneratedTableData::MetaDataInfo> metaDataInfos; - QVector<GeneratedTableData::DataModelInfo> dataModelInfos; - QVector<QStringList> factories; + QList<GeneratedTableData> tables; + QList<GeneratedTableData::MetaDataInfo> metaDataInfos; + QList<GeneratedTableData::DataModelInfo> dataModelInfos; + QList<QStringList> factories; auto docs = m_translationUnit->allDocuments; tables.resize(docs.size()); metaDataInfos.resize(tables.size()); @@ -438,8 +412,8 @@ void CppDumper::dump(TranslationUnit *unit) GeneratedTableData::build(doc, &tables[i], metaDataInfo, &dataModelInfos[i], [this, &factories, i, &classnameForDocument, &namespacePrefix]( const QScxmlExecutableContent::InvokeInfo &invokeInfo, - const QVector<QScxmlExecutableContent::StringId> &names, - const QVector<QScxmlExecutableContent::ParameterInfo> ¶meters, + const QList<QScxmlExecutableContent::StringId> &names, + const QList<QScxmlExecutableContent::ParameterInfo> ¶meters, const QSharedPointer<DocumentModel::ScxmlDocument> &content) -> int { QString className; if (invokeInfo.expr == QScxmlExecutableContent::NoEvaluator) { @@ -538,13 +512,13 @@ void CppDumper::writeImplStart() << Qt::endl; QStringList includes; - for (DocumentModel::ScxmlDocument *doc : qAsConst(m_translationUnit->allDocuments)) { + for (DocumentModel::ScxmlDocument *doc : std::as_const(m_translationUnit->allDocuments)) { switch (doc->root->dataModel) { case DocumentModel::Scxml::NullDataModel: includes += l("QScxmlNullDataModel"); break; case DocumentModel::Scxml::JSDataModel: - includes += l("QScxmlEcmaScriptDataModel"); + includes += l("QScxmlDataModel"); break; case DocumentModel::Scxml::CppDataModel: includes += doc->root->cppDataModelHeaderName; @@ -559,8 +533,10 @@ void CppDumper::writeImplStart() cpp << l("#include \"") << headerName << l("\"") << Qt::endl; cpp << Qt::endl << QStringLiteral("#include <qscxmlinvokableservice.h>") << Qt::endl - << QStringLiteral("#include <qscxmltabledata.h>") << Qt::endl; - for (const QString &inc : qAsConst(includes)) { + << QStringLiteral("#include <qscxmltabledata.h>") << Qt::endl + << QStringLiteral("#include <QtCore/qtmochelpers.h>") << Qt::endl; + + for (const QString &inc : std::as_const(includes)) { cpp << l("#include <") << inc << l(">") << Qt::endl; } cpp << Qt::endl @@ -585,8 +561,11 @@ void CppDumper::writeImplBody(const GeneratedTableData &table, dataModelInitialization = l("stateMachine.setDataModel(&dataModel);"); break; case DocumentModel::Scxml::JSDataModel: - dataModelField = l("QScxmlEcmaScriptDataModel dataModel;"); - dataModelInitialization = l("stateMachine.setDataModel(&dataModel);"); + dataModelField = l("std::unique_ptr<QScxmlDataModel> dataModel;"); + dataModelInitialization = l( + " dataModel.reset(QScxmlDataModel::createScxmlDataModel(QStringLiteral(\"ecmascriptdatamodel\")));\n" + " stateMachine.setDataModel(dataModel.get());\n" + ); break; case DocumentModel::Scxml::CppDataModel: dataModelField = QStringLiteral("// Data model %1 is set from outside.").arg( @@ -675,7 +654,7 @@ QString CppDumper::mangleIdentifier(const QString &str) } } - for (int ei = str.length(); i != ei; ++i) { + for (int ei = str.size(); i != ei; ++i) { auto c = str.at(i); if ((c >= QLatin1Char('0') && c <= QLatin1Char('9')) || isNonDigit(c)) { mangled += c; @@ -750,7 +729,11 @@ QString CppDumper::generateMetaObject(const QString &className, ClassDef classDef; classDef.classname = className.toUtf8(); classDef.qualified = classDef.classname; - classDef.superclassList << qMakePair(QByteArray("QScxmlStateMachine"), FunctionDef::Public); + classDef.superclassList << SuperClass { + QByteArray("QScxmlStateMachine"), + QByteArray(QT_STRINGIFY(QT_PREPEND_NAMESPACE(QScxmlStateMachine))), + FunctionDef::Public + }; classDef.hasQObject = true; FunctionDef constructor; constructor.name = className.toUtf8(); @@ -795,7 +778,6 @@ QString CppDumper::generateMetaObject(const QString &className, signal.arguments << arg; classDef.signalList << signal; - ++classDef.notifyableProperties; PropertyDef prop; prop.name = stateName.toUtf8(); if (m_translationUnit->stateMethods) diff --git a/tools/qscxmlc/scxmlcppdumper.h b/tools/qscxmlc/scxmlcppdumper.h index 35849c0..a3123b0 100644 --- a/tools/qscxmlc/scxmlcppdumper.h +++ b/tools/qscxmlc/scxmlcppdumper.h @@ -1,36 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtScxml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef SCXMLCPPDUMPER_H #define SCXMLCPPDUMPER_H -#include "qscxmlglobals.h" - +#include <QtScxml/qscxmlglobals.h> #include <QtScxml/private/qscxmlcompiler_p.h> #include <QtScxml/private/qscxmltabledata_p.h> diff --git a/tools/qscxmlc/templates.qrc b/tools/qscxmlc/templates.qrc deleted file mode 100644 index a00f24e..0000000 --- a/tools/qscxmlc/templates.qrc +++ /dev/null @@ -1,7 +0,0 @@ -<RCC> - <qresource prefix="/"> - <file compression-algorithm="none">data.t</file> - <file compression-algorithm="none">decl.t</file> - <file compression-algorithm="none">cppdatamodel.t</file> - </qresource> -</RCC> diff --git a/tools/qscxmlc/utils.h b/tools/qscxmlc/utils.h index 58cf924..358780a 100644 --- a/tools/qscxmlc/utils.h +++ b/tools/qscxmlc/utils.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtScxml 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef UTILS_H #define UTILS_H diff --git a/tools/tools.pro b/tools/tools.pro deleted file mode 100644 index 6884612..0000000 --- a/tools/tools.pro +++ /dev/null @@ -1,2 +0,0 @@ -TEMPLATE = subdirs -qtConfig(commandlineparser): SUBDIRS = qscxmlc |