summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@theqtcompany.com>2016-01-29 12:02:04 +0100
committerErik Verbruggen <erik.verbruggen@theqtcompany.com>2016-02-05 13:46:13 +0000
commitd9fb4e37573e55d85b1cfbb24c94d48d93350a54 (patch)
treed61be7734ca364421b9bfd8b0a9b117819b14eb7
parent9ada7aa0f5a41312c953ee3c34053edc4cf15df9 (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.cpp11
-rw-r--r--examples/calculator-common/mainwindow.h3
-rw-r--r--examples/calculator-widgets-static/calculator-widgets-static.cpp3
-rw-r--r--examples/pinball/doc/src/pinball.qdoc6
-rw-r--r--examples/pinball/mainwindow.cpp14
-rw-r--r--examples/pinball/mainwindow.h4
-rw-r--r--examples/pinball/pinball.scxml11
-rw-r--r--mkspecs/features/qscxmlc.prf12
-rw-r--r--src/scxml/qscxmlparser.cpp236
-rw-r--r--src/scxml/qscxmlparser_p.h2
-rw-r--r--tests/auto/scion/scion.pro11
-rw-r--r--tools/qscxmlc/generator.cpp1616
-rw-r--r--tools/qscxmlc/generator.h78
-rw-r--r--tools/qscxmlc/moc.h201
-rw-r--r--tools/qscxmlc/outputrevision.h35
-rw-r--r--tools/qscxmlc/qscxmlc.pro4
-rw-r--r--tools/qscxmlc/scxmlcppdumper.cpp272
-rw-r--r--tools/qscxmlc/utils.h121
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 &paramsIndex)
+{
+ 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 &paramsIndex);
+ 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