diff options
author | Erik Verbruggen <erik.verbruggen@theqtcompany.com> | 2016-01-29 12:02:04 +0100 |
---|---|---|
committer | Erik Verbruggen <erik.verbruggen@theqtcompany.com> | 2016-02-05 13:46:13 +0000 |
commit | d9fb4e37573e55d85b1cfbb24c94d48d93350a54 (patch) | |
tree | d61be7734ca364421b9bfd8b0a9b117819b14eb7 | |
parent | 9ada7aa0f5a41312c953ee3c34053edc4cf15df9 (diff) |
Change staticMetaObject generation.
The full meta-object generation is replaced with the moc generator. This
gives a fully functional meta-object without any overhead during object
instantiation.
Change-Id: Ibf62a6f1bfc0873cd00dd2e4bbad38c8d165005a
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com>
-rw-r--r-- | examples/calculator-common/mainwindow.cpp | 11 | ||||
-rw-r--r-- | examples/calculator-common/mainwindow.h | 3 | ||||
-rw-r--r-- | examples/calculator-widgets-static/calculator-widgets-static.cpp | 3 | ||||
-rw-r--r-- | examples/pinball/doc/src/pinball.qdoc | 6 | ||||
-rw-r--r-- | examples/pinball/mainwindow.cpp | 14 | ||||
-rw-r--r-- | examples/pinball/mainwindow.h | 4 | ||||
-rw-r--r-- | examples/pinball/pinball.scxml | 11 | ||||
-rw-r--r-- | mkspecs/features/qscxmlc.prf | 12 | ||||
-rw-r--r-- | src/scxml/qscxmlparser.cpp | 236 | ||||
-rw-r--r-- | src/scxml/qscxmlparser_p.h | 2 | ||||
-rw-r--r-- | tests/auto/scion/scion.pro | 11 | ||||
-rw-r--r-- | tools/qscxmlc/generator.cpp | 1616 | ||||
-rw-r--r-- | tools/qscxmlc/generator.h | 78 | ||||
-rw-r--r-- | tools/qscxmlc/moc.h | 201 | ||||
-rw-r--r-- | tools/qscxmlc/outputrevision.h | 35 | ||||
-rw-r--r-- | tools/qscxmlc/qscxmlc.pro | 4 | ||||
-rw-r--r-- | tools/qscxmlc/scxmlcppdumper.cpp | 272 | ||||
-rw-r--r-- | tools/qscxmlc/utils.h | 121 |
18 files changed, 2434 insertions, 206 deletions
diff --git a/examples/calculator-common/mainwindow.cpp b/examples/calculator-common/mainwindow.cpp index 43a14f3..eacafaa 100644 --- a/examples/calculator-common/mainwindow.cpp +++ b/examples/calculator-common/mainwindow.cpp @@ -115,6 +115,12 @@ MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) : m_machine->submitEvent("C"); }); + connect(m_machine, &QScxmlStateMachine::eventOccurred, [this](const QScxmlEvent &event) { + if (event.name() == QLatin1String("updateDisplay")) { + const QString display = event.data().toMap().value("display").toString(); + ui->display->setText(display); + } + }); } MainWindow::~MainWindow() @@ -122,8 +128,3 @@ MainWindow::~MainWindow() delete ui; } -void MainWindow::updateDisplay(const QVariant &data) -{ - QString display = data.toMap().value("display").toString(); - ui->display->setText(display); -} diff --git a/examples/calculator-common/mainwindow.h b/examples/calculator-common/mainwindow.h index fce3c64..c8d77f3 100644 --- a/examples/calculator-common/mainwindow.h +++ b/examples/calculator-common/mainwindow.h @@ -69,9 +69,6 @@ public: explicit MainWindow(QScxmlStateMachine *machine, QWidget *parent = 0); ~MainWindow(); -public slots: - void updateDisplay(const QVariant &data); - private: QT_PREPEND_NAMESPACE(Ui::MainWindow) *ui; QScxmlStateMachine *m_machine; diff --git a/examples/calculator-widgets-static/calculator-widgets-static.cpp b/examples/calculator-widgets-static/calculator-widgets-static.cpp index 328f78a..94bb4a3 100644 --- a/examples/calculator-widgets-static/calculator-widgets-static.cpp +++ b/examples/calculator-widgets-static/calculator-widgets-static.cpp @@ -61,9 +61,6 @@ int main(int argc, char **argv) machine.init(); MainWindow mainWindow(&machine); - QObject::connect(&machine, &Calculator::event_updateDisplay, - &mainWindow, &MainWindow::updateDisplay); - machine.start(); mainWindow.show(); return app.exec(); diff --git a/examples/pinball/doc/src/pinball.qdoc b/examples/pinball/doc/src/pinball.qdoc index 6b52bbf..f67b66d 100644 --- a/examples/pinball/doc/src/pinball.qdoc +++ b/examples/pinball/doc/src/pinball.qdoc @@ -273,10 +273,10 @@ substate will be set to \c cLetterOff. The \c cLetterOff defines a transition, which will be triggered by - the \c {letterTriggered.C} event. This transition activates the \c cLetterOn, + the \c {cLetterTriggered} event. This transition activates the \c cLetterOn, the other child of \c cLetter, only when the machine is in \c onState (which will be defined later, but in short: when the pinball game is running). - The \c {letterTriggered.C} event is expected to be an event posted into the state machine + The \c {cLetterTriggered} event is expected to be an event posted into the state machine from outside of the state machine. This event should be generated when the ball hits the \uicontrol C letter target. In our example we mimic it by the pressing \uicontrol C letter button. @@ -530,7 +530,7 @@ the widget is disabled. We do that for all lights, targets and description lables. - We also connect to the \c event_updateScore propagated + We also connect to the \c updateScore propagated by the state machine and update the score displays with the values passed with the event. diff --git a/examples/pinball/mainwindow.cpp b/examples/pinball/mainwindow.cpp index d71198d..f4d1383 100644 --- a/examples/pinball/mainwindow.cpp +++ b/examples/pinball/mainwindow.cpp @@ -89,24 +89,24 @@ MainWindow::MainWindow(Pinball *machine, QWidget *parent) : initAndConnect(QLatin1String("onState"), m_ui->ballOutButton); // datamodel update - connect(m_machine, &Pinball::event_updateScore, - this, &MainWindow::updateScore); + connect(m_machine, SIGNAL(updateScore(const QVariant &)), + this, SLOT(updateScore(const QVariant &))); // gui interaction connect(m_ui->cButton, &QAbstractButton::clicked, - [this] { m_machine->submitEvent("letterTriggered.C"); + [this] { m_machine->submitEvent("cLetterTriggered"); }); connect(m_ui->rButton, &QAbstractButton::clicked, - [this] { m_machine->submitEvent("letterTriggered.R"); + [this] { m_machine->submitEvent("rLetterTriggered"); }); connect(m_ui->aButton, &QAbstractButton::clicked, - [this] { m_machine->submitEvent("letterTriggered.A"); + [this] { m_machine->submitEvent("aLetterTriggered"); }); connect(m_ui->zButton, &QAbstractButton::clicked, - [this] { m_machine->submitEvent("letterTriggered.Z"); + [this] { m_machine->submitEvent("zLetterTriggered"); }); connect(m_ui->yButton, &QAbstractButton::clicked, - [this] { m_machine->submitEvent("letterTriggered.Y"); + [this] { m_machine->submitEvent("yLetterTriggered"); }); connect(m_ui->startButton, &QAbstractButton::clicked, [this] { m_machine->submitEvent("startTriggered"); diff --git a/examples/pinball/mainwindow.h b/examples/pinball/mainwindow.h index 3c7b3c3..985f77c 100644 --- a/examples/pinball/mainwindow.h +++ b/examples/pinball/mainwindow.h @@ -69,9 +69,11 @@ public: explicit MainWindow(Pinball *machine, QWidget *parent = 0); ~MainWindow(); +private slots: + void updateScore(const QVariant &data); + private: void initAndConnect(const QString &state, QWidget *widget); - void updateScore(const QVariant &data); QT_PREPEND_NAMESPACE(Ui::MainWindow) *m_ui; Pinball *m_machine; }; diff --git a/examples/pinball/pinball.scxml b/examples/pinball/pinball.scxml index 597c64f..735bca2 100644 --- a/examples/pinball/pinball.scxml +++ b/examples/pinball/pinball.scxml @@ -50,6 +50,7 @@ ** ****************************************************************************/ --> +<!-- enable-qt-mode: yes --> <scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="Pinball" datamodel="ecmascript"> <datamodel> @@ -130,7 +131,7 @@ <parallel id="lettersState"> <state id="cLetter"> <state id="cLetterOff"> - <transition event="letterTriggered.C" cond="In('onState')" target="cLetterOn"/> + <transition event="cLetterTriggered" cond="In('onState')" target="cLetterOn"/> </state> <final id="cLetterOn"> <onentry> @@ -140,7 +141,7 @@ </state> <state id="rLetter"> <state id="rLetterOff"> - <transition event="letterTriggered.R" cond="In('onState')" target="rLetterOn"/> + <transition event="rLetterTriggered" cond="In('onState')" target="rLetterOn"/> </state> <final id="rLetterOn"> <onentry> @@ -150,7 +151,7 @@ </state> <state id="aLetter"> <state id="aLetterOff"> - <transition event="letterTriggered.A" cond="In('onState')" target="aLetterOn"/> + <transition event="aLetterTriggered" cond="In('onState')" target="aLetterOn"/> </state> <final id="aLetterOn"> <onentry> @@ -160,7 +161,7 @@ </state> <state id="zLetter"> <state id="zLetterOff"> - <transition event="letterTriggered.Z" cond="In('onState')" target="zLetterOn"/> + <transition event="zLetterTriggered" cond="In('onState')" target="zLetterOn"/> </state> <final id="zLetterOn"> <onentry> @@ -170,7 +171,7 @@ </state> <state id="yLetter"> <state id="yLetterOff"> - <transition event="letterTriggered.Y" cond="In('onState')" target="yLetterOn"/> + <transition event="yLetterTriggered" cond="In('onState')" target="yLetterOn"/> </state> <final id="yLetterOn"> <onentry> diff --git a/mkspecs/features/qscxmlc.prf b/mkspecs/features/qscxmlc.prf index 54cb7f0..6ed1ad7 100644 --- a/mkspecs/features/qscxmlc.prf +++ b/mkspecs/features/qscxmlc.prf @@ -34,18 +34,6 @@ qscxmlc_sm.CONFIG += target_predeps QMAKE_EXTRA_COMPILERS += qscxmlc_sm } -{ -load(moc) -qscxmlc_moc.name = sc_$$moc_header.name -qscxmlc_moc.input = QSCXMLC_HEADERS -qscxmlc_moc.variable_out = $$moc_header.variable_out -qscxmlc_moc.commands = $$moc_header.commands -qscxmlc_moc.output = $$moc_header.output -qscxmlc_moc.dependency_type = $$moc_header.dependency_type - -QMAKE_EXTRA_COMPILERS += qscxmlc_moc -} - !isEmpty(STATECHARTS) { INCLUDEPATH += $$absolute_path($$QSCXMLC_DIR, $$OUT_PWD) } diff --git a/src/scxml/qscxmlparser.cpp b/src/scxml/qscxmlparser.cpp index 311d92a..6da03d1 100644 --- a/src/scxml/qscxmlparser.cpp +++ b/src/scxml/qscxmlparser.cpp @@ -143,7 +143,7 @@ private: { Q_ASSERT(state->initialStates.isEmpty()); - if (m_doc->qtMode && !isValidCppIdentifier(state->id)) { + if (m_doc->qtMode && !DocumentModel::isValidCppIdentifier(state->id)) { error(state->xmlLocation, QStringLiteral("state id '%1' is not a valid C++ identifier in Qt mode").arg(state->id)); } @@ -224,6 +224,13 @@ private: error(transition->xmlLocation, QStringLiteral("unknown state '%1' in target").arg(target)); } } + foreach (const QString &event, transition->events) { + if (event.contains(QLatin1Char('.'))) { + continue; + } else if (m_doc->qtMode && !DocumentModel::isValidCppIdentifier(event)) { + error(transition->xmlLocation, QStringLiteral("event name '%1' is not a valid C++ identifier in Qt mode").arg(event)); + } + } m_parentNodes.append(transition); return true; @@ -257,7 +264,7 @@ private: bool visit(DocumentModel::Send *node) Q_DECL_OVERRIDE { - if (m_doc->qtMode && node->type == QStringLiteral("qt:signal") && !isValidCppIdentifier(node->event)) { + if (m_doc->qtMode && node->type == QStringLiteral("qt:signal") && !DocumentModel::isValidCppIdentifier(node->event)) { error(node->xmlLocation, QStringLiteral("event name '%1' is not a valid C++ identifier in Qt mode").arg(node->event)); } @@ -345,115 +352,6 @@ private: return Q_NULLPTR; } - static bool isValidCppIdentifier(const QString &str) - { - static const QStringList keywords = QStringList() - << QStringLiteral("alignas") - << QStringLiteral("alignof") - << QStringLiteral("asm") - << QStringLiteral("auto") - << QStringLiteral("bool") - << QStringLiteral("break") - << QStringLiteral("case") - << QStringLiteral("catch") - << QStringLiteral("char") - << QStringLiteral("char16_t") - << QStringLiteral("char32_t") - << QStringLiteral("class") - << QStringLiteral("const") - << QStringLiteral("constexpr") - << QStringLiteral("const_cast") - << QStringLiteral("continue") - << QStringLiteral("decltype") - << QStringLiteral("default") - << QStringLiteral("delete") - << QStringLiteral("double") - << QStringLiteral("do") - << QStringLiteral("dynamic_cast") - << QStringLiteral("else") - << QStringLiteral("enum") - << QStringLiteral("explicit") - << QStringLiteral("export") - << QStringLiteral("extern") - << QStringLiteral("false") - << QStringLiteral("float") - << QStringLiteral("for") - << QStringLiteral("friend") - << QStringLiteral("goto") - << QStringLiteral("if") - << QStringLiteral("inline") - << QStringLiteral("int") - << QStringLiteral("long") - << QStringLiteral("mutable") - << QStringLiteral("namespace") - << QStringLiteral("new") - << QStringLiteral("noexcept") - << QStringLiteral("nullptr") - << QStringLiteral("operator") - << QStringLiteral("private") - << QStringLiteral("protected") - << QStringLiteral("public") - << QStringLiteral("register") - << QStringLiteral("reinterpret_cast") - << QStringLiteral("return") - << QStringLiteral("short") - << QStringLiteral("signed") - << QStringLiteral("sizeof") - << QStringLiteral("static") - << QStringLiteral("static_assert") - << QStringLiteral("static_cast") - << QStringLiteral("struct") - << QStringLiteral("switch") - << QStringLiteral("template") - << QStringLiteral("this") - << QStringLiteral("thread_local") - << QStringLiteral("throw") - << QStringLiteral("true") - << QStringLiteral("try") - << QStringLiteral("typedef") - << QStringLiteral("typeid") - << QStringLiteral("typename") - << QStringLiteral("union") - << QStringLiteral("unsigned") - << QStringLiteral("using") - << QStringLiteral("virtual") - << QStringLiteral("void") - << QStringLiteral("volatile") - << QStringLiteral("wchar_t") - << QStringLiteral("while"); - - if (keywords.contains(str)) { - return false; - } - - auto isNonDigit = [](QChar ch) -> bool { - return (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z')) - || (ch >= QLatin1Char('a') && ch <= QLatin1Char('z')) - || (ch == QLatin1Char('_')); - }; - - QChar ch = str.at(0); - if (!isNonDigit(ch)) { - return false; - } - for (int i = 1, ei = str.size(); i != ei; ++i) { - ch = str.at(i); - if (!isNonDigit(ch) && !ch.isDigit()) { - return false; - } - } - - if (str.startsWith(QLatin1Char('_')) && str.size() > 1) { - QChar ch = str.at(1); - if (ch == QLatin1Char('_') - || (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z'))) { - return false; - } - } - - return true; - } - private: std::function<void (const DocumentModel::XmlLocation &, const QString &)> m_errorHandler; DocumentModel::ScxmlDocument *m_doc; @@ -607,8 +505,9 @@ private: m_firstSubStateMachineSignal = m_eventNamesByIndex.size(); foreach (const QString &machineName, subStateMachineNames) { auto name = machineName.toUtf8(); - QByteArray signalName = name + "Changed()"; + QByteArray signalName = name + "Changed(QScxmlStateMachine *)"; QMetaMethodBuilder signalBuilder = b.addSignal(signalName); + signalBuilder.setParameterNames(init("statemachine")); int idx = signalBuilder.index(); m_eventNamesByIndex.resize(std::max(idx + 1, m_eventNamesByIndex.size())); } @@ -701,7 +600,8 @@ protected: if (m_subStateMachines.at(idx) != machine) { m_subStateMachines[idx] = machine; // emit changed signal: - QMetaObject::activate(this, metaObject(), m_firstSubStateMachineSignal + idx, Q_NULLPTR); + void *argv[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&machine)) }; + QMetaObject::activate(this, metaObject(), m_firstSubStateMachineSignal + idx, argv); } } @@ -1541,6 +1441,115 @@ void DocumentModel::Scxml::accept(DocumentModel::NodeVisitor *visitor) DocumentModel::NodeVisitor::~NodeVisitor() {} +bool DocumentModel::isValidCppIdentifier(const QString &str) +{ + static const QStringList keywords = QStringList() + << QStringLiteral("alignas") + << QStringLiteral("alignof") + << QStringLiteral("asm") + << QStringLiteral("auto") + << QStringLiteral("bool") + << QStringLiteral("break") + << QStringLiteral("case") + << QStringLiteral("catch") + << QStringLiteral("char") + << QStringLiteral("char16_t") + << QStringLiteral("char32_t") + << QStringLiteral("class") + << QStringLiteral("const") + << QStringLiteral("constexpr") + << QStringLiteral("const_cast") + << QStringLiteral("continue") + << QStringLiteral("decltype") + << QStringLiteral("default") + << QStringLiteral("delete") + << QStringLiteral("double") + << QStringLiteral("do") + << QStringLiteral("dynamic_cast") + << QStringLiteral("else") + << QStringLiteral("enum") + << QStringLiteral("explicit") + << QStringLiteral("export") + << QStringLiteral("extern") + << QStringLiteral("false") + << QStringLiteral("float") + << QStringLiteral("for") + << QStringLiteral("friend") + << QStringLiteral("goto") + << QStringLiteral("if") + << QStringLiteral("inline") + << QStringLiteral("int") + << QStringLiteral("long") + << QStringLiteral("mutable") + << QStringLiteral("namespace") + << QStringLiteral("new") + << QStringLiteral("noexcept") + << QStringLiteral("nullptr") + << QStringLiteral("operator") + << QStringLiteral("private") + << QStringLiteral("protected") + << QStringLiteral("public") + << QStringLiteral("register") + << QStringLiteral("reinterpret_cast") + << QStringLiteral("return") + << QStringLiteral("short") + << QStringLiteral("signed") + << QStringLiteral("sizeof") + << QStringLiteral("static") + << QStringLiteral("static_assert") + << QStringLiteral("static_cast") + << QStringLiteral("struct") + << QStringLiteral("switch") + << QStringLiteral("template") + << QStringLiteral("this") + << QStringLiteral("thread_local") + << QStringLiteral("throw") + << QStringLiteral("true") + << QStringLiteral("try") + << QStringLiteral("typedef") + << QStringLiteral("typeid") + << QStringLiteral("typename") + << QStringLiteral("union") + << QStringLiteral("unsigned") + << QStringLiteral("using") + << QStringLiteral("virtual") + << QStringLiteral("void") + << QStringLiteral("volatile") + << QStringLiteral("wchar_t") + << QStringLiteral("while"); + + if (keywords.contains(str)) { + return false; + } + + auto isNonDigit = [](QChar ch) -> bool { + return (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z')) + || (ch >= QLatin1Char('a') && ch <= QLatin1Char('z')) + || (ch == QLatin1Char('_')); + }; + + QChar ch = str.at(0); + if (!isNonDigit(ch)) { + return false; + } + for (int i = 1, ei = str.size(); i != ei; ++i) { + ch = str.at(i); + if (!isNonDigit(ch) && !ch.isDigit()) { + return false; + } + } + + if (str.startsWith(QLatin1Char('_')) && str.size() > 1) { + QChar ch = str.at(1); + if (ch == QLatin1Char('_') + || (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z'))) { + return false; + } + } + + return true; +} + /*! * \class QScxmlParser::Loader * \brief A URI resolver and resource loader for the QScxmlParser . @@ -2005,6 +2014,7 @@ void QScxmlParserPrivate::parse() break; } parseSubDocument(i, m_reader, m_fileName); + i->content->qtMode = m_doc->qtMode; } break; default: addError(QStringLiteral("unexpected parent of content %1").arg(m_stack.last().kind)); diff --git a/src/scxml/qscxmlparser_p.h b/src/scxml/qscxmlparser_p.h index 1ea8276..0b01920 100644 --- a/src/scxml/qscxmlparser_p.h +++ b/src/scxml/qscxmlparser_p.h @@ -545,6 +545,8 @@ public: } }; +Q_SCXML_EXPORT bool isValidCppIdentifier(const QString &str); + } // DocumentModel namespace class Q_SCXML_EXPORT QScxmlParserPrivate diff --git a/tests/auto/scion/scion.pro b/tests/auto/scion/scion.pro index 6b4accf..408c94a 100644 --- a/tests/auto/scion/scion.pro +++ b/tests/auto/scion/scion.pro @@ -47,17 +47,6 @@ myscxml_hdr.depends = scxml/${QMAKE_FUNC_nameTheNamespace}_${QMAKE_FILE_IN_BASE} myscxml_hdr.output = scxml/${QMAKE_FUNC_nameTheNamespace}_${QMAKE_FILE_IN_BASE}.h QMAKE_EXTRA_COMPILERS += myscxml_hdr -qtPrepareTool(QMAKE_MOC, moc) -#scxml_moc_source.CONFIG = no_link -scxml_moc_source.dependency_type = TYPE_C -scxml_moc_source.commands = $$QMAKE_MOC ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} -scxml_moc_source.output = scxml/$${QMAKE_H_MOD_MOC}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_CPP)} -scxml_moc_source.input = SCXML_HEADERS -#scxml_moc_source.depends += $$WIN_INCLUDETEMP -scxml_moc_source.variable_out = SOURCES -silent:scxml_moc_source.commands = @echo moc ${QMAKE_FILE_IN} && $$scxml_moc_source.commands -QMAKE_EXTRA_COMPILERS += scxml_moc_source - SCXMLS_DIR += $$absolute_path($$PWD/../../3rdparty/scion-tests/scxml-test-framework/test) ALLSCXMLS = $$files($$SCXMLS_DIR/*.scxml, true) diff --git a/tools/qscxmlc/generator.cpp b/tools/qscxmlc/generator.cpp new file mode 100644 index 0000000..88688f9 --- /dev/null +++ b/tools/qscxmlc/generator.cpp @@ -0,0 +1,1616 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include "generator.h" +#include "outputrevision.h" +#include "utils.h" +#include <QtCore/qmetatype.h> +#include <QtCore/qjsondocument.h> +#include <QtCore/qjsonobject.h> +#include <QtCore/qjsonvalue.h> +#include <QtCore/qjsonarray.h> +#include <QtCore/qplugin.h> + +#include <private/qmetaobject_p.h> //for the flags. + +QT_BEGIN_NAMESPACE + +void fprintf(QTextStream &out, const char *fmt, ...) +{ + va_list argp; + va_start(argp, fmt); + const int bufSize = 4096; + char buf[bufSize]; + vsnprintf(buf, bufSize, fmt, argp); + out << buf; +} + +void fputc(char c, QTextStream &out) +{ + out << c; +} + +void fputs(const char *s, QTextStream &out) +{ + out << s; +} + +uint nameToBuiltinType(const QByteArray &name) +{ + if (name.isEmpty()) + return 0; + + uint tp = QMetaType::type(name.constData()); + return tp < uint(QMetaType::User) ? tp : uint(QMetaType::UnknownType); +} + +/* + Returns \c true if the type is a built-in type. +*/ +bool isBuiltinType(const QByteArray &type) + { + int id = QMetaType::type(type.constData()); + if (id == QMetaType::UnknownType) + return false; + return (id < QMetaType::User); +} + +static const char *metaTypeEnumValueString(int type) + { +#define RETURN_METATYPENAME_STRING(MetaTypeName, MetaTypeId, RealType) \ + case QMetaType::MetaTypeName: return #MetaTypeName; + + switch (type) { +QT_FOR_EACH_STATIC_TYPE(RETURN_METATYPENAME_STRING) + } +#undef RETURN_METATYPENAME_STRING + return 0; + } + +Generator::Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes, const QHash<QByteArray, QByteArray> &knownQObjectClasses, const QHash<QByteArray, QByteArray> &knownGadgets, QTextStream &outfile) + : out(outfile), cdef(classDef), metaTypes(metaTypes), knownQObjectClasses(knownQObjectClasses) + , knownGadgets(knownGadgets) +{ + if (cdef->superclassList.size()) + purestSuperClass = cdef->superclassList.first().first; +} + +static inline int lengthOfEscapeSequence(const QByteArray &s, int i) +{ + if (s.at(i) != '\\' || i >= s.length() - 1) + return 1; + const int startPos = i; + ++i; + char ch = s.at(i); + if (ch == 'x') { + ++i; + while (i < s.length() && is_hex_char(s.at(i))) + ++i; + } else if (is_octal_char(ch)) { + while (i < startPos + 4 + && i < s.length() + && is_octal_char(s.at(i))) { + ++i; + } + } else { // single character escape sequence + i = qMin(i + 1, s.length()); + } + return i - startPos; +} + +void Generator::strreg(const QByteArray &s) +{ + if (!strings.contains(s)) + strings.append(s); +} + +int Generator::stridx(const QByteArray &s) +{ + int i = strings.indexOf(s); + Q_ASSERT_X(i != -1, Q_FUNC_INFO, "We forgot to register some strings"); + return i; +} + +// Returns the sum of all parameters (including return type) for the given +// \a list of methods. This is needed for calculating the size of the methods' +// parameter type/name meta-data. +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 + return sum; +} + +bool Generator::registerableMetaType(const QByteArray &propertyType) +{ + if (metaTypes.contains(propertyType)) + return true; + + if (propertyType.endsWith('*')) { + QByteArray objectPointerType = propertyType; + // The objects container stores class names, such as 'QState', 'QLabel' etc, + // not 'QState*', 'QLabel*'. The propertyType does contain the '*', so we need + // to chop it to find the class type in the known QObjects list. + objectPointerType.chop(1); + if (knownQObjectClasses.contains(objectPointerType)) + return true; + } + + static const QVector<QByteArray> smartPointers = QVector<QByteArray>() +#define STREAM_SMART_POINTER(SMART_POINTER) << #SMART_POINTER + QT_FOR_EACH_AUTOMATIC_TEMPLATE_SMART_POINTER(STREAM_SMART_POINTER) +#undef STREAM_SMART_POINTER + ; + + foreach (const QByteArray &smartPointer, smartPointers) + if (propertyType.startsWith(smartPointer + "<") && !propertyType.endsWith("&")) + return knownQObjectClasses.contains(propertyType.mid(smartPointer.size() + 1, propertyType.size() - smartPointer.size() - 1 - 1)); + + static const QVector<QByteArray> oneArgTemplates = QVector<QByteArray>() +#define STREAM_1ARG_TEMPLATE(TEMPLATENAME) << #TEMPLATENAME + QT_FOR_EACH_AUTOMATIC_TEMPLATE_1ARG(STREAM_1ARG_TEMPLATE) +#undef STREAM_1ARG_TEMPLATE + ; + foreach (const QByteArray &oneArgTemplateType, oneArgTemplates) + if (propertyType.startsWith(oneArgTemplateType + "<") && propertyType.endsWith(">")) { + const int argumentSize = propertyType.size() - oneArgTemplateType.size() - 1 + // 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); + return isBuiltinType(templateArg) || registerableMetaType(templateArg); + } + return false; +} + +/* returns \c true if name and qualifiedName refers to the same name. + * If qualified name is "A::B::C", it returns \c true for "C", "B::C" or "A::B::C" */ +static bool qualifiedNameEquals(const QByteArray &qualifiedName, const QByteArray &name) +{ + if (qualifiedName == name) + return true; + int index = qualifiedName.indexOf("::"); + if (index == -1) + return false; + return qualifiedNameEquals(qualifiedName.mid(index+2), name); +} + +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); + if (cdef->enumDeclarations.contains(def.name)) { + enumList += def; + } + QByteArray alias = cdef->flagAliases.value(def.name); + if (cdef->enumDeclarations.contains(alias)) { + def.name = alias; + enumList += def; + } + } + cdef->enumList = enumList; + } + +// +// Register all strings used in data section +// + strreg(cdef->qualified); + registerClassInfoStrings(); + registerFunctionStrings(cdef->signalList); + registerFunctionStrings(cdef->slotList); + registerFunctionStrings(cdef->methodList); + registerFunctionStrings(cdef->constructorList); + registerPropertyStrings(); + registerEnumStrings(); + + QByteArray qualifiedClassNameIdentifier = cdef->qualified; + qualifiedClassNameIdentifier.replace(':', '_'); + +// +// Build stringdata struct +// + 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, " char stringdata%d[%d];\n", stringDataCounter++, stringDataLength - thisLength); + stringDataLength = thisLength; + } + } + fprintf(out, " 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", + 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; + } + } + } + fprintf(out, "\n },\n"); + } + +// +// Build stringdata array +// + fprintf(out, " \""); + int col = 0; + int len = 0; + int stringDataLength = 0; + for (int i = 0; i < strings.size(); ++i) { + QByteArray s = strings.at(i); + len = s.length(); + stringDataLength += len + 1; + if (stringDataLength >= constCharArraySizeLimit) { + fprintf(out, "\",\n \""); + stringDataLength = len + 1; + col = 0; + } else if (i) + fputs("\\0", out); // add \0 at the end of each string + + if (col && col + len >= 72) { + fprintf(out, "\"\n \""); + col = 0; + } else if (len && s.at(0) >= '0' && s.at(0) <= '9') { + fprintf(out, "\"\""); + len += 2; + } + int idx = 0; + while (idx < s.length()) { + if (idx > 0) { + col = 0; + fprintf(out, "\"\n \""); + } + int spanLen = qMin(70, s.length() - idx); + // don't cut escape sequences at the end of a line + int backSlashPos = s.lastIndexOf('\\', idx + spanLen - 1); + if (backSlashPos >= idx) { + int escapeLen = lengthOfEscapeSequence(s, backSlashPos); + spanLen = qBound(spanLen, backSlashPos + escapeLen - idx, s.length() - idx); + } + fprintf(out, "%.*s", spanLen, s.constData() + idx); + idx += spanLen; + col += spanLen; + } + col += len + 2; + } + +// Terminate stringdata struct + fprintf(out, "\"\n};\n"); + fprintf(out, "#undef QT_MOC_LITERAL\n\n"); + +// +// build the data array +// + + int index = MetaObjectPrivateFieldCount; + fprintf(out, "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; + + int methodCount = cdef->signalList.count() + cdef->slotList.count() + cdef->methodList.count(); + fprintf(out, " %4d, %4d, // methods\n", methodCount, methodCount ? index : 0); + index += methodCount * 5; + if (cdef->revisionedMethods) + index += methodCount; + int paramsIndex = index; + int totalParameterCount = aggregateParameterCount(cdef->signalList) + + aggregateParameterCount(cdef->slotList) + + aggregateParameterCount(cdef->methodList) + + 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 + + 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); + + 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, + isConstructible ? index : 0); + + int flags = 0; + if (cdef->hasQGadget) { + // 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()); + + +// +// Build classinfo array +// + generateClassInfos(); + +// +// Build signals array first, otherwise the signal indices would be wrong +// + generateFunctions(cdef->signalList, "signal", MethodSignal, paramsIndex); + +// +// Build slots array +// + generateFunctions(cdef->slotList, "slot", MethodSlot, paramsIndex); + +// +// Build method array +// + generateFunctions(cdef->methodList, "method", MethodMethod, paramsIndex); + +// +// Build method version arrays +// + if (cdef->revisionedMethods) { + generateFunctionRevisions(cdef->signalList, "signal"); + generateFunctionRevisions(cdef->slotList, "slot"); + generateFunctionRevisions(cdef->methodList, "method"); + } + +// +// Build method parameters array +// + generateFunctionParameters(cdef->signalList, "signal"); + generateFunctionParameters(cdef->slotList, "slot"); + generateFunctionParameters(cdef->methodList, "method"); + if (isConstructible) + generateFunctionParameters(cdef->constructorList, "constructor"); + +// +// Build property array +// + generateProperties(); + +// +// Build enums array +// + generateEnums(enumsIndex); + +// +// Build constructors array +// + if (isConstructible) + generateFunctions(cdef->constructorList, "constructor", MethodConstructor, paramsIndex); + +// +// Terminate data array +// + 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; + knownExtraMetaObject.unite(knownQObjectClasses); + + for (int i = 0; i < cdef->propertyList.count(); ++i) { + const PropertyDef &p = cdef->propertyList.at(i); + if (isBuiltinType(p.type)) + continue; + + if (p.type.contains('*') || p.type.contains('<') || p.type.contains('>')) + continue; + + int 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; + + QByteArray thisScope = cdef->qualified; + do { + int s = thisScope.lastIndexOf("::"); + thisScope = thisScope.left(s); + QByteArray currentScope = thisScope.isEmpty() ? unqualifiedScope : thisScope + "::" + unqualifiedScope; + scopeIt = knownExtraMetaObject.constFind(currentScope); + } while (!thisScope.isEmpty() && scopeIt == knownExtraMetaObject.constEnd()); + + if (scopeIt == knownExtraMetaObject.constEnd()) + continue; + + const QByteArray &scope = *scopeIt; + + if (scope == "Qt") + continue; + if (qualifiedNameEquals(cdef->qualified, scope)) + continue; + + if (!extraList.contains(scope)) + extraList += scope; + } + + // 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("::"); + if (s > 0) { + QByteArray scope = enumKey.left(s); + if (scope != "Qt" && !qualifiedNameEquals(cdef->qualified, scope) && !extraList.contains(scope)) + extraList += scope; + } + } + + 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_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()); + + if (isQObject) + fprintf(out, " { Q_NULLPTR, "); + else if (cdef->superclassList.size() && (!cdef->hasQGadget || knownGadgets.contains(purestSuperClass))) + fprintf(out, " { &%s::staticMetaObject, ", purestSuperClass.constData()); + else + fprintf(out, " { Q_NULLPTR, "); + fprintf(out, "qt_meta_stringdata_%s.data,\n" + " qt_meta_data_%s, ", qualifiedClassNameIdentifier.constData(), + qualifiedClassNameIdentifier.constData()); + if (hasStaticMetaCall) + fprintf(out, " qt_static_metacall, "); + else + fprintf(out, " Q_NULLPTR, "); + + if (extraList.isEmpty()) + fprintf(out, "Q_NULLPTR, "); + else + fprintf(out, "qt_meta_extradata_%s, ", qualifiedClassNameIdentifier.constData()); + fprintf(out, "Q_NULLPTR}\n};\n\n"); + + if(isQt) + return; + + if (!cdef->hasQObject) + return; + + 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 Q_NULLPTR;\n"); + fprintf(out, " if (!strcmp(_clname, 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()); + } + 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) { + fprintf(out, " if (!strcmp(_clname, %s))\n return ", iface.at(j).interfaceId.constData()); + for (int 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()); + } + } + if (!purestSuperClass.isEmpty() && !isQObject) { + QByteArray superClass = purestSuperClass; + fprintf(out, " return %s::qt_metacast(_clname);\n", superClass.constData()); + } else { + fprintf(out, " return Q_NULLPTR;\n"); + } + fprintf(out, "}\n"); + +// +// Generate internal qt_metacall() function +// + generateMetacall(); + +// +// Generate internal signal functions +// + for (int signalindex = 0; signalindex < cdef->signalList.size(); ++signalindex) + generateSignal(&cdef->signalList[signalindex], signalindex); + +// +// Generate plugin meta data +// +// generatePluginMetaData(); +} + + +void Generator::registerClassInfoStrings() +{ + for (int i = 0; i < cdef->classInfoList.size(); ++i) { + const ClassInfoDef &c = cdef->classInfoList.at(i); + strreg(c.name); + strreg(c.value); + } +} + +void Generator::generateClassInfos() +{ + if (cdef->classInfoList.isEmpty()) + return; + + fprintf(out, "\n // classinfo: key, value\n"); + + for (int i = 0; i < cdef->classInfoList.size(); ++i) { + const ClassInfoDef &c = cdef->classInfoList.at(i); + 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); + + 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); + if (!isBuiltinType(a.normalizedType)) + strreg(a.normalizedType); + strreg(a.name); + } + } +} + +void Generator::generateFunctions(const QList<FunctionDef>& list, const char *functype, int type, int ¶msIndex) +{ + 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); + + QByteArray comment; + unsigned char flags = type; + if (f.access == FunctionDef::Private) { + flags |= AccessPrivate; + comment.append("Private"); + } else if (f.access == FunctionDef::Public) { + flags |= AccessPublic; + comment.append("Public"); + } else if (f.access == FunctionDef::Protected) { + flags |= AccessProtected; + comment.append("Protected"); + } + if (f.isCompat) { + flags |= MethodCompatibility; + comment.append(" | MethodCompatibility"); + } + if (f.wasCloned) { + flags |= MethodCloned; + comment.append(" | MethodCloned"); + } + if (f.isScriptable) { + flags |= MethodScriptable; + comment.append(" | isScriptable"); + } + if (f.revision > 0) { + flags |= MethodRevisioned; + 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()); + + paramsIndex += 1 + argc * 2; + } +} + +void Generator::generateFunctionRevisions(const QList<FunctionDef>& list, const char *functype) +{ + if (list.count()) + fprintf(out, "\n // %ss: revision\n", functype); + for (int i = 0; i < list.count(); ++i) { + const FunctionDef &f = list.at(i); + fprintf(out, " %4d,\n", f.revision); + } +} + +void Generator::generateFunctionParameters(const QList<FunctionDef>& list, const char *functype) +{ + 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); + 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); + fputc(',', out); + } + + // Parameter names + for (int j = 0; j < argsCount; ++j) { + const ArgumentDef &arg = f.arguments.at(j); + fprintf(out, " %4d,", stridx(arg.name)); + } + + fprintf(out, "\n"); + } +} + +void Generator::generateTypeInfo(const QByteArray &typeName, bool allowEmptyName) +{ + Q_UNUSED(allowEmptyName); + if (isBuiltinType(typeName)) { + int type; + const char *valueString; + if (typeName == "qreal") { + type = QMetaType::UnknownType; + valueString = "QReal"; + } else { + type = nameToBuiltinType(typeName); + valueString = metaTypeEnumValueString(type); + } + if (valueString) { + fprintf(out, "QMetaType::%s", valueString); + } else { + Q_ASSERT(type != QMetaType::UnknownType); + fprintf(out, "%4d", type); + } + } else { + Q_ASSERT(!typeName.isEmpty() || allowEmptyName); + fprintf(out, "0x%.8x | %d", IsUnresolvedType, stridx(typeName)); + } +} + +void Generator::registerPropertyStrings() +{ + for (int i = 0; i < cdef->propertyList.count(); ++i) { + const PropertyDef &p = cdef->propertyList.at(i); + strreg(p.name); + if (!isBuiltinType(p.type)) + strreg(p.type); + } +} + +void Generator::generateProperties() +{ + // + // Create meta data + // + + if (cdef->propertyList.count()) + fprintf(out, "\n // properties: name, type, flags\n"); + for (int i = 0; i < cdef->propertyList.count(); ++i) { + const PropertyDef &p = cdef->propertyList.at(i); + uint flags = Invalid; + if (!isBuiltinType(p.type)) + flags |= EnumOrFlag; + if (!p.member.isEmpty() && !p.constant) + flags |= Writable; + if (!p.read.isEmpty() || !p.member.isEmpty()) + flags |= Readable; + if (!p.write.isEmpty()) { + flags |= Writable; + 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") + flags |= Designable; + + if (p.scriptable.isEmpty()) + flags |= ResolveScriptable; + else if (p.scriptable != "false") + flags |= Scriptable; + + if (p.stored.isEmpty()) + flags |= ResolveStored; + else 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") + flags |= User; + + if (p.notifyId != -1) + flags |= Notify; + + if (p.revision > 0) + flags |= Revisioned; + + if (p.constant) + flags |= Constant; + if (p.final) + flags |= Final; + + 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); + } + } +} + +void Generator::registerEnumStrings() +{ + for (int i = 0; i < cdef->enumList.count(); ++i) { + const EnumDef &e = cdef->enumList.at(i); + strreg(e.name); + for (int j = 0; j < e.values.count(); ++j) + strreg(e.values.at(j)); + } +} + +void Generator::generateEnums(int index) +{ + if (cdef->enumDeclarations.isEmpty()) + return; + + fprintf(out, "\n // enums: name, flags, count, data\n"); + index += 4 * cdef->enumList.count(); + int i; + for (i = 0; i < cdef->enumList.count(); ++i) { + const EnumDef &e = cdef->enumList.at(i); + fprintf(out, " %4d, 0x%.1x, %4d, %4d,\n", + stridx(e.name), + cdef->enumDeclarations.value(e.name) ? 1 : 0, + e.values.count(), + index); + index += e.values.count() * 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); + QByteArray code = cdef->qualified.constData(); + if (e.isEnumClass) + code += "::" + e.name; + code += "::" + val; + fprintf(out, " %4d, uint(%s),\n", + stridx(val), code.constData()); + } + } +} + +void Generator::generateMetacall() +{ + bool isQObject = (cdef->classname == "QObject"); + + fprintf(out, "\nint %s::qt_metacall(QMetaObject::Call _c, int _id, void **_a)\n{\n", + cdef->qualified.constData()); + + if (!purestSuperClass.isEmpty() && !isQObject) { + QByteArray superClass = purestSuperClass; + 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; + methodList += cdef->signalList; + methodList += cdef->slotList; + methodList += cdef->methodList; + + if (methodList.size()) { + needElse = true; + fprintf(out, "if (_c == QMetaObject::InvokeMetaMethod) {\n"); + fprintf(out, " if (_id < %d)\n", methodList.size()); + fprintf(out, " qt_static_metacall(this, _c, _id, _a);\n"); + fprintf(out, " _id -= %d;\n }", methodList.size()); + + fprintf(out, " else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {\n"); + fprintf(out, " if (_id < %d)\n", methodList.size()); + + if (methodsWithAutomaticTypesHelper(methodList).isEmpty()) + fprintf(out, " *reinterpret_cast<int*>(_a[0]) = -1;\n"); + else + fprintf(out, " qt_static_metacall(this, _c, _id, _a);\n"); + fprintf(out, " _id -= %d;\n }", 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" + " 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"); + } + if (methodList.size() || cdef->signalList.size() || cdef->propertyList.size()) + fprintf(out, "\n "); + fprintf(out,"return _id;\n}\n"); +} + + +QMultiMap<QByteArray, int> Generator::automaticPropertyMetaTypesHelper() +{ + QMultiMap<QByteArray, int> automaticPropertyMetaTypes; + for (int i = 0; i < cdef->propertyList.size(); ++i) { + const QByteArray propertyType = cdef->propertyList.at(i).type; + if (registerableMetaType(propertyType) && !isBuiltinType(propertyType)) + automaticPropertyMetaTypes.insert(propertyType, i); + } + return automaticPropertyMetaTypes; +} + +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) { + const QByteArray argType = f.arguments.at(j).normalizedType; + if (registerableMetaType(argType) && !isBuiltinType(argType)) + methodsWithAutomaticTypes[i].insert(argType, j); + } + } + return methodsWithAutomaticTypes; +} + +void Generator::generateStaticMetacall() +{ + fprintf(out, "void %s::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)\n{\n", + cdef->qualified.constData()); + + bool needElse = false; + bool isUsed_a = false; + + 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) { + 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()); + } + fprintf(out, ");\n"); + fprintf(out, " if (_a[0]) *reinterpret_cast<%s**>(_a[0]) = _r; } break;\n", + cdef->hasQGadget ? "void" : "QObject"); + } + fprintf(out, " default: break;\n"); + fprintf(out, " }\n"); + fprintf(out, " }"); + needElse = true; + isUsed_a = true; + } + + QList<FunctionDef> methodList; + methodList += cdef->signalList; + methodList += cdef->slotList; + methodList += cdef->methodList; + + if (!methodList.isEmpty()) { + if (needElse) + fprintf(out, " else "); + else + fprintf(out, " "); + fprintf(out, "if (_c == QMetaObject::InvokeMetaMethod) {\n"); + 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"); + 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); + + //---- + if (f.implementation) { + fprintf(out, f.implementation, methodindex); + fprintf(out, " break;\n"); + continue; + } + //---- + + if (f.normalizedType != "void") + fprintf(out, "{ %s _r = ", noRef(f.normalizedType).constData()); + fprintf(out, "_t->"); + if (f.inPrivateClass.size()) + fprintf(out, "%s->", f.inPrivateClass.constData()); + 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()"); + } + fprintf(out, ");"); + if (f.normalizedType != "void") { + fprintf(out, "\n if (_a[0]) *reinterpret_cast< %s*>(_a[0]) = _r; } ", + noRef(f.normalizedType).constData()); + isUsed_a = true; + } + fprintf(out, " break;\n"); + } + fprintf(out, " default: ;\n"); + fprintf(out, " }\n"); + fprintf(out, " }"); + needElse = true; + + QMap<int, QMultiMap<QByteArray, int> > methodsWithAutomaticTypes = methodsWithAutomaticTypesHelper(methodList); + + 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"); + 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"); + foreach (const QByteArray &key, it->uniqueKeys()) { + foreach (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, " }\n"); + fprintf(out, " break;\n"); + } + fprintf(out, " }\n"); + fprintf(out, " }"); + isUsed_a = true; + } + + } + if (!cdef->signalList.isEmpty()) { + 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) { + const FunctionDef &f = cdef->signalList.at(methodindex); + if (f.wasCloned || !f.inPrivateClass.isEmpty() || f.isStatic || f.implementation) + continue; + anythingUsed = true; + fprintf(out, " {\n"); + fprintf(out, " typedef %s (%s::*_t)(",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) + fprintf(out, ", "); + fprintf(out, "%s", QByteArray(a.type.name + ' ' + a.rightType).constData()); + } + if (f.isPrivateSignal) { + if (argsCount > 0) + fprintf(out, ", "); + fprintf(out, "%s", "QPrivateSignal"); + } + if (f.isConst) + 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.name.constData()); + fprintf(out, " *result = %d;\n", methodindex); + fprintf(out, " }\n }\n"); + } + if (!anythingUsed) + fprintf(out, " Q_UNUSED(result);\n Q_UNUSED(func);\n"); + fprintf(out, " }"); + needElse = true; + } + + QMultiMap<QByteArray, int> automaticPropertyMetaTypes = automaticPropertyMetaTypesHelper(); + + if (!automaticPropertyMetaTypes.isEmpty()) { + if (needElse) + fprintf(out, " else "); + else + fprintf(out, " "); + fprintf(out, "if (_c == QMetaObject::RegisterPropertyMetaType) {\n"); + fprintf(out, " switch (_id) {\n"); + fprintf(out, " default: *reinterpret_cast<int*>(_a[0]) = -1; break;\n"); + foreach (const QByteArray &key, automaticPropertyMetaTypes.uniqueKeys()) { + foreach (int propertyID, automaticPropertyMetaTypes.values(key)) + fprintf(out, " case %d:\n", propertyID); + fprintf(out, " *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType< %s >(); break;\n", key.constData()); + } + fprintf(out, " }\n"); + fprintf(out, " }\n"); + isUsed_a = true; + needElse = true; + } + + if (!cdef->propertyList.empty()) { + bool needGet = false; + 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); + needGet |= !p.read.isEmpty() || !p.member.isEmpty(); + if (!p.read.isEmpty() || !p.member.isEmpty()) + needTempVarForGet |= (p.gspec != PropertyDef::PointerSpec + && p.gspec != PropertyDef::ReferenceSpec); + + needSet |= !p.write.isEmpty() || (!p.member.isEmpty() && !p.constant); + needReset |= !p.reset.isEmpty(); + } + fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n "); + + if (needElse) + fprintf(out, "else "); + fprintf(out, "if (_c == QMetaObject::ReadProperty) {\n"); + if (needGet) { + 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"); + if (needTempVarForGet) + fprintf(out, " void *_v = _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.read.isEmpty() && p.member.isEmpty()) + continue; + QByteArray prefix = "_t->"; + 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()); + else if (p.gspec == PropertyDef::ReferenceSpec) + fprintf(out, " case %d: _a[0] = const_cast<void*>(reinterpret_cast<const void*>(&%s%s())); break;\n", + propindex, prefix.constData(), p.read.constData()); + 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.isEmpty()) + fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s(); break;\n", + propindex, p.type.constData(), prefix.constData(), p.read.constData()); + else + fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s; break;\n", + propindex, p.type.constData(), prefix.constData(), p.member.constData()); + } + fprintf(out, " default: break;\n"); + fprintf(out, " }\n"); + } + + fprintf(out, " }"); + + fprintf(out, " else "); + 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"); + fprintf(out, " void *_v = _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.constant) + continue; + if (p.write.isEmpty() && p.member.isEmpty()) + continue; + QByteArray prefix = "_t->"; + if (p.inPrivateClass.size()) { + prefix += p.inPrivateClass + "->"; + } + 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.isEmpty()) { + fprintf(out, " case %d: %s%s(*reinterpret_cast< %s*>(_v)); break;\n", + propindex, prefix.constData(), p.write.constData(), p.type.constData()); + } else { + fprintf(out, " case %d:\n", propindex); + fprintf(out, " if (%s%s != *reinterpret_cast< %s*>(_v)) {\n", + 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) { + 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()); + } + fprintf(out, " }\n"); + fprintf(out, " break;\n"); + } + } + fprintf(out, " default: break;\n"); + fprintf(out, " }\n"); + } + + fprintf(out, " }"); + + 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"); + fprintf(out, " switch (_id) {\n"); + for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) { + const PropertyDef &p = cdef->propertyList.at(propindex); + if (!p.reset.endsWith(')')) + continue; + QByteArray prefix = "_t->"; + if (p.inPrivateClass.size()) { + prefix += p.inPrivateClass + "->"; + } + 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"); + needElse = true; + } + + if (needElse) + fprintf(out, "\n"); + + if (methodList.isEmpty()) { + fprintf(out, " Q_UNUSED(_o);\n"); + if (cdef->constructorList.isEmpty() && automaticPropertyMetaTypes.isEmpty() && methodsWithAutomaticTypesHelper(methodList).isEmpty()) { + fprintf(out, " Q_UNUSED(_id);\n"); + fprintf(out, " Q_UNUSED(_c);\n"); + } + } + if (!isUsed_a) + fprintf(out, " Q_UNUSED(_a);\n"); + + fprintf(out, "}\n\n"); +} + +void Generator::generateSignal(FunctionDef *def,int index) +{ + if (def->wasCloned || def->isAbstract || def->implementation) + return; + fprintf(out, "\n// SIGNAL %d\n%s %s::%s(", + index, def->type.name.constData(), cdef->qualified.constData(), def->name.constData()); + + QByteArray thisPtr = "this"; + const char *constQualifier = ""; + + if (def->isConst) { + thisPtr = "const_cast< "; + thisPtr += cdef->qualified; + thisPtr += " *>(this)"; + constQualifier = "const"; + } + + Q_ASSERT(!def->normalizedType.isEmpty()); + if (def->arguments.isEmpty() && def->normalizedType == "void") { + if (def->isPrivateSignal) + fprintf(out, "QPrivateSignal"); + + fprintf(out, ")%s\n{\n" + " QMetaObject::activate(%s, &staticMetaObject, %d, Q_NULLPTR);\n" + "}\n", constQualifier, thisPtr.constData(), index); + return; + } + + 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()); + } + if (def->isPrivateSignal) { + if (!def->arguments.isEmpty()) + fprintf(out, ", "); + fprintf(out, "QPrivateSignal"); + } + + 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, " void *_a[] = { "); + if (def->normalizedType == "void") { + fprintf(out, "Q_NULLPTR"); + } else { + if (def->returnTypeIsVolatile) + fprintf(out, "const_cast<void*>(reinterpret_cast<const volatile void*>(&_t0))"); + else + fprintf(out, "const_cast<void*>(reinterpret_cast<const void*>(&_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); + else + fprintf(out, ", const_cast<void*>(reinterpret_cast<const void*>(&_t%d))", i); + fprintf(out, " };\n"); + fprintf(out, " QMetaObject::activate(%s, &staticMetaObject, %d, _a);\n", thisPtr.constData(), index); + if (def->normalizedType != "void") + fprintf(out, " return _t0;\n"); + fprintf(out, "}\n"); +} + +#if 0 +static void writePluginMetaData(FILE *out, const QJsonObject &data) +{ + const QJsonDocument doc(data); + + 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); + } + fprintf(out, " 0x%02x\n};\n", (uchar)binary.at(last)); +#endif +} + +void Generator::generatePluginMetaData() +{ + if (cdef->pluginData.iid.isEmpty()) + return; + + // Write plugin meta data #ifdefed QT_NO_DEBUG with debug=false, + // true, respectively. + + 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()); + + // Add -M args from the command line: + foreach (const QString &key, cdef->pluginData.metaArgs.keys()) + data.insert(key, cdef->pluginData.metaArgs.value(key)); + + fputs("\nQT_PLUGIN_METADATA_SECTION const uint qt_section_alignment_dummy = 42;\n\n" + "#ifdef QT_NO_DEBUG\n", out); + writePluginMetaData(out, data); + + fputs("\n#else // QT_NO_DEBUG\n", out); + + data.remove(debugKey); + data.insert(debugKey, QJsonValue(true)); + writePluginMetaData(out, data); + + fputs("#endif // QT_NO_DEBUG\n\n", out); + + // 'Use' all namespaces. + int 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", + cdef->qualified.constData(), cdef->classname.constData()); +} +#endif + +QT_END_NAMESPACE diff --git a/tools/qscxmlc/generator.h b/tools/qscxmlc/generator.h new file mode 100644 index 0000000..6388fe6 --- /dev/null +++ b/tools/qscxmlc/generator.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef GENERATOR_H +#define GENERATOR_H + +#include "moc.h" +#include <QHash> +#include <QVector> +#include <QTextStream> + +QT_BEGIN_NAMESPACE + +class Generator +{ + QTextStream &out; + ClassDef *cdef; + QVector<uint> meta_data; +public: + Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes, const QHash<QByteArray, QByteArray> &knownQObjectClasses, const QHash<QByteArray, QByteArray> &knownGadgets, QTextStream &outfile); + void generateCode(); +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 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); + void registerEnumStrings(); + void generateEnums(int index); + void registerPropertyStrings(); + void generateProperties(); + void generateMetacall(); + void generateStaticMetacall(); + void generateSignal(FunctionDef *def, int index); +// void generatePluginMetaData(); + QMultiMap<QByteArray, int> automaticPropertyMetaTypesHelper(); + 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 + QList<QByteArray> strings; + QByteArray purestSuperClass; + QList<QByteArray> metaTypes; + QHash<QByteArray, QByteArray> knownQObjectClasses; + QHash<QByteArray, QByteArray> knownGadgets; +}; + +QT_END_NAMESPACE + +#endif // GENERATOR_H diff --git a/tools/qscxmlc/moc.h b/tools/qscxmlc/moc.h new file mode 100644 index 0000000..5149454 --- /dev/null +++ b/tools/qscxmlc/moc.h @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef MOC_H +#define MOC_H + +#include <qstringlist.h> +#include <qmap.h> +#include <qpair.h> +#include <qjsondocument.h> +#include <qjsonarray.h> +#include <qjsonobject.h> +#include <stdio.h> +#include <ctype.h> + +QT_BEGIN_NAMESPACE + +struct QMetaObject; + +struct Type +{ + enum ReferenceType { NoReference, Reference, RValueReference, Pointer }; + + inline Type() : isVolatile(false), isScoped(false), /*firstToken(NOTOKEN), */referenceType(NoReference) {} + inline explicit Type(const QByteArray &_name) + : name(_name), rawName(name), isVolatile(false), isScoped(false), /*firstToken(NOTOKEN),*/ 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; + ReferenceType referenceType; +}; + +struct EnumDef +{ + QByteArray name; + QList<QByteArray> values; + bool isEnumClass; // c++11 enum class + EnumDef() : isEnumClass(false) {} +}; + +struct ArgumentDef +{ + ArgumentDef() : isDefault(false) {} + Type type; + QByteArray rightType, normalizedType, name; + QByteArray typeNameForCast; // type name to be used in cast from void * in metacall + bool isDefault; +}; + +struct FunctionDef +{ + FunctionDef(): returnTypeIsVolatile(false), 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; + QByteArray normalizedType; + QByteArray tag; + QByteArray name; + bool returnTypeIsVolatile; + + QList<ArgumentDef> arguments; + + enum Access { Private, Protected, Public }; + Access access; + bool isConst; + bool isVirtual; + bool isStatic; + bool inlineCode; + bool wasCloned; + + 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; +}; + +struct PropertyDef +{ + PropertyDef():notifyId(-1), constant(false), final(false), gspec(ValueSpec), revision(0){} + QByteArray name, 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 { + QByteArray s("set"); + s += toupper(name[0]); + s += name.mid(1); + return (s == write); + } + int revision; +}; + + +struct ClassInfoDef +{ + QByteArray name; + QByteArray value; +}; + +struct ClassDef { + ClassDef(): + hasQObject(false), hasQGadget(false), notifyableProperties(0) + , revisionedMethods(0), revisionedProperties(0), begin(0), end(0){} + QByteArray classname; + QByteArray qualified; + QList<QPair<QByteArray, FunctionDef::Access> > superclassList; + + struct Interface + { + inline explicit Interface(const QByteArray &_className) + : className(_className) {} + QByteArray className; + QByteArray interfaceId; + }; + QList<QList<Interface> >interfaceList; + + bool hasQObject; + bool hasQGadget; + + struct PluginData { + QByteArray iid; + QMap<QString, QJsonArray> metaArgs; + QJsonDocument metaData; + } pluginData; + + QList<FunctionDef> constructorList; + QList<FunctionDef> signalList, slotList, methodList, publicList; + int notifyableProperties; + QList<PropertyDef> propertyList; + QList<ClassInfoDef> classInfoList; + QMap<QByteArray, bool> enumDeclarations; + QList<EnumDef> enumList; + QMap<QByteArray, QByteArray> flagAliases; + int revisionedMethods; + int revisionedProperties; + + int begin; + int end; +}; + +struct NamespaceDef { + QByteArray name; + int begin; + int end; +}; + +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; +} + +QT_END_NAMESPACE + +#endif // MOC_H diff --git a/tools/qscxmlc/outputrevision.h b/tools/qscxmlc/outputrevision.h new file mode 100644 index 0000000..da8cd09 --- /dev/null +++ b/tools/qscxmlc/outputrevision.h @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef OUTPUTREVISION_H +#define OUTPUTREVISION_H + +// if the output revision changes, you MUST change it in qobjectdefs.h too +enum { mocOutputRevision = 67 }; // moc format output revision + +#endif // OUTPUTREVISION_H diff --git a/tools/qscxmlc/qscxmlc.pro b/tools/qscxmlc/qscxmlc.pro index 196af79..a7c3c45 100644 --- a/tools/qscxmlc/qscxmlc.pro +++ b/tools/qscxmlc/qscxmlc.pro @@ -2,15 +2,17 @@ option(host_build) TARGET = qscxmlc CONFIG += console c++11 -QT = core +QT = core-private DEFINES += BUILD_QSCXMLC SOURCES += \ + generator.cpp \ qscxmlc.cpp \ scxmlcppdumper.cpp HEADERS += \ + moc.h generator.h outputrevision.h utils.h \ scxmlcppdumper.h HEADERS += \ diff --git a/tools/qscxmlc/scxmlcppdumper.cpp b/tools/qscxmlc/scxmlcppdumper.cpp index 3ccfb0e..26d497b 100644 --- a/tools/qscxmlc/scxmlcppdumper.cpp +++ b/tools/qscxmlc/scxmlcppdumper.cpp @@ -32,6 +32,9 @@ #include <algorithm> #include <functional> #include <QFileInfo> +#include <QBuffer> + +#include "generator.h" QT_BEGIN_NAMESPACE struct StringListDumper { @@ -98,9 +101,9 @@ struct ClassDump { Method init; Method initDataModel; StringListDumper dataMethods; + StringListDumper classMethods; Method constructor; Method destructor; - StringListDumper properties; StringListDumper signalMethods; QList<Method> publicMethods; QList<Method> protectedMethods; @@ -112,6 +115,8 @@ struct ClassDump { ClassDump() : needsEventFilter(false) {} + + QByteArray metaData; }; namespace { @@ -170,7 +175,6 @@ static QString toHex(const QString &str) static const char *headerStart = "#include <QScxmlStateMachine>\n" - "#include <QAbstractState>\n" "#include <QString>\n" "#include <QByteArray>\n" "\n"; @@ -216,6 +220,7 @@ public: addSubStateMachineProperties(doc); addEvents(); + generateMetaObject(); generateTables(); } @@ -318,11 +323,12 @@ protected: QString name = mangledName(node); QString stateName = QStringLiteral("state_") + name; // Property stuff: - clazz.properties << QStringLiteral("Q_PROPERTY(QAbstractState *%1 READ %1() CONSTANT)").arg(name); - Method getter(QStringLiteral("QAbstractState *%1() const").arg(name)); - getter.impl << QStringLiteral("QAbstractState *%2::%1() const").arg(name) - << QStringLiteral("{ return &data->%1; }").arg(stateName); - clazz.publicMethods << getter; + if (m_qtMode) { + Method getter(QStringLiteral("bool %1() const").arg(name)); + getter.impl << QStringLiteral("bool %2::%1() const").arg(name) + << QStringLiteral("{ return data->%1.active(); }").arg(stateName); + clazz.publicMethods << getter; + } // Declaration: if (node->type == State::Final) { @@ -345,6 +351,16 @@ protected: if (node->type == State::Parallel) { clazz.init.impl << stateName + QStringLiteral(".setChildMode(QState::ParallelStates);"); } + if (!node->id.isEmpty()) { + clazz.init.impl << QStringLiteral("QObject::connect(&") + + stateName + + QStringLiteral(", SIGNAL(activeChanged(bool)), &stateMachine, SIGNAL(") + + node->id + + QStringLiteral("Changed(bool)));"); + } + + m_stateNames.append(node->id); + m_stateFieldNames.append(stateName); // visit the kids: m_parents.append(node); @@ -417,7 +433,13 @@ protected: bool visit(Transition *node) Q_DECL_OVERRIDE { const QString tName = transitionName(node); - m_knownEvents.unite(node->events.toSet()); + if (m_qtMode) { + foreach (const QString &event, node->events) { + if (isValidCppIdentifier(event)) { + m_knownEvents.insert(event); + } + } + } // Declaration: clazz.classFields << QStringLiteral("QScxmlTransition ") + tName + QLatin1Char(';'); @@ -541,6 +563,7 @@ protected: if (m_qtMode && node->type == QStringLiteral("qt:signal")) { if (!m_signals.contains(node->event)) { m_signals.insert(node->event); + m_signalNames.append(node->event); clazz.signalMethods << QStringLiteral("void %1(const QVariant &data);").arg(node->event); } } @@ -637,33 +660,29 @@ private: void addSubStateMachineProperties(ScxmlDocument *doc) { - QStringList serviceProps; foreach (ScxmlDocument *subDocs, doc->allSubDocuments) { QString name = subDocs->root->name; if (name.isEmpty()) continue; + auto mangledName = CppDumper::mangleId(name); + auto qualifiedName = namespacePrefix + mangledName; + if (m_serviceProps.contains(qMakePair(mangledName, qualifiedName))) + continue; + m_serviceProps.append(qMakePair(mangledName, qualifiedName)); + clazz.classFields << QStringLiteral("%1 *%2;").arg(qualifiedName, mangledName); + clazz.constructor.initializer << QStringLiteral("%1(Q_NULLPTR)").arg(mangledName); - serviceProps.append(name); - clazz.classFields << QStringLiteral("%1%2 *%2;").arg(namespacePrefix, name); - clazz.constructor.initializer << QStringLiteral("%1(Q_NULLPTR)").arg(name); - clazz.properties << QStringLiteral("Q_PROPERTY(%1%2 *%2 READ %2() NOTIFY %2Changed())").arg(namespacePrefix, name); - Method getter(QStringLiteral("%1%2 *%2() const").arg(namespacePrefix, name)); - getter.impl << QStringLiteral("%1%2 *%3::%2() const").arg(namespacePrefix, name, clazz.className) - << QStringLiteral("{ return data->%1; }").arg(name); - clazz.publicMethods << getter; - clazz.signalMethods << QStringLiteral("void %1Changed();").arg(name); - } - - if (!serviceProps.isEmpty()) { - Method reg(QStringLiteral("void setService(const QString &id, QScxmlInvokableService *service) Q_DECL_OVERRIDE Q_DECL_FINAL")); - reg.impl << QStringLiteral("void %1::setService(const QString &id, QScxmlInvokableService *service) {").arg(clazz.className); - foreach (const QString &prop, serviceProps) { - reg.impl << QStringLiteral(" SET_SERVICE_PROP(%1, %2, %3%2, %2Changed)") - .arg(addString(prop)) - .arg(prop, namespacePrefix); + if (m_qtMode) { + Method getter(QStringLiteral("%1 *%2() const").arg(qualifiedName, mangledName)); + getter.impl << QStringLiteral("%1 *%2::%3() const").arg(qualifiedName, clazz.className, mangledName) + << QStringLiteral("{ return data->%1; }").arg(mangledName); + clazz.publicMethods << getter; + clazz.signalMethods << QStringLiteral("void %1Changed(%2 *statemachine);").arg(mangledName, qualifiedName); } - reg.impl << QStringLiteral("}"); - clazz.protectedMethods.append(reg); + + clazz.dataMethods << QStringLiteral("%1 *machine_%2() const").arg(qualifiedName, mangledName) + << QStringLiteral("{ return %1; }").arg(name) + << QString(); } } @@ -689,17 +708,17 @@ private: } } - if (!m_signals.isEmpty()) { + if (!m_signalNames.isEmpty()) { clazz.needsEventFilter = true; clazz.init.impl << QStringLiteral("stateMachine.setScxmlEventFilter(this);"); auto &dm = clazz.dataMethods; dm << QStringLiteral("bool handle(QScxmlEvent *event, QScxmlStateMachine *stateMachine) Q_DECL_OVERRIDE {"); if (m_qtMode) { dm << QStringLiteral(" if (event->originType() != QStringLiteral(\"qt:signal\")) { return true; }") - << QStringLiteral(" %1 *m = qobject_cast<%1 *>(stateMachine);").arg(clazz.className); - foreach (const QString &s, m_signals) { + << QStringLiteral(" %1 *m = static_cast<%1 *>(stateMachine);").arg(clazz.className); + foreach (const QString &signalName, m_signalNames) { dm << QStringLiteral(" if (event->name() == %1) { emit m->%2(event->data()); return false; }") - .arg(qba(s), CppDumper::mangleId(s)); + .arg(qba(signalName), CppDumper::mangleId(signalName)); } } dm << QStringLiteral(" return true;") @@ -995,7 +1014,7 @@ private: for (int i = 0, ei = strings.size(); i < ei; ++i) { QString s = strings.at(i); QString comment = cEscape(s); - t << QStringLiteral("QT_UNICODE_LITERAL_II(\"%1\") // %2").arg(toHex(s) + QStringLiteral("\\x00"), comment); + t << QStringLiteral("QT_UNICODE_LITERAL_II(\"%1\") // %3: %2").arg(toHex(s) + QStringLiteral("\\x00"), comment, QString::number(i)); } } t << QStringLiteral("};") << QStringLiteral(""); @@ -1021,6 +1040,159 @@ private: } } + void generateMetaObject() + { + ClassDef classDef; + classDef.classname = clazz.className.toUtf8(); + classDef.qualified = classDef.classname; + classDef.superclassList << qMakePair(QByteArray("QScxmlStateMachine"), FunctionDef::Public); + classDef.hasQObject = true; + + // Event signals: + foreach (const QString &signalName, m_signalNames) { + FunctionDef signal; + signal.type.name = "void"; + signal.type.rawName = signal.type.name; + signal.normalizedType = signal.type.name; + signal.name = signalName.toUtf8(); + signal.access = FunctionDef::Public; + signal.isSignal = true; + + ArgumentDef arg; + arg.type.name = "const QVariant &"; + arg.type.rawName = arg.type.name; + arg.normalizedType = "QVariant"; + arg.name = "data"; + arg.typeNameForCast = arg.normalizedType + "*"; + signal.arguments << arg; + + classDef.signalList << signal; + } + + // stateNames: + foreach (const QString &stateName, m_stateNames) { + if (stateName.isEmpty()) + continue; + + QByteArray mangledStateName = CppDumper::mangleId(stateName).toUtf8(); + + FunctionDef signal; + signal.type.name = "void"; + signal.type.rawName = signal.type.name; + signal.normalizedType = signal.type.name; + signal.name = mangledStateName + "Changed"; + signal.access = FunctionDef::Private; + signal.isSignal = true; + if (!m_qtMode) { + signal.implementation = "QMetaObject::activate(_o, &staticMetaObject, %d, _a);"; + } else { + clazz.signalMethods << QStringLiteral("void %1Changed(bool active);").arg(stateName); + } + ArgumentDef arg; + arg.type.name = "bool"; + arg.type.rawName = arg.type.name; + arg.normalizedType = arg.type.name; + arg.name = "active"; + arg.typeNameForCast = arg.type.name + "*"; + signal.arguments << arg; + classDef.signalList << signal; + + ++classDef.notifyableProperties; + PropertyDef prop; + prop.name = stateName.toUtf8(); + prop.type = "bool"; + prop.read = "data->state_" + mangledStateName + ".active"; + prop.notify = mangledStateName + "Changed"; + prop.notifyId = classDef.signalList.size() - 1; + prop.gspec = PropertyDef::ValueSpec; + prop.scriptable = "true"; + classDef.propertyList << prop; + } + + // event slots: + foreach (const QString &eventName, m_knownEvents) { + FunctionDef slot; + slot.type.name = "void"; + slot.type.rawName = slot.type.name; + slot.normalizedType = slot.type.name; + slot.name = eventName.toUtf8(); + slot.access = FunctionDef::Public; + slot.isSlot = true; + + classDef.slotList << slot; + + ArgumentDef arg; + arg.type.name = "const QVariant &"; + arg.type.rawName = arg.type.name; + arg.normalizedType = "QVariant"; + arg.name = "data"; + arg.typeNameForCast = arg.normalizedType + "*"; + slot.arguments << arg; + + classDef.slotList << slot; + } + + // sub-statemachines: + QHash<QByteArray, QByteArray> knownQObjectClasses; + knownQObjectClasses.insert(QByteArray("QScxmlStateMachine"), QByteArray()); + Method reg(QStringLiteral("void setService(const QString &id, QScxmlInvokableService *service) Q_DECL_OVERRIDE Q_DECL_FINAL")); + reg.impl << QStringLiteral("void %1::setService(const QString &id, QScxmlInvokableService *service) {").arg(clazz.className); + if (m_serviceProps.isEmpty()) { + reg.impl << QStringLiteral(" Q_UNUSED(id);") + << QStringLiteral(" Q_UNUSED(service);"); + } + for (const auto &service : m_serviceProps) { + auto serviceName = service.first; + QString fqServiceClass = service.second; + QByteArray serviceClass = fqServiceClass.toUtf8(); + knownQObjectClasses.insert(serviceClass, ""); + + reg.impl << QStringLiteral(" SET_SERVICE_PROP(%1, %2, %3%2, %4)") + .arg(addString(serviceName)) + .arg(serviceName, namespacePrefix).arg(classDef.signalList.size()); + + QByteArray mangledServiceName = CppDumper::mangleId(serviceName).toUtf8(); + + FunctionDef signal; + signal.type.name = "void"; + signal.type.rawName = signal.type.name; + signal.normalizedType = signal.type.name; + signal.name = mangledServiceName + "Changed"; + signal.access = FunctionDef::Private; + signal.isSignal = true; + if (!m_qtMode) { + signal.implementation = "QMetaObject::activate(_o, &staticMetaObject, %d, _a);"; + } + ArgumentDef arg; + arg.type.name = serviceClass + " *"; + arg.type.rawName = arg.type.name; + arg.type.referenceType = Type::Pointer; + arg.normalizedType = serviceClass + "*(*)"; + arg.name = "statemachine"; + arg.typeNameForCast = arg.type.name + "*"; + signal.arguments << arg; + classDef.signalList << signal; + + ++classDef.notifyableProperties; + PropertyDef prop; + prop.name = serviceName.toUtf8(); + prop.type = serviceClass + "*"; + prop.read = "data->machine_" + mangledServiceName; + prop.notify = mangledServiceName + "Changed"; + prop.notifyId = classDef.signalList.size() - 1; + prop.gspec = PropertyDef::ValueSpec; + prop.scriptable = "true"; + classDef.propertyList << prop; + } + reg.impl << QStringLiteral("}"); + clazz.protectedMethods.append(reg); + + QBuffer buf(&clazz.metaData); + buf.open(QIODevice::WriteOnly); + QTextStream out(&buf); + Generator(&classDef, QList<QByteArray>(), knownQObjectClasses, QHash<QByteArray, QByteArray>(), out).generateCode(); + } + QString qba(const QString &bytes) { return QStringLiteral("string(%1)").arg(addString(bytes)); @@ -1039,8 +1211,12 @@ private: TranslationUnit *translationUnit; QHash<AbstractState *, QString> m_mangledNames; QVector<Node *> m_parents; + QList<QPair<QString, QString>> m_serviceProps; QSet<QString> m_knownEvents; QSet<QString> m_signals; + QStringList m_signalNames; + QStringList m_stateNames; + QStringList m_stateFieldNames; QString m_currentTransitionName; bool m_bindLate; bool m_qtMode; @@ -1085,7 +1261,7 @@ void CppDumper::dump(TranslationUnit *unit) writeImplEnd(); } -QString CppDumper::mangleId(const QString &id) +QString CppDumper::mangleId(const QString &id) // TODO: remove { QString mangled(id); mangled = mangled.replace(QLatin1Char('_'), QLatin1String("__")); @@ -1116,9 +1292,13 @@ void CppDumper::writeHeaderStart(const QString &headerGuard, const QStringList & void CppDumper::writeClass(const ClassDump &clazz) { h << l("class ") << clazz.className << QStringLiteral(": public QScxmlStateMachine\n{") << endl; - h << QLatin1String(" Q_OBJECT\n"); - clazz.properties.write(h, QStringLiteral(" "), QStringLiteral("\n")); - h << QLatin1String("\npublic:\n"); + h << QStringLiteral("public:") << endl + << QStringLiteral(" /* qmake ignore Q_OBJECT */") << endl + << QStringLiteral(" Q_OBJECT") << endl + ; + + h << endl + << QStringLiteral("public:") << endl; h << l(" ") << clazz.className << l("(QObject *parent = 0);") << endl; h << l(" ~") << clazz.className << "();" << endl; @@ -1203,9 +1383,12 @@ void CppDumper::writeImplBody(const ClassDump &clazz) } cpp << l(" {") << endl; - cpp << QStringLiteral(" Data(%1 &stateMachine)\n : stateMachine(stateMachine)").arg(clazz.className) << endl; + cpp << QStringLiteral(" Data(%1 &stateMachine)").arg(clazz.className) << endl + << QStringLiteral(" : stateMachine(stateMachine)") << endl; clazz.constructor.initializer.write(cpp, QStringLiteral(" , "), QStringLiteral("\n")); - cpp << l(" { init(); }") << endl; + cpp << l(" {") << endl; + clazz.constructor.impl.write(cpp, QStringLiteral(" "), QStringLiteral("\n")); + cpp << l(" }") << endl; cpp << endl; cpp << l(" void init() {\n"); @@ -1220,10 +1403,12 @@ void CppDumper::writeImplBody(const ClassDump &clazz) cpp << l("};") << endl << endl; + clazz.classMethods.write(cpp, QStringLiteral(""), QStringLiteral("\n")); + cpp << clazz.className << l("::") << clazz.className << l("(QObject *parent)") << endl << QStringLiteral(" : QScxmlStateMachine(parent)") << endl << QStringLiteral(" , data(new Data(*this))") << endl - << QStringLiteral("{ qRegisterMetaType<%1 *>(); qRegisterMetaType<QAbstractState *>(); }").arg(clazz.className) << endl + << QStringLiteral("{ qRegisterMetaType<%1 *>(); data->init(); }").arg(clazz.className) << endl << endl; cpp << clazz.className << l("::~") << clazz.className << l("()") << endl << l("{ delete data; }") << endl @@ -1239,7 +1424,8 @@ void CppDumper::writeImplBody(const ClassDump &clazz) " fq *casted = machine ? dynamic_cast<fq*>(machine->stateMachine()) : Q_NULLPTR; \\\n" " if (data->n != casted) { \\\n" " data->n = casted; \\\n" - " emit sig(); \\\n" + " void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&casted)) }; \\\n" + " QMetaObject::activate(this, &staticMetaObject, sig, _a); \\\n" " } \\\n" " return; \\\n" " }\n" @@ -1267,6 +1453,8 @@ void CppDumper::writeImplBody(const ClassDump &clazz) m.impl.write(cpp, QStringLiteral(""), QStringLiteral("\n")); } } + + cpp << endl << clazz.metaData; } void CppDumper::writeImplEnd() diff --git a/tools/qscxmlc/utils.h b/tools/qscxmlc/utils.h new file mode 100644 index 0000000..58cf924 --- /dev/null +++ b/tools/qscxmlc/utils.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef UTILS_H +#define UTILS_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +inline bool is_whitespace(char s) +{ + return (s == ' ' || s == '\t' || s == '\n'); +} + +inline bool is_space(char s) +{ + return (s == ' ' || s == '\t'); +} + +inline bool is_ident_start(char s) +{ + return ((s >= 'a' && s <= 'z') + || (s >= 'A' && s <= 'Z') + || s == '_' || s == '$' + ); +} + +inline bool is_ident_char(char s) +{ + return ((s >= 'a' && s <= 'z') + || (s >= 'A' && s <= 'Z') + || (s >= '0' && s <= '9') + || s == '_' || s == '$' + ); +} + +inline bool is_identifier(const char *s, int len) +{ + if (len < 1) + return false; + if (!is_ident_start(*s)) + return false; + for (int i = 1; i < len; ++i) + if (!is_ident_char(s[i])) + return false; + return true; +} + +inline bool is_digit_char(char s) +{ + return (s >= '0' && s <= '9'); +} + +inline bool is_octal_char(char s) +{ + return (s >= '0' && s <= '7'); +} + +inline bool is_hex_char(char s) +{ + return ((s >= 'a' && s <= 'f') + || (s >= 'A' && s <= 'F') + || (s >= '0' && s <= '9') + ); +} + +inline const char *skipQuote(const char *data) +{ + while (*data && (*data != '\"')) { + if (*data == '\\') { + ++data; + if (!*data) break; + } + ++data; + } + + if (*data) //Skip last quote + ++data; + return data; +} + +QT_END_NAMESPACE + +#endif // UTILS_H |